react-create-app
とかボイラーテンプレートでサクッとできる時代だけど、自分で作ってみて学ぼうと思ったので、秘伝のタレ作ってた 🍲
構成
/build ... ビルドされたファイルが出力される /src |- /stylus | |- App.styl |- index.js |- index.pug package.json
環境
@babel/core: 7.4.5 webpack: 4.34.0 webpack-dev-server: 3.7.1 # plugins html-webpack-plugin: 3.2.0 mini-css-extract-plugin: 0.7.0 clean-webpack-plugin: 3.0.0
つくったのよこせ!
はい。
TL;DR 秘伝のタレレシピ
Babel
install
$ yarn add -D @babel/core @babel/cli @babel/preset-env core-js@3 $ yarn add @babel/polyfill
設定ファイル babel.config.js
を作成して設定を書いていく。
複雑なことはせずにシンプルにES6使えるようにする感じで。
babel.config.js
const presets = [ ["@babel/preset-env", { "modules": false, "targets": [">0.25% in JP", "not ie <= 10", "not op_mini all"], "useBuiltIns": "usage", "corejs": 3, }], ]; module.exports = { presets };
useBuiltIns
を使っている時に core-js
のバージョン指定がないと次のようなWARNINGが表示される
WARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option. You should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands: npm install --save core-js@2 npm install --save core-js@3 yarn add core-js@2 yarn add core-js@3
core-jsのバージョン指定が無いと2系を指定するけど、将来バージョンが変更になるかもって感じっぽい
Webpack
install
$ yarn add -D webpack webpack-cli webpack-dev-server babel-loader
build コマンドを作成
build する npm script を package.json
に追加する
{ // ... 略 "scripts": { "build": "./node_modules/.bin/webpack --mode production", "build:dev": "./node_modules/.bin/webpack --mode development" }, // ... 略 }
設定ファイルの作成
webpack.config.js
を作成して設定を追加していく
webpack.config.js
const path = require('path'); // PATH const ASSET_PATH = process.env.ASSET_PATH || '/'; const src = path.join(__dirname, 'src'); const entrypoint = { // app.js という名前で出力する app: path.join(src, 'index.js'), }; const output = path.resolve(__dirname, 'build'); module.exports = (env, argv) => { return { mode: 'development', entry: entrypoint, output: { path: output, publicPath: ASSET_PATH, filename: '[name].js', }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: [ { loader: "babel-loader", } ], }, ], }, devtool: 'source-map', }; };
eslint とか走ってないけど、とりまモダンなJS出かける環境の設定ができました。
webpack + pug
install
$ yarn add -D pug pug-loader html-webpack-plugin
note. HtmlWebpackPlugin
webpackでbundle したJavaScriptをscriptタグで読み込んだHTMLを生成するプラグイン。template
オプションで pug ファイルなどを生成するHTMLのテンプレートファイルを指定できる。
ref.
- HtmlWebpackPlugin | webpack
- GitHub - jantimon/html-webpack-plugin: Simplifies creation of HTML files to serve your webpack bundles
pug の設定
webpack.config.js
const path = require('path'); // Plugins // HTMLを生成するプラグイン const HtmlWebpackPlugin = require('html-webpack-plugin'); // PATH // 略 // html template const htmlTemplate = path.join(src, 'index.pug'); module.exports = (env, argv) => { const isProduction = argv.mode === 'production'; return { mode: 'development', entry: entrypoint, output: { path: output, publicPath: ASSET_PATH, filename: '[name].js', }, module: { rules: [ // 略 { test: /\.pug$/, use: { loader: 'pug-loader', // production mode の時 minify する options: !isProduction ? { pretty: true } : {} } }, ], }, devtool: 'source-map', plugins: [ new HtmlWebpackPlugin({ template: htmlTemplate, }), ], }; };
Template src/index.pug
doctype html head title My HIDEN no TARE! body h1 秘伝のタレ
👇 webpack
$ yarn run build
/build
ディレクトリ内にHTMLとJavaScriptファイルが生成される。
HTMLファイルには下記のscriptタグが自動的に挿入されている。
<script type="text/javascript" src="/app.js"></script>
webpack + stylus
install
$ yarn add -D stylus stylus-loader css-loader mini-css-extract-plugin
note. css-loader
import
や url
の解決などCSSの依存解決を行う loader
ref. GitHub - webpack-contrib/css-loader: CSS Loader
note.
JavaScriptにbundleされるCSSをCSSファイルに変換するプラグイン
ref.
- MiniCssExtractPlugin | webpack
- GitHub - webpack-contrib/mini-css-extract-plugin: Lightweight CSS extraction plugin
stylus の設定
webpack.config.js
const path = require('path'); // Plugins const HtmlWebpackPlugin = require('html-webpack-plugin'); // CSSファイルを出力するプラグイン const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 略 module.exports = (env, argv) => { const isProduction = argv.mode === 'production'; const enabledSourceMap = isProduction || false; const getStyleLoaders = () => { const loaders = [ { loader: MiniCssExtractPlugin.loader, }, { loader: 'css-loader', options: { // production mode の時 source map を出力する sourceMap: enabledSourceMap, // 事前に適応するloaderの数 importLoaders: 1 }, }, { loader: 'stylus-loader', options: { // stylus では source map を作らない sourceMap: false, }, }, ]; return loaders; }; return { // 略 resolve: { // import を js ファイル以外でも使えるようにする modules: ['node_modules'], // import できる拡張子を指定 extensions: ['.json', '.js', '.jsx', '.styl'], }, module: { rules: [ // 略 { test: /\.styl$/, use: getStyleLoaders(), // CSS は Webpack の Tree Shaking での自動的削除は // 問題がある可能性があるので sideEffects を true にする sideEffects: true, }, ], }, devtool: enabledSourceMap && 'source-map', plugins: [ new HtmlWebpackPlugin({ template: htmlTemplate, }), new MiniCssExtractPlugin({ // stylesheets ディレクトリの中に出力させる filename: "stylesheets/[name].css", }), ], }; };
webpack の loader は下から順番に実行されるので CSS の処理は次のような感じ
stylus-loader
で stylus を コンパイルcss-loader
で CSS の依存を解決し JS ファイルに bundleMiniCssExtractPlugin.loader
で JS に bundle された CSS を CSSファイルにして出力
CSSの出力
webpack.config.js
に resolve
の設定を追加し stylus ファイルを import 出来るようにする。
module.exports = (env, argv) => { return { // ... resolve: { // import を js ファイル以外でも使えるようにする modules: ['node_modules'], // import できる拡張子を指定 extensions: ['.json', '.js', '.jsx', '.styl'], }, // ...
※ webpack 4 では拡張子の指定に extensions: ['', '.json', '.js', '.jsx', '.styl']
のように空文字(''
)があるとエラーになる
エントリーポイントになっているsrc/index.jsに読み込ませたい stylus ファイルを読み込ませる。
import './stylus/App.styl';
👇 wepack
$ yarn run build
/build/stylesheets
内に app.css
と app.css.map
が生成される。
出力されるHTMLはheadタグ内に自動的に下記のような<link>
が作られ css が読み込まれるようになっている
<link href="/stylesheets/app.css" rel="stylesheet">
PostCSS で 自動的にベンダープレフィックスを付けたりよしなにする
stylus の nib を使ってもよいのですが、最近は PostCSS を使ってよしなにするのがナウいらしいので、PostCSSを使ってベンタープレフィックスと nomalize 、メディクリの統合・productionモードの際にminifyをしたいと思います。 (Prettierでコードフォーマットを揃えるのはまたいつか)
install
$ yarn add -D postcss-loader postcss-preset-env postcss-flexbugs-fixes postcss-normalize css-mqpacker cssnano
postcss-loader
... PostCSSをwebpackで使うためのloaderpostcss-preset-env
... モダンCSSをconvertするパッケージ。対応ブラウザに合わせたautoprefixの指定などもできるpostcss-flexbugs-fixes
...display: flex
のブラウザのバグ対応したCSSを追加してくれるpostcss-normalize
...browsers
で指定した対応ブラウザに合わせて必要な normalize.css を追加するcss-mqpacker
... mediaクエリをまとめるパッケージ。デファクトスタンダードっぽいけど、現在メンテがされなくて deprecated になっているので将来的にforkされたべつのパッケージになるかも
cssnano
... CSSをminifyするライブラリ
PostCSS の設定
webpack.config.js
// 略 // CSS // normalize.css を追加 const postcssNormalize = require('postcss-normalize'); // media query をまとめる const cssMqpacker = require('css-mqpacker'); // css minify const cssnano = require('cssnano'); module.exports = (env, argv) => { const isProduction = argv.mode === 'production'; const enabledSourceMap = isProduction || false; const getStyleLoaders = () => { const loaders = [ { loader: MiniCssExtractPlugin.loader, }, { loader: 'css-loader', options: { sourceMap: enabledSourceMap, // stylus と PostCSS の2つ importLoaders: 2 }, }, { loader: 'postcss-loader', options: { plugins: [ // production mode の時 minify isProduction && cssnano({ preset: 'default' }), cssMqpacker({ sort: true }), require('postcss-flexbugs-fixes'), require('postcss-preset-env')({ autoprefixer: { flexbox: 'no-2009', }, stage: 3, }), postcssNormalize({ browsers: [">0.25% in JP", "not ie <= 10", "not op_mini all"], }), ].filter(Boolean), // false を削除 sourceMap: enabledSourceMap, } }, { loader: 'stylus-loader', options: { sourceMap: false, }, }, ]; return loaders; }; return { // 略 module: { rules: [ // 略 { test: /\.styl$/, use: getStyleLoaders(), sideEffects: true, }, ], }, // 略 }; };
build 時に出力先のディレクトリを cleanup する
development
mode の時は source map を出力しないようにしているので、build した際に不要なファイルが残っていないようにclean-webpack-plugin
プラグインを使って、buildが走る前に出力先build
ディレクトリの中をcleanupする。
install
$ yarn add -D clean-webpack-plugin
clean-webpack-plugin の設定
webpack.config.js
const path = require('path'); // plugins const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // 略 module.exports = (env, argv) => { // 略 return { // 略 plugins: [ new CleanWebpackPlugin({ // 実際に削除してしまう前にテストする場合は true にする dry: false, // 削除するファイル名をconsoleに出力 verbose: true, // static なファイルを置いておく可能性があるので img ディレクトリ内のファイルは削除しない cleanOnceBeforeBuildPatterns: ['**/*', '!img', '!img/*'] }), // 略 ], }; };
👇 動作確認
$ yarn run build $ yarn run build:dev
production mode で build した後に development mode で build して app.css.map
など不要なファイルが消えていればOK
ホットリロード可能な確認環境 webpack-dev-server
webpackでwatchできるファイルを監視してホットリロードできるとても便利なサーバー
install
$ yarn add -D webpack-dev-server
webpack-dev-server の設定
open
オプションで自動的にブラウザを開く機能があるが、常に新しくページを開かれるのもストレスなのでオプションで新しく開くかどうか選べるようにします。
package.json にサーバー起動の npm script を追加
//略 "scripts": { "start": "./node_modules/.bin/webpack-dev-server --mode development --hot --env.open", "restart": "./node_modules/.bin/webpack-dev-server --mode development --hot", "build": "./node_modules/.bin/webpack --mode production", "build:dev": "./node_modules/.bin/webpack --mode development" }
start
なら新しくブラウザを開き、restart
なら開かない
webpack.config.js
// 略 module.exports = (env, argv) => { const isProduction = argv.mode === 'production'; const enabledSourceMap = isProduction || false; const isOpenDevServer = !!env && env.open; // 略 return { // 略 devServer: { open: isOpenDevServer, port: 3000, watchContentBase: true, } }; };
👇 動作確認
$ yarn run start
localhost:3000
でページが起動し、javascript, pug, stylus を変更でホットリロードされていればOK
ポエム
フレームワークのお作法で開発するなら必要のない部分かもですが、秘伝のタレを自分で作ってみることによって理解が深まったように思います。 特に loader は受け取ったものを JS で返し、plugin は受け取ったものを自由な形式で返せる という違いが発見できたことは学びでした。
昨今のフロントエンドまわりパッケージが乱立しててどれを選べば良いのか…ってのが難しい。
一時期より動きは穏やかになったとは言え、まだまだ変化の早めな界隈な感じなので下手に選ぶとすぐ使えなくなったり、更新が止まって廃止とかになったりするので調査に時間がかかる感は否めないです。(Qiitaとか、日付のないblogとかたくさん検索にかかるので余計に検索に時間が…
[参考]
- 【2019年版】webpack 4 個人的設定まとめ – expexp.jp
- Webpack4でPug、Stylus、TypeScriptの開発環境を整える - Qiita
- DevServer | webpack
pug
- HtmlWebpackPlugin | webpack
- GitHub - jantimon/html-webpack-plugin: Simplifies creation of HTML files to serve your webpack bundles
- なんとなくで理解しないWebpackのCSS周辺 - Qiita
- GitHub - webpack-contrib/css-loader: CSS Loader
- MiniCssExtractPlugin | webpack
- GitHub - webpack-contrib/mini-css-extract-plugin: Lightweight CSS extraction plugin
- webpack 4とmini-css-extract-pluginを使ってCSSを別ファイルに書き出す方法 | フロントエンドBlog | ミツエーリンクス
- webpack 4 複数のstyl(css)を別ファイルで出力するためのwebpack.config.js | e-JOINT.jp
PostCSS
\秘伝の🌟タレ/