はじめに
Webpackを用いてフロントエンドの環境を構築します。
ディレクトリ構造
root/ - プロジェクトディレクトリ
+ dist/ - サーバーのドキュメントルート
| + js/ - JavaScriptの出力先
| + css/ - CSSの出力先
| ` * - その他のファイル
+ src
| + js/ - JavaScriptのソース
| | + app.js - JavaScriptのエントリポイント
| | ` *.js - その他のJavaScript
| ` scss/ - Scssのソース
| + app.scss - スタイルシートのエントリポイント
| ` _*.scss - その他のスタイルシート
+ package.json - node設定(下記参照)
+ webpack.config.js - webpack設定(下記参照)
` node_modules/
ファイルの内容
package.jsonの内容
{
"scripts": {
"watch": "webpack --mode development --watch --color --progress",
"dev": "webpack --mode development",
"prod": "webpack --mode production --env.production",
"start": "webpack-dev-server --color --mode development",
"fix": "eslint --fix ./src"
},
"main": "dist/app.min.js",
"devDependencies": {
"@babel/cli": "^7.7.7",
"@babel/core": "^7.7.7",
"@babel/polyfill": "^7.7.0",
"@babel/preset-env": "^7.7.7",
"babel-loader": "^8.0.6",
"copy-webpack-plugin": "^5.1.1",
"core-js": "^3.6.1",
"css-loader": "^3.4.0",
"eslint": "^6.8.0",
"eslint-config-google": "^0.13.0",
"fs": "0.0.1-security",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"sass-loader": "^8.0.0",
"style-loader": "^1.1.2",
"terser-webpack-plugin": "^2.3.1",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1"
},
"private": true
}
Webpack.config.jsの内容
const webpack = require('webpack');
const fs = require('fs');
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const pjson = require('./package.json');
const build = new Date().toISOString();
module.exports = (env) => {
/** @type {bool} 開発モードかの判定 */
const isProduction = env && env.production;
/** @type {string} ヘッダーに出力するバナー情報。主にpackage.jsonの値を使用 */
const banner = `${pjson.name} v${pjson.version} | ${pjson.author.name} | license: ${pjson.license} | build: ${build}`;
return {
mode: env,
target: 'node',
devtool: !isProduction ? 'source-map' : false,
devServer: {
// ルート
contentBase: path.join(__dirname, 'docs'),
// 自動的にブラウザを開くか
open: true,
// ホスト
host: '0.0.0.0',
// ポート
port: 9000,
// 公開するサーバーのルート(/の場合localhost直下)
publicPath: '/',
// エラーをオーバーレイ表示する
disableHostCheck: true,
clientLogLevel: 'warning',
historyApiFallback: true,
hot: true,
inline: true,
},
stats: {
colors: true,
},
entry: {
// 出力先のファイル名とそのソースを指定
'app': 'src/js/app'.js
// ...
},
// 出力先設定
output: {
// 出力先のパス
path: path.resolve(__dirname, 'dist'),
// 出力先のファイル命名規則(productionモードでは[ファイル名].min.jsとなる)
filename: !isProduction ? '[name].js' : '[name].min.js',
// ライブラリ名
library: 'GracefulLibray',
libraryTarget: 'umd',
umdNamedDefine: true,
globalObject: `(typeof self !== 'undefined' ? self : this)`,
},
// 圧縮設定
optimization: {
minimize: isProduction,
minimizer: [
// JSはTerserを使用する
new TerserPlugin({
terserOptions: {
// 出力するECMAScipt(JavaScript)のバージョン。10(2019)まで指定可能だが対応しているブラウザがないので6(2015)にするのが無難。
ecma: 6,
compress: {
// errorとwarn以外のコンソールを削除する
drop_console: true
},
output: {
// コメント類を削除
comments: false,
// コードを見やすくする
beautify: false,
},
// ライセンス情報は、[name].LICENSEに出力される。
// ※/*! ... **/内のテキストのみ。
extractComments: {
condition: true,
banner: (f) => {
// バナー
return banner;
},
},
},
}),
// CSS部分はoptimize-css-assetsを使用する
new OptimizeCSSAssetsPlugin({
cssProcessorPluginOptions: {
preset: ['advanced',
{
autoprefixer: {
// autoprefixerによる vendor prefix の追加を行う
add: true,
// サポートするブラウザVersionの指定
browsers: ["last 2 versions", "ie >= 11", "Android >= 4"]
},
// ライセンスも含めて、コメントを全て削除する
discardComments: { removeAll: true },
// CSSの定義のソートを行う
cssDeclarationSorter : { order: 'smacss' }
}
],
},
canPrint: true
})
],
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
},
concatenateModules: false,
},
module: {
// ルール設定
rules: [
// JavaScriptの設定
{
test: /\.js/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
],
},
// SCSSの設定
{
test: /\.scss/,
use:
[
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: 'css-loader',
options: {
url: false,
sourceMap: !isProduction,
// 0 => no loaders (default);
// 1 => postcss-loader;
// 2 => postcss-loader, sass-loader
importLoaders: 2,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: !isProduction,
},
},
],
},
// TypeScriptの場合(別途ts-loaderとtypescriptをインストールする必要あり)
// {
// test: /\.ts$/,
// use: "ts-loader"
// }
],
},
// 名前解決
resolve: {
modules: [`${__dirname}/src`, 'node_modules'],
extensions: ['.js', '.scss'],
},
plugins: [
new webpack.BannerPlugin({
// バナーを出力
banner: banner,
}),
new MiniCssExtractPlugin({
// スタイルシートの出力ファイル名はここで指定する
filename: !isProduction ? 'style.css' : 'style.min.css',
}),
],
// パッケージに含めないライブラリ
// externals: {
// jquery: 'jQuery'
// },
};
使い方
まずは、プロジェクトディレクトリで、コマンドラインから以下のコマンドを実行し、パッケージをインストールしよう。
npm install
コーディングを終えたら、以下のコマンドでhttp://localhost:3000/にdistディレクトリがドキュメントルートのサーバーを起動する。この設定では、同一ネットワークにあるPCからもアクセスできる。例えばWifiでつながったiPhoneで同時チェックと言った使い方ができる。
npm run start
src内のJavaScriptやScssを更新したときに自動的に再コンパイルされリロードするスグレモノ。実行結果はメモリに保持されるので高速な反面、distディレクトリ内のjsやcssが自動更新されるわけではないので注意。
ESLintでコード整形を行う場合は、以下のコマンドを実行する。複数人で作業する場合、gitのpush時にhuskyなどで、このコマンドを自動実行するようにするといいかもしれない。
npm run fix
コンパイルして圧縮したjsやcssを出力したい場合は以下のコマンドを入力
npm run prod
単純に出力したい場合は、
npm run dev
実際コーディングする(bootstrap4を使った例)
例えば、みんなだいすきbootstrapを使う場合、
npm install bootstrap
で普通にbootstrapをインストールする。
次にapp.jsとapp.scssでbootstrapを呼び出す。
import $ from 'jquery';
// 全部使う場合はこれでいいがおすすめできない。
//import 'bootstrap';
// 使わないモジュールはコメントアウトしよう。
import 'bootstrap/js/dist/util';
import 'bootstrap/js/dist/alert'
import 'bootstrap/js/dist/button'
import 'bootstrap/js/dist/carousel'
import 'bootstrap/js/dist/collapse'
import 'bootstrap/js/dist/dropdown'
import 'bootstrap/js/dist/modal'
import 'bootstrap/js/dist/popover'
import 'bootstrap/js/dist/scrollspy'
import 'bootstrap/js/dist/tab'
import 'bootstrap/js/dist/tooltip'
//ここから下に自分のコードを入れる
別途_variables.scss
を作成して、bootstrapの設定をオーバーライドする値を入れる。例えば、全体フォントを游ゴシックにしたい場合
$font-family-base: -apple-system, BlinkMacSystemFont, "Yu Gothic Medium", "游ゴシック Medium", YuGothic, "游ゴシック体", "ヒラギノ角ゴ Pro W3", "メイリオ", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
と入れておこう。変数になっているところがポイント。この他変更できる値は、/node_modules/bootstrap/scss/_variables.scss
を見ること。これをapp.scssから読み込む。
ss:./src/scss/app.scss
@import "variables";
// ここも例によってモジュール単位で呼び出すことを推奨
//@import "~bootstrap/scss/bootstrap";
// 使わないモジュールはコメントアウトしよう。
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/root";
@import "~bootstrap/scss/reboot";
// ここから上は必須
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/images";
@import "~bootstrap/scss/code";
@import "~bootstrap/scss/grid";
@import "~bootstrap/scss/tables";
@import "~bootstrap/scss/forms";
@import "~bootstrap/scss/buttons";
@import "~bootstrap/scss/transitions";
@import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/button-group";
@import "~bootstrap/scss/input-group";
@import "~bootstrap/scss/custom-forms";
@import "~bootstrap/scss/nav";
@import "~bootstrap/scss/navbar";
@import "~bootstrap/scss/card";
@import "~bootstrap/scss/breadcrumb";
@import "~bootstrap/scss/pagination";
@import "~bootstrap/scss/badge";
@import "~bootstrap/scss/jumbotron";
@import "~bootstrap/scss/alert";
@import "~bootstrap/scss/progress";
@import "~bootstrap/scss/media";
@import "~bootstrap/scss/list-group";
@import "~bootstrap/scss/close";
@import "~bootstrap/scss/modal";
@import "~bootstrap/scss/tooltip";
@import "~bootstrap/scss/popover";
@import "~bootstrap/scss/carousel";
@import "~bootstrap/scss/utilities";
@import "~bootstrap/scss/print";
// ここから下に自分のコードを入れる。
// こんな感じでBootstrapに用意されている関数を使ってらくらくコーディングしよう。
.container
{
// この例では、ブラウザの横のサイズに応じてcontainerの最大サイズを変更している。
// 参考:https://getbootstrap.com/docs/4.4/layout/overview/#containers
@include media-breakpoint-down(sm) {
max-width: 700px;
}
@include media-breakpoint-up(xl) {
max-width: 1200px;
}
}
で、HTML。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" href="css/app.css" />
<title>Hello, world!</title>
</head>
<body>
<div class="container">
<h1>Hello, world!</h1>
</div>
<script src="js/app.js"></script>
</body>
</html>
すると、srcディレクトリ内構造はこうなっているはずである。
src
+ js/
| + app.js
` scss/
+ app.scss
` _valiables.scss
この状態で
npm run start
を実行すると、http://localhost:3000/にアクセスしたときこのHTMLが表示される。また、jsやscssを編集すると、ブラウザが自動的にリロードされその結果がすぐにわかる。
更新履歴
2020/01/02
- 情報が古すぎたので色々書き直し。JS圧縮プログラムのuglifyをTetherに変えたなど。
- 今回から更新履歴を書くことに