webpack(ウェブパック)は、複数のJSファイルをまとめてくれるツールです。
webpackは「gulp」「Grunt 」「Browserify」のようなタスクランナーですが、最大の特徴は「モジュールバンドラ」です。
モジュールバンドラーはファイルをまとめることを指し、複数のモジュール(ここではJS)の依存関係を解決します。
今回は下記を参考にて webpack4 を npm で構築してみました。
最新版で学ぶwebpack 4入門 JavaScriptのモジュールバンドラ
webpackのメリットはいくつかあります。
など、今まで別々で管理していたJSファイルを1つにまとめることで、フロントエンド開発手法そのものが変わってきます。
gulp で開発してきた場合は、gulp + webpack で運用してるようですが、新規ならば webpack で十分みたいです(開発者の好みか?)。
node.js のバージョンは13以上
$ node -v
v13.6.0
webpack のテストサンプルを利用するロジェクトを作成する。
また、npm init で package.json も作成しておく。
$ mkdir webpack-test
$ cd webpack-test
$ npm init -y
$ mkdir src
devDependencies(-D) で webpack webpack-cli をインストールする
$npm i -D webpack webpack-cli
...
+ webpack-cli@3.3.11
+ webpack@4.41.6
...
現時点で、「モジュール方式」で JavaScript を実装することが推奨されている。
※ECMAScript Modules(ES Modules、ESM)
webpack4 では明示的に指定しない場合、以下の設定となります。
import { say } from "./module1";
say();
export function say() {
alert("module1 hello!");
}
手っ取り早く npm 5.2.0 で追加された「npx」コマンドで webpack ビルドしてみる。
(npm run build で webpack を動作させた方が応用が効くが)
$ npx webpack
Hash: 58e8f21bff04a84f6d5c
Version: webpack 4.41.6
Time: 247ms
Built at: 2020/02/27 20:49:44
Asset Size Chunks Chunk Names
main.js 957 bytes 0 [emitted] main
Entrypoint main = main.js
[0] ./src/index.js + 1 modules 96 bytes {0} [built]
| ./src/index.js 42 bytes [built]
| ./src/person.js 54 bytes [built]
src/index.js に src/person.js が統合され dist/main.js が書き出される
index.html を作成し「dist/main.js」を読み込む
npx webpack でなく package.json を設定して npm ビルドする
{
"name": "webpack-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11"
},
"devDependencies": {
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11"
},
"scripts": {
"build": "webpack"
}
}
npm run build で webpack ビルドされる
$ npm run build
> webpack-test@1.0.0 build /Users/yoo/docker/projects/webpack-test
> webpack
...
webpack.config.js を作成して、「entry」「output」を設定することで、エントリポイント、出力先ファイルをカスタマイズできます。
※前述の通り webpack4 では「src/index.js」「dist/main.js」がデフォルト
module.exports = {
entry: `./src/index.js`,
output: {
path: `${__dirname}/dist`,
filename: "main.js"
},
mode: "development"
};
$ npm run build
modeを「development」にすると、出力ファイル(main.js)が圧縮されずに書き出されます。
webpack-dev-server をして、JavaScriptの実装をリアルタイムにブラウザ確認することもできるが、一般的なウォッチを利用してビルドする。
"scripts": {
"build": "webpack",
"watch": "webpack --watch"
},
$ npm run watch
これで、JavaScript を保存すると自動でビルドされます。
webpack の環境を構築してみる
参考:webpack 4 入門 webpackの最大の特徴は「モジュールバンドラ」を利用して、複数のjsファイルを1つのファイル「bundle.js」にまとめる。
├── node_modules
├── package.json
├── public
│ ├── index.html
│ └── js
│ └── bundle.js
├── src
│ └── js
│ ├── app.js
│ └── modules
│ ├── add-calculator.js
│ └── tax-calculator.js
└── webpack.config.js
上記の場合「app.js」「add-calculator.js」「tax-calculator.js」をビルドして「bundle.js」にまとめます。
※NodeJS, npm, yarn などは解説省略
$ yarn init --yes
yarn init v1.21.1
warning The yes flag has been set. This will automatically answer yes to all questions, which may have security implications.
success Saved package.json
✨ Done in 0.04s.
$ yarn add webpack webpack-cli webpack-dev-server --dev
var path = require('path');
module.exports = {
entry: "./src/js/app.js",
output: {
path: path.join(__dirname, 'public/js'),
filename: 'bundle.js',
},
resolve: {
extensions: ['.js', '.ts', '.svg']
},
module: {
rules: [{
test: /\.js$/,
use: [{
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}],
}, {
test: /\.ts$/,
use: [{
loader: 'ts-loader',
options: {
compilerOptions: {
declaration: false,
target: 'es5',
module: 'commonjs'
},
transpileOnly: true
}
}]
}, {
test: /\.svg$/,
use: [{
loader: 'html-loader',
options: {
minimize: true
}
}]
}]
}
}
entry:コンパイルするメインJSファイルパス(エントリーポイント)
output:出力するメインJSファイルのパス 最初に require した path を利用して最終的に「public」ディレクトリに「bundle.js」を書き出す
require , importするファイル拡張子を省略設定
ビルドする際、利用するモジュールを設定 babel-loader: Bableに対応する
次世代 EcmaScript で書かれたJSを、サポートしていないブラウザでも動くJSに変換
ts-loader:TypeSceiptに対応する
$ yarn install
yarn install v1.21.1
[1/4] 🔍 Resolving packages...
success Already up-to-date.
✨ Done in 0.12s.
$ mkdir src
export default function addCalculator(number1 ,number2) {
return number1 + number2;
}
import addCalculator from './modules/add-calculator';
import taxCalculator from './modules/tax-calculator';
var item1_price = 100;
var item2_price = 300;
var total_price = addCalculator(item1_price, item2_price);
var tax = 0.1;
var tax_price = taxCalculator(total_price, tax);
window.onload = function() {
document.getElementById('calculate-result').innerHTML = tax_price;
};
export default function taxCalculator(price ,tax) {
return Math.round(price * (1 + tax));
}
$ yarn run webpack
node_modules のパスをbash に登録することで、webpack コマンドを直接利用できる
$ vi ~/.bash_profile
export PATH=$PATH:./node_modules/.bin
$ source ~/.bash_profile
単体テストランナー「Karma」をインストール
テストフレームワーク「Mocha」をインストール
JS、CSSを1ファイルにまとめる webpack を利用
設定ファイル「karma.conf.js」が作成され、指定ブラウザにKarmaが起動
「jQuery LoadingOverlay」をネイティブで実装してみる。
Github: yoo16/html_samples
This is loading1 element areas.
This is loading1 element areas.
This is loading1 element areas.
This is loading1 element areas.
This is loading1 element areas.
This is loading1 element areas.
This is loading2 element areas.
This is loading2 element areas.
This is loading2 element areas.
This is loading2 element areas.
This is loading2 element areas.
This is loading2 element areas.
var LoadingOverlay = function() {
var _this = this
this.loading_overlay_name = 'loading_overlay';
this.show = function(selector, options) {
_this.hide(selector);
var style = "";
style+= "position: absolute;";
style+= "z-index: 1050;";
style+= "background-color: rgba(255, 255, 255, 0.2);";
style+= "background-position: center center;";
style+= "background-repeat: no-repeat;";
var top = "0;";
var left = "0;";
var width = "100%;";
var height = "100%;";
var background_size = 'contains';
var element = document.body;
if (selector) {
element = document.getElementById(selector);
var rect = element.getBoundingClientRect();
top = rect.top;
left = rect.left;
width = element.clientWidth;
height = element.clientHeight;
background_size = _this.backgroundSize(element);
let td = element.closest('td');
if (td) {
top = 0;
left = 0;
}
}
style+= "background-size: " + background_size + ";";
style+= "top: " + top + "px;";
style+= "left: " + left + "px;";
style+= "width: " + width + "px;";
style+= "height: " + height + "px;";
style+="background-image: url(" + loading_image + ");";
_this.loading_overlay_element = document.createElement('div');
_this.loading_overlay_element.classList.add(_this.loading_overlay_name);
_this.loading_overlay_element.style = style;
element.appendChild(_this.loading_overlay_element);
//timer
function hideHandler() {
clearInterval(_this.timer);
_this.timer = null;
_this.hide(selector);
}
if (options && options.timeout > 0) {
if (!_this.timer) _this.timer = setInterval(hideHandler, options.timeout);
}
}
this.hide = function(selector) {
var parent_element;
if (selector) {
parent_element = document.getElementById(selector);
} else {
parent_element = document.body;
}
[].forEach.call(parent_element.children, function(element) {
if (element.classList.contains(_this.loading_overlay_name)) {
parent_element.removeChild(element);
}
});
}
this.backgroundSize = function(element) {
background_size = "contains";
var img = new Image();
img.src = loading_image;
var width_rate = 0;
var height_rate = 0;
if (img.width > element.clientWidth) {
width_rate = element.clientWidth / img.width;
}
if (img.height > element.clientHeight) {
height_rate = element.clientHeight / img.height;
}
var rate = 0;
if (width_rate > height_rate) {
rate = width_rate;
} else {
rate = height_rate;
}
if (rate > 0) background_size = rate * 30 + "%;"
return background_size;
}
}
var loading_image = "data:image/gif;base64,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";
var loading_overlay = new LoadingOverlay();
document.createElement('div') で LoadingOver Element を作成し、style を動的に設定後、ターゲットに追加(appendChild()) している。 (ターゲットがない場合は document.body)
styleはターゲットの位置やサイズを取得して absolute で位置合わせしている。 ローディング画像(loading_image)は、画像をBase64に変換しているが、URLでも構わない。 backgroundSize() ではローディング画像のサイズを調整している。 ローディング画像が親より小さければ「background_size: contains」、大きければ「%」で調整する。
指定したIDの子要素の中の、LoadingOver Element のクラスを検索して削除(removeChild()) これで要素毎のローディングにも対応できる。
クラス名や「rel」アトリビュートを指定して、新規ウィンドウで開く
rel New Window
class New Window
HTML属性にウィンドウ名「window_name」、オプション「window_option」を指定した
rel属性を指定した querySelectorAll() で NodeList を取得して click イベントを登録 (IEだと単にループで回せないので、一工夫必要)
function loadPopupRelEvent(rel_name) {
var popupEvent = function(event) {
var window_name = '_blank';
var window_option = null;
if (this.getAttribute('window_name')) window_name = this.getAttribute('window_name');
if (this.getAttribute('window_option')) window_option = this.getAttribute('window_option');
window.open(this.href, window_name, window_option).focus()
event.preventDefault();
event.stopPropagation();
}
var query_string = '[rel = "' + rel_name + '"]';
let elements = document.querySelectorAll(query_string);
if (!elements) return;
[].forEach.call(elements, function (element) {
element.addEventListener('click', popupEvent, false);
});
}
document.addEventListener('DOMContentLoaded', function () {
loadPopupRelEvent('popup', params);
});
getElementsByClassName() で HTMLCollection を取得して click イベントを登録 (IEだと単にループで回せないので、一工夫必要)
function loadPopupClassEvent(class_name) {
var popupEvent = function(event) {
var window_name = '_blank';
var window_option = null;
if (this.getAttribute('window_name')) window_name = this.getAttribute('window_name');
if (this.getAttribute('window_option')) window_option = this.getAttribute('window_option');
window.open(this.href, window_name, window_option).focus()
event.preventDefault();
event.stopPropagation();
}
let elements = document.getElementsByClassName(class_name);
if (!elements) return;
[].forEach.call(elements, function (element) {
element.addEventListener('click', popupEvent, false);
});
}
document.addEventListener('DOMContentLoaded', function () {
loadPopupClassEvent('popup_class', params);
});
要素にclickイベントを登録し、正規表現でURLパースする
テーブルソートは、jQuery(tablednd.js)で比較的簡単に実装できる。 tablednd はマウスイベントを駆使して実装している感じだが、ここではHTML5のDND(Drag & Dropイベント)を利用して実装してみる。
まずは仕様を確認 ・HTML Living Standard:Drag and drop ・ネイティブ HTML5 ドラッグ&ドロップ
tableの並び替え&アップデートスクリプトのライブラリ(PwJS)を作成してみた。
PwJSライブラリ *PwTableDND.js, PwNode.jsを利用 html_samples/sotrable_table.html
色々と制御する必要があるので、ここでは主要部分だけ記述 *並び替えの保持やAPI更新は記述なし
「dragstart」「dragover」「drop」などのDNDイベントを登録して、Element取得・操作を行う
//各イベントハンドラ
function handleDragStart(event) {
//ここにドラッグアイテムの取得処理
event.target.style.opacity = '0.4'; //透明度を有効
event.stopPropagation();
}
function handleDrag(event) {
}
function handleDragEnter(event) {
}
function handleDragOver(event) {
event.dataTransfer.dropEffect = 'move'; //仕様でサポートしている dropEffect
event.preventDefault(); //伝播を止める
return false;
}
function handleDragLeave(event) {
}
function handleDrop(event) {
//ここにターゲットアイテムの取得処理
event.preventDefault(); //伝播を止める
event.stopPropagation(); //リンクキャンセル
}
function handleDragEnd(event) {
event.target.style.opacity = '' //透明度を無効
}
//各イベントの登録
document.addEventListener('dragstart', handleDragStart, false);
document.addEventListener('drag', handleDrag, false);
document.addEventListener('dragenter', handleDragEnter, false)
document.addEventListener('dragover', handleDragOver, false);
document.addEventListener('dragleave', handleDragLeave, false);
document.addEventListener('drop', handleDrop, false);
document.addEventListener('dragend', handleDragEnd, false);
伝播しないように preventDefault(), ドラッグでリンク誤動作しないように stopPropagation() を実行しているが、実際に実行してみるとよくわかる。
並び順のデータID「row-id」属性を持った「tr」で構成 「tbody」内の「tr」を入れ替えるようなスクリプトを作成する
City
Tokyo
Osaka
Nagoya
Kobe
querySelectorAll() などでElement や HTMLCollection を取得して、各行の属性を動的に設定 HTML5では「draggable = true」でドラッグ可能になる
....
var _this = this;
this.table_id = 'sortable-table';
this.row_id_column = 'row-id';
this.body_selector = '';
this.tr_selector = '';
this.sortable_tr_selector = '';
....
this.enableDrag = function() {
_this.loadSelectorColumn();
if (!this.table_id) return;
var row_id;
[].forEach.call(this.getElements(), function(element, index) {
_this.before_rows.push(element);
row_node = PwNode.byElement(element);
if (row_id = row_node.attr(_this.row_id_column)) {
row_node.setAttr('id', pw_row_id_column + row_id);
row_node.setAttr('order', index + 1);
row_node.setAttr('draggable', true);
if (!pw_app.isIE()) {
row_node.setAttr('ondragstart', "event.dataTransfer.setData('text/plain', null)");
}
}
});
}
this.loadSelectorColumn = function() {
_this.body_selector = '#' + _this.table_id + ' tbody';
_this.tr_selector = '#' + _this.table_id + ' tr';
_this.sortable_tr_selector = _this.body_selector + ' tr';
}
this.getElements = function() {
let elements = document.querySelectorAll(_this.sortable_tr_selector);
return elements;
}
FirefoxではHTMLタグに「ondragstart="event.dataTransfer.setData('text/plain', null)"」を指定しておかないと、何故か動作しなかった。 *pw_app.isIE() はライブラリのIE判別関数
dropイベントで、Drag Element, Drop Elementの取得・入れ替えを行う
function handleDragStart(event) {
let tr = event.target.closest('tr');
if (tr) _this.drag_item = event.target.closest('tr');
event.target.style.opacity = '0.4';
event.stopPropagation();
}
function handleDrop(event) {
var tr = event.target.closest('tr');
if (tr && _this.drag_item != tr) {
_this.target_item = tr;
}
event.target.style.opacity = '1.0'
let row_id = _this.drag_item.getAttribute(_this.row_id_column);
if (row_id && _this.target_item && _this.drag_item != _this.target_item) {
var tbody = PwNode.byQuery(_this.body_selector).first();
let drag_order = _this.drag_item.getAttribute('order');
let target_order = _this.target_item.getAttribute('order');
if (drag_order > target_order) {
tbody.insertBefore(_this.drag_item, _this.target_item);
} else if (drag_order < target_order) {
tbody.insertBefore(_this.drag_item, _this.target_item.nextElementSibling);
}
//テーブル並び替え後の処理を記述
//並び順管理、tr.order を振り直す etc..
}
event.preventDefault();
event.stopPropagation();
}
event.target.closest() で「tr」を取得 「tbody」内で insertBefore() を利用し、並び順を考慮しつつ Element を書き換える 色々な方法があると思うが、上記は「tr」の order が並び替え後に順番を振り直す事を前提にしている
jQuery の $(document).on() をネイティブでコーディング document.addEventListner() で記述するが、ネイティブの場合はターゲット(Event.target)が何かによって条件分岐が必要
yoo16/html_samples/blob/master/document_on.html
//jQuery
$(document).on('click', '#id_name', function() {
//working
});
//native
document.addEventListener('click', function(event) {
if (event.target.id === 'id_name') {
//working
}
});
//jQuery
$(document).on('click', '.class_name', function() {
//working
});
//native
document.addEventListener('click', function(event) {
if(event.target.classList.contains('class_name'))
//working
}
});
querySelectorAll() で取得する方法もある。 (この方がjQueryに近いか?) ただ、addEventListener() を事前登録するため、後にHTMLを発生さした要素では動作しないのでイベント管理が必要
document.querySelectorAll('.class_name').forEach(function(element) {
element.addEventListener('click', function(event) {
//working
});
});
上記はIEの場合「NodeList」をループで回せない可能性があるので、
let elements = document.querySelectorAll('.class_name');
[].forEach.call(elements, function(element) {
element.addEventListener('click', function(event) {
//working
});
});
としても良い。
近年「脱jQuery」のワードが出ているが、手法を間違えると「jQuery」で良かったということにも・・・ 「jQueryのここが便利だったのか」と痛感することも結構あります。
周りが発言しているからという曖昧な理由でなく、「脱jQuery」した未来予想図を作っておきます。 (JavaScript、フレームワーク知識向上、半自動化などの開発効率化の目処など) 特にJSフレームワークを利用しない場合、jQueryの部分を全て自分で書き直す覚悟が必要でしょう。
「Vue.js」「AngularJS」「React」の選択肢が決まっていて、バックエンドやテンプレート管理の設計ができている。 恐らく、これが今一番主流でしょう。 ただしフレームワークや設計方法の理解度が低いと、プロジェクトが破綻する可能性がある。
「脱jQuery」をしたことで、ソース管理が余計に複雑になってしまう事が多々ある。 特にjQuryライブラリに依存したプロジェクトを書き換えると相当な労力を要するので覚悟が必要である。
など、案件によってよく吟味して作業した方が良い。
現在は「Vue.js」「AngularJS」「React」が主流 「YUI」「Backbone.js」などフェイドアウトしていくフレームワークも多々あるので、選択を間違えないようにネイティブの知識の保険をかける必要がある。
個人的なロードマップとして、既存JSフレームワーク利用の前に、自作フレームワークを作成してある程度経験を積み、同時に「Vue.js(個人的にイチオシ)」「AngularJS」の情報も蓄積しつつ、効率的・自動化を踏まえて移行するつもりだ。
過去の経験から既存JSフレームワークを安易に利用すると、
・知識が浅いと構造がグチャグチャになる ・効率化するにはJSフレームワーク以外の設計知識も必要 ・進化が早いので、圧倒的な仕様変更に対応できない
になるためだ。
現在(2019/03時点)は「ECMAscript 6」が主流であるので、「ECMAscript 7」は必要な時に利用していく。 更に「TypeScript」を併用するのが理想かもしれないが、まずはネイティブの知識を復習・蓄積していく。
・JavaScriptネイティブ追求 ・Dom操作追求 ・MVC設計において他のフレームワークとの一般共通化 ・View(テンプレートエンジン)の手法・管理 ・バックエンドの親和性 ・開発半自動化
などを目標とします。
JavaScriptでAPIからデータ取得・処理する際、ネスト地獄で複雑なコードになりがち。 かと言って、AngularやReactなどのフレームワークを使うと勉強コスト、スパゲティー設計など本末転倒な結果になりやすい。
フレームワークの最大のデメリットは、
中で何が行われているか?ソースを解読しないとわからない
と言うことで、こみいった実装をせずシンプルに実装してみる。
(1) APIリクエストの配列設定 (2) Ajax処理の抽象化 (3) 並列処理の抽象化 (4) コールバック処理の考慮
$(document).on('click', '.action-api', function() {
//API設定(抽象化する必要あり)
var requests = [
{
url: 'https://xxxx/api/user',
params: {'user_id': 394},
callback: callbackUser
},
{
url: 'https://xxxx/api/order',
params: {'user_id': 394, 'item_id': 31},
callback: callbackApplication
},
];
//並列処理実行
parallelAjax(requests, doneCallback);
//各APIリクエストのコールバック
function callbackUser(results) {
console.log(results);
}
function callbackApplication(results) {
console.log(results);
}
//並列処理完了後のコールバック
function doneCallback(results) {
console.log(results);
}
});
var requestAjax = function(values){
var $ajax = $.ajax(values);
var defer = new $.Deferred();
$ajax.done(function(data, status, $ajax){
defer.resolveWith(this, arguments);
});
$ajax.fail(function(data, status, $ajax){
defer.resolveWith(this, arguments);
});
return $.extend({}, $ajax, defer.promise());
};
function parallelAjax(requests, callback) {
var results = [];
$.each (requests, function(index, value) {
var $ajax = requestAjax({url: value.url, data: value.params}).done(function(res, status) {
if (value.callback) {
value.callback(res);
}
});
results.push($ajax);
});
$.when.apply(null, results).done(function(){
if (callback) callback(results);
});
$.when.apply(null, results).fail(function(){
});
}
並列処理は後々ループで実行ため、オブジェクト配列で設定しておく。 差し当たり以下の項目で設定 ・URL ・URLパラメータ ・API処理後のコールバック (その他、POST/GETやデータ型なども設定できると良い?)
var requests = [
{
url: 'https://xxxx/api/user',
params: {'user_id': 394},
callback: callbackUser
},
{
url: 'https://xxxx/api/application',
params: {'user_id': 394},
callback: callbackApplication
},
];
こうすることで、callbackを別モデル、ファイルなどに分離して記述できるかと。 実用レベルだと、APIリクエスト設定も動的に抽象化が必要ですが。。。
並列処理をする際「jQuery.Deferred」を利用 ざっとした流れは、 (1) Deferredオブジェクトを作成 (2) Ajaxリクエスト (3) Ajax処理後返り値を Deferred.resolveWith() でコールバック
詳しくは、 ・「爆速でわかるjQuery.Deferred超入門」 ・「結局jQuery.Deferredの何が嬉しいのか分からない、という人向けの小話」を参照
var requestAjax = function(values) {
var $ajax = $.ajax(values);
var defer = new $.Deferred();
$ajax.done(function(data, status, $ajax) {
defer.resolveWith(this, arguments);
});
$ajax.fail(function(data, status, $ajax) {
defer.resolveWith(this, arguments);
});
return $.extend({}, $ajax, defer.promise());
};
(1) 配列化したリクエストをループで処理 (2) Ajax実行 (3) APIリクエストで設定したコールバックを実行 (3) $.whenで並列処理完了後の処理
function parallelAjax(requests, callback) {
var results = [];
$.each (requests, function(index, value) {
var $ajax = requestAjax({url: value.url, data: value.params}).done(function(res, status) {
if (value.callback) {
value.callback(res);
}
});
results.push($ajax);
});
$.when.apply(null, results).done(function(){
if (callback) callback(results);
});
$.when.apply(null, results).fail(function(){
});
}
「jquery.cookie.js」を利用して、Cookieを保存する。
$.cookie('user_name', 'yoo', {expires: 30 });
var user_name = $.cookie('user_name');
$.cookie('user_name', null);
※「$.removeCookie」も利用できるようだが、動作しなかった(バージョンによる?)