webpackって使うことあるのかな?と思っていましたが、使うと便利です
2回目からはほぼコピペだし😅
いろいろ対応できるように、理解できている範囲でまとめておこうと思います
webpackでしていること
- babelでJSのトランスパイル
- scssのコンパイル
- 画像な圧縮
- ファイルの圧縮
- モジュールの利用と依存関係の解決(読み込みの順番を気にしない)
ちなみに「Parcel」は、npm install -g parcel
だけで、設定ファイルも不要でトランスパイルやビルドが行えるらしい!!
*Node.jsインストール済み
npm
JavaScriptのパッケージを管理するツールで、依存するパッケージもまとめてインストールしてくれるnpm init -y
で「package.json」を作成
オプション
--save
実行時に必要な依存パッケージの場合
package.json の dependencies に記録されます(もし誰かが npm install したときに依存パッケージとしてインストールされる)--save-dev
開発用のパッケージの場合
package.jsonのdevDependencies に記録されます(されない)
*もし誰かがこのパッケージをインストールしたときに依存パッケージとしてインストールされるかどうかなので実質的には関係ない
webpackのインストールですが…
すべてインストールするのは面倒なので、基本のpackage.jsonから流用します😅
コピペしてnpm install
webpack webpack-cli
package.jsonファイル
{
"name": "hogehoge",
"scripts": {
"start": "webpack-dev-server --config webpack.dev.js",
"dev": "webpack --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
"devDependencies": {
"@babel/core": "^7.10.5",
"@babel/preset-env": "^7.10.4",
"autoprefixer": "^9.8.5",
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.5.3",
"file-loader": "^6.0.0",
"html-loader": "^1.1.0",
"html-webpack-plugin": "^4.3.0",
"image-webpack-loader": "^6.0.0",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.14.1",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"sass-loader": "^8.0.2",
"terser-webpack-plugin": "^3.0.8",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.0.9"
},
"dependencies": {
"core-js": "^3.6.5",
"regenerator-runtime": "^0.13.7",
}
}
設定ファイル
webpack.config.js(設定ファイル)
( webpack – – mode development)などをまとめて
設定ファイルにしてwebpackコマンドのオプションとして渡す ( – – config ファイル名)
基本の形
outputのpathの値は絶対パスなのでpathモジュールを使う
src直下にエントリーポイントを作る、public直下にバンドルされる
*デフォルトのdist直下でいい場合は、outputのpathは不要
const path = require('path');
module.exports={
mode: '',
devtool: '',
entry: './src/',
output: {
path: path.resolve(__dirname, 'public'),
filename: '',
},
optimization: {
},
module: { //ローダーの設定
rules: [],
},
plugins: [],
}
複数のエントリーポイントにする時はentryの値をオブジェクトにする
entry: {
app: './src/js/app.js',
hoge: './src/js/hoge.js',
},
output: {
path: path.resolve(__dirname, 'public'),
filename: 'js/[name].js',
chunkFilename: 'js/[name].js',
},
outputのファイル名には[name]をつける(プレスホールダーになる)
[name]にentryのkeyが入る
chunkFilenameは、エントリーポイント以外(splitChunksプラグインなど)から出力されたファイルの名前を設定する
設定ファイルを分ける
webpack-merge
const output = merge(object1, object2, object3, ...)
- webpack.common.js(共通)
- webpack.dev.js(開発用)
- webpack prod.js(本番用)
例:productionではミニファイされるようになっている(TerserPlugin)
webpack.dev.js(開発用)
const path = require('path');
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
module.exports = merge(commonConfig, {
mode: 'development',
watch: true,
devtool: 'cheap-module-eval-source-map',
devServer: {
open: false,
port: 9000,
contentBase: path.resolve(__dirname, 'public'),
}
});
開発用サーバー
webpack-dev-server
webpack-dev-server(npm scriptsで実行)
const path = require('path');
devServer: {
open: true,
port: 9000,
contentBase: path.resolve(__dirname, 'public'),
}
*メモリを保持するだけで、ファイルに出力しない
webpack.common.js(共通)
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
devtool: 'none',
entry: './src/js/app.js',
output: {
path: path.resolve(__dirname, 'public'),
filename: 'js/[name].js',
},
optimization: {
splitChunks: {
chunks: 'initial',
name: 'vendor',
}
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.(jpe?g|gif|png|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[contenthash].[ext]',
outputPath: 'images',
publicPath: '/images',
},
},
'image-webpack-loader',
],
},
{
test: /\.html$/,
loader: 'html-loader',
},
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
inject:'body'
}),
new MiniCssExtractPlugin({
filename: './css/.[name].[contenthash].css',
}),
],
};
出力したファイルを消す
clean-webpack-plugin
HTMLをどこに配置するか
バンドルしたファイルの読み込みを手動でするか、webpackで管理するか
public直下に置く場合(手動)
HTMLファイル以外の全てのファイルやディテクトリが削除対象
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns:['**/*', '!**.html'],
}),
],
src直下に置く(webpackで管理)
html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
new HtmlWebpackPlugin({
template: './src/index.html',
}),
エントリーポイントが2つの場合
new HtmlWebpackPlugin({
template: './src/index.html',
// appから出力されるjs を読み込んだ HTML を出力する
chunks: ['app'],
}),
new HtmlWebpackPlugin({
filename: 'hoge.html',
template: './src/hoge.html',
chunks: ['hoge'],
}),
ブラウザキャッシュ対策(disk cache)
ファイル名を変更する
outputにハッシュ(例:[contenthash] )を追加
output: {
path: path.resolve(__dirname, 'public'),
filename: 'js/[name].[contenthash].js',
},
- [contenthash]更新されたをコンテンツ毎
- [hash]ビルド全体で同じハッシュを取得
- [chunkhash]chunk単位で割り振られる
画像の出力
background-image
file-loader
options: {
name: '[name].[contenthash].[ext]',
//publicからの相対パス(格納先)
outputPath: 'images',
//画像のパス(別のサーバーに画像がある場合など状況に応じて)
publicPath: 'images',
},
HTMLで読み込んでいる画像
html-loader
webpackgがテンプレートを解析するために必要
画像の圧縮
image-webpack-loader
バンドルファイルを分割する場合は
splitChunksプラグイン
- 目的はキャッシュ活用のため、更新頻度が高いものと低いものを一緒にバンドルしないようにする
- 「html webpackプラグイン」を使用していたら、自動で読み込み追加される
- デフォルトの設定(chunksはasyncになっている)を上書きする(インストール不要)
上書きした例
async:ダイナミックインポートしたモジュール(デフォルト)
initial :npmでインストールしたモジュール
all:どちらも
複数に分割する時cacheGroups:{ }
optimization: {
//ここ
}
splitChunks: {
chunks: 'initial',
cacheGroups: {
hoge1: {
//npmインストールしたモジュール
test: /node_modules/,
name: 'vendor',
},
hoge2: {
test: /src[\\/]js[\\/]hoge/,
name: 'hoge',
//分割対象ファイルの最小サイズ
minSize: 0,
//2つ以上のファイルで利用されている場合分割対象
minChunks: 2,
},
},
},
JavaScript
Babelのインストール
BabelはJavaScriptのトランスパイラ、core-jsというPolyfillを利用します
babel-loader(ローダー)
babel/preset-env(babelPluginsの集まり)
babel/core(本体)
regenerator-runtime (async/await)
余談 React : @babel/preset-react
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
]
}
対象ブラウザを指定する
ファイルサイズを減らすため、必要なポリフィルのみ取り込む
.browserslistrc ファイルを作成(共通利用できる)
または、package.jsonに
“browserslist”:[“ie 11”]を追加しても良い
ie 11
設定ファイル(babel.config.js)
設定ファイル(babel.config.js)
module.exports = {
presets: [
[
'@babel/preset-env',
{
//必要なポリフィルだけ
useBuiltIns: 'usage',
//babelのバージョンが上がると変わる可能性あり
corejs: 3,
},
],
],
};
SCSS
エントリーポイントにscssファイルをimportする
node-sass sass-loader css-loader
css-loaderでJSに読み込んでstyle-loaderでHTMLに出力する
個別のCSSでファイルで出力するのでstyle-loaderは不要
CSSファイルに出力する
mini-css-extract-plugin
ベンダープレフィクスを自動でつける
autoprefixerはpostcss製のプラグイン
autoprefixer postcss-loader
設定ファイル(postcss.config.js)
対応ブラウザは.browserslistrc
module.exports = {
plugins: [require('autoprefixer')],
};
webpack prod.js(本番用)
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = merge(commonConfig, {
mode: 'production',
optimization: {
minimizer: [
new TerserPlugin({
//ライセンスコメント
extractComments: false,
//console.logを消す
terserOptions: {
compress: {
drop_console: true,
},
},
}),
new OptimizeCSSAssetsPlugin({}),
],
},
});
CSSの圧縮と最適化
optimize-css-assets-webpack-plugin terser-webpack-plugin
postcssローダーを使っている場合
postcss.config.jsに追加
//ここを追加
require('cssnano')({
preset: 'default',
}),
postcssローダーを使っていない場合
minimizer: [new TerserPlugin({ }), new OptimizeCSSAssetsPlugin({})],
注意 optimizationのminimizerに追加する時は、new TerserPlugin({ })を追加する
new TerserPlugin({ })がないとJSは圧縮されない
用語
- エントリーポイント
- 最初に見に行くファイル
- ここにモジュールをimportする(起点)
- モジュール
- 部品(jsファイルやsassファイルなど)
- バンドル
- モジュールをまとめる
- JSのトランスパイル
- ES6以降のJavaScriptをそれ以前のものに変換する
- scssのコンパイル
- CSSにする(コンピュータ用にする)
- loader(ローダー)
- ファイルを加工して別の状態にする役割
- webpackはJavaScriptファイルのみ、そのまま使えるのでそれ以外はローダーが必要
- plugins(プラグイン)
- webpackの拡張機能
- Polyfill
- 最近の機能をサポートしていない古いブラウザーで、その機能を使えるようにするためのコード
- PostCSS
- JavaScriptでCSSを変換するためのプラグインを作るツール
必要なファイルだけgulpで圧縮
src直下のファイルたちを、削除した悲しすぎる経験から💧
$ npm install --save-dev gulp gulp-zip
gulpfile.jsを作る
gulpで実行
const gulp = require('gulp');
const zip = require('gulp-zip');
const paths = {
package: {
src: [
"**/*",
"!.vscode",
"!node_modules/**",
"!packaged/**",
"!src/**",
"!babel.config.js",
"!.gitignore",
"!gulpfile.js",
"!package.json",
"!package-lock.json",
"!postcss.config.js",
"!.browserslistrc",
"!webpack.common.js",
"!webpack.dev.js",
"!webpack.prod.js"
],
dest: "packaged",
},
}
exports.default = () => (
gulp.src(paths.package.src)
.pipe(zip('圧縮フォルダの名前.zip'))
.pipe(gulp.dest(paths.package.dest))
);
gulp cliがグローバルインストールされていない場合は
npm scripts(package.json)を使う
{
"scripts": {
//追加
"gulp": "gulp"
}
}