###この記事について
この記事はWebpack5 設定①の続きです。
#webpackを分割
webpackを共有、商用、開発用に分割します。
- webpack.config.jsのファイル名をwebpack.common.jsに変更
- webpack.common.jsをコピーしてファイル名をwebpack.dev.jsに変更
- 同じくもう一つwebpack.common.jsをコピーしてファイル名をwebpack.prod.jsに変更
- それぞれ中身を以下のように変更
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = ({ outputFile, assetFile }) => ({
entry: {
app: "./src/app.js",
sub: "./src/sub.js",
},
output: {
path: path.resolve(__dirname, "public"),
filename: `${outputFile}.js`,
},
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
exclude: /node_modules/,
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: "eslint-loader",
options: {
fix: true,
},
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader",
],
},
{
test: /\.(jpe?g|gif|png|svg|woff2?|ttf|ept)$/,
use: [
{
loader: "file-loader",
options: {
name: `${assetFile}.[ext]`,
outputPath: "images",
publicPath: "images",
},
},
],
},
{
test: /\.html$/,
use: ["html-loader"],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: `${outputFile}.css`,
}),
],
});
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpackMerge = require("webpack-merge").merge;
const commonConf = require("./webpack.common");
const outputFile = "[name]";
const assetFile = "[name]";
module.exports = () =>
webpackMerge(commonConf({ outputFile, assetFile }), {
mode: "development",
devtool: "source-map",
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
inject: "body",
}),
],
});
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpackMerge = require("webpack-merge").merge;
const commonConf = require("./webpack.common");
const outputFile = "[name].[chunkhash]";
const assetFile = "[name].[contenthash]";
module.exports = () =>
webpackMerge(commonConf({ outputFile, assetFile }), {
mode: "production",
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
inject: "body",
}),
],
});
bundleして作られたファイルを自動で消すために以下を追加
yarn add rimraf --dev
webpack-mergeも追加
yarn add webpack-merge --dev
package.jsonにscripts追加
{
"name": "webpack-lesson",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
// ここから追加
"scripts": {
"cleanup": "npx rimraf ./public",
"dev": "npm run cleanup && npx webpack --config ./webpack.dev.js",
"build": "npm run cleanup && npx webpack --config ./webpack.prod.js"
},
// ここまで
"devDependencies": {
// ...省略
}
ローカルパッケージを実行するときはnpxでscriptsのコマンドはnpm run
#ファイルをミニファイ化する
jsのミニファイプラグイン(terser-webpack-plugin)とcssのミニファイプラグイン(optimize-css-assets-webpack-plugin, html-webpack-plugin)を追加
html-webpack-pluginはすでに入れてあるので他の残りをinstall
yarn add optimize-css-assets-webpack-plugin terser-webpack-plugin --dev
webpack.prod.jsに追加
const HtmlWebpackPlugin = require("html-webpack-plugin");
const OptimizeCssPlugin = require("optimize-css-assets-webpack-plugin"); // 追加
const TerserPlugin = require("terser-webpack-plugin"); // 追加
const webpackMerge = require("webpack-merge").merge;
const commonConf = require("./webpack.common");
const outputFile = "[name].[chunkhash]";
const assetFile = "[name].[contenthash]";
module.exports = () =>
webpackMerge(commonConf({ outputFile, assetFile }), {
mode: "production",
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
inject: "body",
// ここから追加
minify: {
collapseWhitespace: true,
keepClosingSlash: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
},
// ここまで
}),
],
// ここから追加
optimization: {
minimizer: [new TerserPlugin(), new OptimizeCssPlugin()],
},
// ここまで
});
各プラグインについてのリンク
#開発サーバー設定
webpackのサーバー追加
yarn add webpack-dev-server --dev
webpack.dev.jsに設定
webpadk dev-serverページ
// ...省略
module.exports = () =>
webpackMerge(commonConf({ outputFile, assetFile }), {
mode: "development",
devtool: "source-map",
// ここから追加
devServer: {
open: true,
contentBase: "./public",
watchOptions: {
ignored: /node_modules/,
},
},
// ここまで
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
inject: "body",
}),
],
});
scriptsの追加と変更
{
"name": "webpack-lesson",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"cleanup": "npx rimraf ./public",
"dev": "npm run webpack:dev && npm run webpack:server",
"webpack:server": "npx webpack serve --config ./webpack.dev.js",
"webpack:dev": "npm run cleanup && npx webpack --config ./webpack.dev.js",
"build": "npm run cleanup && npx webpack --config ./webpack.prod.js"
},
"devDependencies": {
// ...省略
#共有プラグイン設定
jQueryを例に、使用場所ごとにimportしなくても使えるように設定する
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { ProvidePlugin } = require("webpack"); // 追加
// ...省略
plugins: [
new MiniCssExtractPlugin({
filename: `${outputFile}.css`,
}),
// ここから追加
new ProvidePlugin({
jQuery: "jquery",
$: "jquery",
}),
// ここまで
],
});
.eslintrcにも追加
{
"env": {
"browser": true,
"es2017": true,
"jquery": true // 追加
},
// ...省略
#SplitChunksを設定
一つ前のセクションでjQueryの共有プラグイン設定をしましたが、現状ではapp.jsでsub.jsそれぞれでjQueryを使用してる場合、jQueryを2回読み込んでることになります。またそれぞれのjsファイルに変更があった場合にjQueryを含んだキャッシュが生成されているので変更があるたびにjQueryも再配信されることになります。
そうした無駄を無くすためにバンドル時にjQueryを別で生成して、他のjsファイルに変更があった場合にjQueryは再配信しなくても済むように処理をします。
webpack optimization.splitChunksページ
###jQueryのようなサードパーティーライブラリの登録
webpack.common.jsにSplitChunksを設定
// ...省略
output: {
path: path.resolve(__dirname, "public"),
filename: `${outputFile}.js`,
chuncFilename: `${outputFile}.js`, // 追加
},
// ...省略
plugins: [
new MiniCssExtractPlugin({
filename: `${outputFile}.css`,
}),
new ProvidePlugin({
jQuery: "jquery",
$: "jquery",
}),
],
// ここから追加
optimization: {
splitChunks: {
chunks: "all",
minSize: 0,
cacheGroups: {
vendor: {
name: "vendors",
test: /[\\/]node_modules[\\/]/,
priority: -10,
},
default: false,
},
},
// ここまで
},
});
###独自の共通部品の登録
次の例ではsrc直下にutilsフォルダ作成して、その中にindex.jsを作成し、
以下のようなfunctionをExportして、それぞれのjsファイルでimportせずに使いまわせるようにする
export default {
log: function (str) {
console.log(str);
},
};
webpack.common.jsに以下を追加
// ...省略
plugins: [
new MiniCssExtractPlugin({
filename: `${outputFile}.css`,
}),
new ProvidePlugin({
jQuery: "jquery",
$: "jquery",
utils: [path.resolve(__dirname, "src/utils"), "default"], // 追加 指定したpathでexportしているdefault関数を指定している
}),
],
optimization: {
splitChunks: {
chunks: "all",
minSize: 0,
cacheGroups: {
vendors: {
name: "vendors",
test: /node_modules/,
priority: -10,
},
// ここから追加
utils: {
name: "utils",
test: /src[\\/]utils/,
},
// ここまで
default: false,
},
},
},
});
.eslintrcにutils追加
{
"env": {
"browser": true,
"es2017": true
},
"extends": "eslint:recommended",
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module"
},
"globals": {
"utils": "readonly", // 追加
"jQuery": "readonly",
"$": "readonly"
},
"rules": {
}
}
#Resolveを使う
ツリー構造を整理してresolveを設定することでpathの最適化をします。
src直下にjsフォルダを作成して、その中にutilsを移動
app.jsとsub.jsもjsフォルダに移動
scssフォルダを作成して、その中にapp.scssを移動
ファイルの移動に合わせてpathを変更
// ...省略
module.exports = ({ outputFile, assetFile }) => ({
entry: {
app: "./src/js/app.js", // 変更
sub: "./src/js/sub.js", // 変更
},
// ...省略
new ProvidePlugin({
jQuery: "jquery",
$: "jquery",
utils: [path.resolve(__dirname, "src/js/utils"), "default"], // 変更
}),
// ...省略
utils: {
name: "utils",
test: /src[\\/]js[\\/]utils/, // 変更
},
// ...省略
resolve追加
optimization: {
splitChunks: {
chunks: "all",
minSize: 0,
cacheGroups: {
vendors: {
name: "vendors",
test: /node_modules/,
priority: -10,
},
utils: {
name: "utils",
test: /src[\\/]js[\\/]utils/,
},
default: false,
},
},
},
// ここから追加 resolveを設定することでpathや拡張子を省略して書くことができます。
resolve: {
alias: {
"@scss": path.resolve(__dirname, "src/scss"),
"@imgs": path.resolve(__dirname, "src/images"),
},
extensions: [".js", ".scss"],
modules: [path.resolve(__dirname, "src"), "node_modules"],
},
// ここまで
});
#VSCodeでResolveの自動補完設定をする
root直下にjsconfig.jsonファイルを作成し以下を記入
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@scss": ["scss"],
"scss": ["scss"],
"js": ["js"],
"images": ["images"]
}
}
}
VSCodeを再起動すると補完が効く
#非同期でファイルを読み込む
optimization: {
splitChunks: {
chunks: "all", // chunksをallに設定するとdfefaultでは同期処理するようになり
minSize: 0, // cacheGroupsの個々のプロパティーにchunks: 'async'と設定するとで
cacheGroups: { // 非同期で読み込むことができる。
vendors: {
name: "vendors",
test: /node_modules/,
priority: -10,
},
utils: {
name: "utils",
test: /src[\\/]/,
chunks: 'async' // この設定を追加すると非同期で読み込まれる。
}, // たとえば重たい画像ファイルだけを非同期にしたりすることがある
非同期で呼び出すファイルは下記のようにダイナミックインポートで指定する
import("@scss/app);
#複数のHTMLファイルでスクリプトタブで生成する
以下の例ではindex.htmlにはapp.js、other.htmlにはsub.jsをスクリプトタグで生成する
src直下にother.htmlファイルを作成
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>this is other html</h1>
</body>
</html>
html-webpack-pluginをインストール
yarn add --dev html-webpack-plugin
// ...省略
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
inject: "body",
chunks: ["app"], // 追加
}),
// ここか追加
new HtmlWebpackPlugin({
template: "./src/other.html",
filename: "other.html",
inject: "body",
chunks: ["sub"],
}),
// ここまで
],
});
以上になります。
最後までお読みいただきありがとうございました。