webpackって超便利ですよね。
違うファイルで作ったモジュールを1つにまとめてることができて、bundle.jsみたいなファイルにまとめることが出来て。。。
でも。。。。
僕みたいなまだまだレガシーエンジニアには1つのファイルにまとめてSPAライクにすることなんて出来ないわけです。
ぶっちゃけ共通のApp.jsと各ページ1ファイル読み込むパターンで十分なんです。
初期ロードで数10MBのjsを読み込むのって我慢できないんです。
なので今回はwebpackを使って複数のディレクトリにまたがったファイルを依存解決も行いながら別ディレクトリに同じ構成で書き出すのをやってみたいと思います。
目指す姿
applicationのルートディレクトリにfrontendsディレクトリを作成し、PC用の各ページのjsファイル、mobile各ページ用のjsファイル、共通で使えそうなmodulesファイルに分けました。
assets
frontends
├── App.es6
├── App.mobile.es6
├── mobile
│ └── users
| ├── setting.es6
| └── show.es6
├── modules
│ ├── _Dropdown.es6
│ ├── _Follow.es6
│ ├── _Tab.es6
│ └── _XhrMethods.es6
├── pc
│ └── users
| ├── setting.es6
| └── show.es6
└── validations
├── _Contact.es6
├── _Password.edit.es6
├── _Password.edit.from.email.es6
└── _Send.email.es6
もちろん各ファイルの中ではimport
しまくってます!
// pc/users/show.es6
import Tab from '../../modules/_Tab.es6'
import DropDown from '../../modules/_DropDown.es6'
const tab = new Tab({active: 'hogehoge',...})
const dropDown = new DropDown({el: 'jsDropdown'})
// more
実際に使ってるのはApp.es6, App.mobile.es6, pc/, mobile/
なので、そのファイルをそのままdist/javascripts
に書き出したいと思います。
dist/javascripts
├── App.es6.js
├── App.mobile.es6.js
├── mobile
│ └── users
| ├── setting.es6.js
| └── show.es6.js
└── pc
└── users
├── setting.es6.js
└── show.es6.js
よくあるwebpackで複数ファイルを出力する方法
実際によくあるのが、webpack.config.js
のentries
にhashを指定すること複数のファイルの出力に対応するということです。
例: 複数ファイルを指定
module.exports = {
entry: {
pc_users_show: 'frontends/users/show.es6',
pc_users_setting: 'frontends/users/setting.es6',
// more
},
output: {
path: '/dist/javascripts',
filename: '[name].js'
},
// その他の設定
}
こうしてwebpackwを実行すると/dist
にentry
で指定したkeyの名前でファイルが主力されます。
dist/javascripts
├── pc_users_show.es6.js
└── pc_users_setting.es6.js
ここで問題なのがoutput.pathが1つのみなのでディレクトリごとに書き出すなどの処理ができないということです。
webpackで階層ごとにファイルを書き出す
output.pathではディレクトリが1つしか指定できませんが、entryのkeyをいじることで階層ごとに書き出すことが可能です。
module.exports = {
entry: {
pc/users/show: 'frontends/users/show.es6', //keyを階層化して指定する
pc/users/setting: 'frontends/users/setting.es6',
// more
},
output: {
path: '/dist/javascripts',
filename: '[name].js'
},
// その他の設定
}
↓
dist/javascripts
└── pc
└── users
├── setting.es6.js
└── show.es6.js
こうすることで階層化しつつ、各モジュール依存を解決した状態で書き出すことは出来ました(๑•̀ㅂ•́)و✧
ただ、ファイルが増えるたびに全てのファイルをentryのkeyの追加していくのは面倒くさいです。
なので、glob
を使用してファイル名を取得してkeyも一緒に作るようにしちゃいましょう!
globを使用してwebpackでいい感じに階層化してモジュール依存解決して書き出す
const path = require('path'),
glob = require('glob'),
_ = require('lodash');
const jsBasePath = path.resolve(__dirname, 'frontends'),
jsDistPath = path.resolve(__dirname, 'dists/javascripts')
// filenameを取得するメソッドを追記
// 後から使う
String.prototype.filename = function(){
return this.match(".+/(.+?)([\?#;].*)?$")[1];
}
// jsBasePath配下の.es6のpathも含めたファイルを取得する
// _から始まるファイルはmoduleなので書き出す必要はないのでいらない
var targets = _.filter(glob.sync(`${jsBasePath}/**/*.es6`), (item) => {
return !item.filename().match(/^_/)
});
// entryに入れるhash
var entries = {};
// pathも含めたfilenameからpathとfilenameでhashを作る
targets.forEach(value => {
var re = new RegExp(`${jsBasePath}/`);
var key = value.replace(re, '');
// 確認用に取得したファイル名を出す
console.log('--------------------------')
console.log(key)
console.log(value.filename())
entries[key] = value;
});
module.exports = {
entry: entries, //作成したhashをset
output: {
path: jsDistPath,
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.es6$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2015']
}
}
]
}
}
こうすることで階層構造を保ったままファイルを書き出すことが出来ました。
Hash: a94765d83d77487ff60d
Version: webpack 1.13.3
Time: 18715ms
Asset Size Chunks Chunk Names
App.es6.js 596 kB 0 [emitted] App.es6
mobile/users/setting.es6.js 7.86 kB 1 [emitted] mobile/users/setting.es6
mobile/users/show.es6.js 592 kB 2 [emitted] mobile/users/show.es6
App.mobile.es6.js 583 kB 3 [emitted] App.mobile.es6
pc/users/setting.es6.js 7.86 kB 4 [emitted] pc/users/setting.es6
pc/users/show.es6.js 593 kB 5 [emitted] pc/users/show.es6
+ 386 hidden modules
[BS] Reloading Browsers...
終わりに
みなさんお気付きの通り、こちら超ゴリゴリの実装な気がしてたまりません(´;ω;`)
何かいい方法があればご教授ください。。。