React #1 Advent Calendar 2017 2日目の記事です。
もう2018年になって一月経ちましたが空いていたので埋めます。
webpack 4 のbeta版が2018/01/25 JSTにプレリリースされました。
おそらく2月か3月中には正式リリースされるでしょう。
(追記 2/25)
正式リリースされました
正式版でもこの記事の内容に問題が無いことを確認しました。
https://github.com/webpack/webpack/releases/tag/v4.0.0
今回のwebpackは変更点が多く、速度改善もされているようです。
また設定ファイルも不要となり、webpack.config.js書くのが嫌な方には朗報です。
この記事では設定ファイル無しでビルドするところまで簡単な例を用いて紹介いたします。
webpack 4について
今回はReactのビルドに関する記事なのでwebpack4の変更点をすべて載せる予定はありません。
それらについては以下の記事が詳しいので拝見してください。
- Release v4.0.0-beta.0 · webpack/webpack
- 次のリリースであるwebpack 4の主な変更点まとめ
- webpack 4 beta — try it today!
- Webpack 4 tutorial: All You Need to Know, from 0 Conf to Production Mode
- Release v4.0.0 · webpack/webpack
拙者が試したプロジェクトの周辺環境
- babel-preset-env
- babel-preset-react
- flow
- webpack 3.8
これまでのwebpackの内容
js
とjsx
ファイルをミニファイして一つにバンドルしているだけです。
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
'index': [
path.resolve(__dirname, 'src/index.js')
]
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'public'),
publicPath: '/',
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}),
new webpack.optimize.UglifyJsPlugin(),
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
},
]
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
webpack 4への移行
準備
webpack 4とwebpack-cliをインストールします。
webpackをCLIで実行するのにこれまではwebpackだけで実行できたのですが、
webpack 4からはwebpack-cliに分離しました。
npm install --save-dev webpack webpack-cli
設定ファイルを使ったビルド
設定ファイルありバージョンを先に紹介します。
上記で紹介したwebpack.config.jsではwebpack4を使ったビルドは通りません。
上記の設定のままwebpackを実行すると、
TypeError: webpack.optimize.UglifyJsPlugin is not a constructor
とエラーが出ました。
これはmode
という新しいオプションが関係します。
mode
にはproduction
とdevelopment
があります。
production
は最適化オプションであるwebpack.optimization
のプラグインが有効になります。
そのため、今まで指定していたwebpack.optimize.UglifyJsPlugin
は不要となります。
development
では最適化はされず、eval-source-maps
が有効になります。
またwatchで動かす場合はdevelopment
を指定しましょう。
mode
を指定せずにwebpackを実行した場合、どちらかを指定するように警告が出ます。
なので、mode
も設定しましょう。
修正したwebpack.config.jsは以下の通りです。
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production', // 追加
entry: {
'index': [
path.resolve(__dirname, 'src/index.js')
]
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'public'),
publicPath: '/',
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
// webpack.optimize.UglifyJsPluginを削除
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
},
]
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
設定ファイルを使わないビルド
今回の目玉でもあるでしょう設定ファイルwebpack.configを使わないビルドについて紹介します。
module-bind
まず何も考えずにオプションを付けずにwebpackを実行してみると、以下のようなエラーが出ました。
Module parse failed: Unexpected token (15:6)
You may need an appropriate loader to handle this file type.
| render() {
| return (
| <div>
これはbabelが利いておらず、JSX文法を解析できていないためです。
webpack.config.jsでは以下のように指定していた部分です。
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
},
]
},
webpack.config内で指定していたbabel-loader
を指定するには--module-bind
オプションを使います。
--module-bind 拡張子=ローダー名
で使うことができます。
webpack --module-bind 'js=babel-loader' --module-bind 'jsx=babel-loader'
※jsとjsxを一つにまとめてjs,jsx=babel-loader
ということができたら良いのですが、うまくいきませんでした。もし知っている方がいればコメントお願い致します。
resolve-extensions
上記--module-bind
オプションをつけて実行すると別のエラーがでました。
Module not found: Error: Can't resolve './Foo' in '/path/to/src'
これはimport先のモジュールが読み込めていないエラーです。
以下のように拡張子を省いている場合、ファイル解決ができません。
import Foo from 'components/Foo'
これをwebpack.config.jsでは以下で設定することで解決していました。
resolve: {
extensions: ['.js', '.jsx'],
},
CLIにもこれと同じことができるresolve-extensions
というオプションがあります。
--resolve-extensions .js,.jsx
のように拡張子を指定します。
先程のmodule-bind
と組み合わせると以下のようになります。
webpack --module-bind 'js=babel-loader' --module-bind 'jsx=babel-loader' --resolve-extensions .js,.jsx
mode
このままでは警告が出ます。というのも前述したmode
を指定していないためです。
mode
オプションをつけると警告は消えます。以下のようになります。
webpack --module-bind 'js=babel-loader' --module-bind 'jsx=babel-loader' --resolve-extensions .js,.jsx --mode production
entry
エントリポイントを指定することもできます。
--entry src
のように指定するとsrcディレクトリ内にあるindex.jsをエントリポイントとします。
--entry src/entry.js
のようにファイルを指定することもできます。
output
出力先を指定することもできます。
--output public
とするとpublicディレクトリにエントリポイントのファイルと同じファイル名のファイルができます。
--output public/index.bundle.js
のようにファイル名を指定することもできます。
また、-o public/index.bundle.js
と--output
を-o
とすることもできます。
最終形
上記で紹介したオプションをすべて反映させて最終的には以下のようにすることで、
前述したwebpack.config.jsを使ったビルドと同じことができます。
webpack --module-bind 'js=babel-loader' --module-bind 'jsx=babel-loader' --resolve-extensions .js,.jsx --mode production --entry ./src --output public/index.bundle.js
他オプション
webpack --help
でオプションの一覧が見れます。
各オプションについては別の記事で紹介するかもしれません。
所感
webpackでもparcelのように設定ファイル無しでビルドすることができました。しかし数多くのオプションを指定しなければいけません。
webpack.config.jsを用いる方がわかりやすいかどうか好みが別れるとこかと思いますが、個人的には選択肢が増えて良いかと思います。
もっと複雑な構成になると設定ファイル無しでどこまでできるかまだ検証できていませんが、これからはできる限り設定ファイル無しで開発できたらいいなと考えています。
また、今回のwebpackはビルド速度の改善も大幅にされています。今回試した感じではParcelより早いかもと感じました。
Keep webpack Fast: A Field Guide for Better Build Performanceがビルド速度改善に関して詳しく書かれています。
参考
- Release v4.0.0-beta.0 · webpack/webpack
- 次のリリースであるwebpack 4の主な変更点まとめ
- webpack 4 beta — try it today!
- Webpack 4 tutorial: All You Need to Know, from 0 Conf to Production Mode
- Keep webpack Fast: A Field Guide for Better Build Performance
- Release v4.0.0 · webpack/webpack
最後までお読み頂きありがとうございました。
不備や質問がありましたらコメントお願い致します。