概要
- Bootstrap4 を適用したWebフロントエンドアプリをWebpack4な環境(+npm,babelなど)で開発する方法のメモです
- 期待される効能
- <script>タグや<link>タグをつかってBootstrapのたくさんある関連ファイル(jsやcssの類)を読み込まなくて済むのでサーバーへのリクエスト数を減らせる
- ビルド過程でs(a)css→cssコンパイルも同時に行えるのでスタイル変更・確認がはかどる
- webpack-dev-serverを使えばオートリロード機能でjsアプリ部分の開発もはかどる
- 本稿で作ったサンプルはこちら
(1つのJSファイルと1つのCSSファイルのみ構成にできる) - 全ソースコードはこちら
-
https://github.com/riversun/npm_webpack4_bootstrap4
(cloneして npm install → npm start でサンプルが表示されます)
-
https://github.com/riversun/npm_webpack4_bootstrap4
環境
- ES6
- Bootstrap 4.3.1
- Webpack 4.40.2
必要なnpmパッケージのインストール
- babel,webpackといったフロントエンド開発用パッケージを入れる
npm install --save-dev @babel/core @babel/preset-env babel-loader core-js css-loader style-loader url-loader webpack webpack-cli webpack-dev-server
- scssビルドなどcss生成のためのパッケージを入れる
npm install --save-dev mini-css-extract-plugin node-sass sass-loader postcss-loader autoprefixer
- bootstrap 関連のパッケージを入れる
npm install bootstrap jquery popper.js
これらのパッケージをいれると、package.jsonは以下のような感じになる(抜粋)
"devDependencies": {
"@babel/core": "^7.6.0",
"@babel/preset-env": "^7.6.0",
"autoprefixer": "^9.6.1",
"babel-loader": "^8.0.6",
"core-js": "^3.2.1",
"css-loader": "^3.2.0",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.12.0",
"postcss-loader": "^3.0.0",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"url-loader": "^2.1.0",
"webpack": "^4.40.2",
"webpack-cli": "^3.3.9",
"webpack-dev-server": "^3.8.1"
},
"dependencies": {
"bootstrap": "^4.3.1",
"jquery": "^3.4.1",
"popper.js": "^1.15.0"
}
webpack.config.jsを作る
const packageJson = require('./package.json');
const version = packageJson.version;
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const AutoPrefixer = require('autoprefixer');
const TerserPlugin = require('terser-webpack-plugin');
const webpack = require('webpack');
module.exports = (env, argv) => {
・・・略・・・,
module: {
・・・略・・・,
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development',
},
},
{
loader: 'css-loader',
},
{
loader: 'postcss-loader',
options: {
plugins: [
AutoPrefixer(
{
grid: 'autoplace'
},
),
],
},
},
{
loader: 'sass-loader',
}
],
},
],
},
resolve: {
alias: {}
},
plugins: [
new webpack.BannerPlugin(`[name] v${version} Copyright (c) 2019 Your Name`),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
new MiniCssExtractPlugin({
filename: argv.mode === 'production' ? `css/[name].css` : `css/[name].css`, //`[name].min.js`
}),
],
};
・・・略・・・
(webpack.config.jsのソースコード全文はこちら)
以下、重要なポイントをみていく
ポイント1: jQueryの変数名参照を有効にする
webpack.config.jsのplugins: []以下に着目。
以下のようにするとjsコードの中から、指定した変数名でjqueryモジュールを参照できるようにする。
ここでは$、jQuery、window.jQueryとしてjQueryを参照できるようになった。
これでjsコード内で**import $ from 'jquery'**などと、いちいち書く必要はなくなる。
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
ポイント2: scssのコンパイルをして、cssファイルを生成する
webpack.config.jsのmodule: {rules: [] 以下に着目。
MiniCssExtractPluginをつかって、cssを外部ファイルとして生成する。
Webpack3まで使われていたextract-text-webpack-pluginはdeprecatedなのでWebpack4では**MiniCssExtractPlugin**を使う。
また、Autoprefixerは、CSSのベンダープレフィックス(-webkit-など)を自動的に追加してくれるPostCSSのプラグイン。IE向けのグリッド対応をするときに昔はgrid:trueと書いていたが、今ではこれはダメで、最新版ではgrid:autoplaceになっているので注意。
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: process.env.NODE_ENV === 'development',
},
},
{
loader: 'css-loader',
},
{
loader: 'postcss-loader',
options: {
plugins: [
AutoPrefixer(
{
grid: 'autoplace'
},
),
],
},
},
{
loader: 'sass-loader',
}
],
}
また、
webpack.config.jsの**plugins: []**以下に
new MiniCssExtractPlugin({
filename: argv.mode === 'production' ? `css/[name].css` : `css/[name].css`,
}),
のようにする。
注:AutoprefixerのBrowserlistの書き方
Autoprefixerでは、ブラウザをどこまで対応させるかを指定する機能がある。
以下のようにwebpack.config.js内に**browsers: ['last 2 versions']**のように書くと
{
loader: 'postcss-loader',
options: {
plugins: [
AutoPrefixer(
{
grid: 'autoplace',
browsers: ['last 2 versions']
},
),
],
},
},
↓のような警告メッセージが出る
Replace Autoprefixer browsers option to Browserslist config.
Use browserslist key in package.json or .browserslistrc file.
Using browsers option cause some error. Browserslist config
can be used for Babel, Autoprefixer, postcss-normalize and other tools.
If you really need to use option, rename it to overrideBrowserslist.
Learn more at:
https://github.com/browserslist/browserslist#readme
https://twitter.com/browserslist
そこで、Browserlistはwebpack.config.jsではなく、package.jsonに書く。
"browserslist": [
"last 2 versions",
"Android 4.0",
"iOS 8.1"
],
index.scss
*.scssファイルをsrcに以下のようなindex.scssとして置いた場合、
// padding for fixed nav-bar
body {
padding-top: 70px;
}
// padding for tab content
.tab-content {
background-color: transparent;
padding-top: 10px;
}
//customize bootstrap theme
$theme-colors: (
dark: #999999
);
//import boostrap's scss
@import "~bootstrap/scss/bootstrap.scss";
ビルド時にcss/app.cssとして生成される。
その他のフロントエンド関連のwebpack設定は**こちら**にて説明している。
index.html
次に、Bootstrap4向けに以下のようなindex.htmlを用意した
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<header>
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
<a class="navbar-brand" href="#">Bootstrap with Webpack4</a>
</nav>
</header>
<div class="container-fluid">
<div class="row">
<div class="p-1 col-sm-12">
<button id="bt1" type="button" class="btn btn-primary">Primary</button>
<button id="bt2" type="button" class="btn btn-secondary">Secondary</button>
<button id="bt3" type="button" class="btn btn-success">Success</button>
<button id="bt4" type="button" class="btn btn-danger">Danger</button>
<button id="bt5" type="button" class="btn btn-warning">Warning</button>
<button id="bt6" type="button" class="btn btn-info">Info</button>
<button id="bt7" type="button" class="btn btn-light">Light</button>
<button id="bt8" type="button" class="btn btn-dark">Dark</button>
<button id="bt9" type="button" class="btn btn-link">Link</button>
</div>
</div>
<div class="row">
<div class="p-1 col-sm-12">
<ul class="list-group">
<a href="#" class="list-group-item list-group-item-action">Link1</a>
<a href="#" class="list-group-item list-group-item-action">Link2</a>
<a href="#" class="list-group-item list-group-item-action">Link3</a>
<a href="#" class="list-group-item list-group-item-action">Link4</a>
</ul>
</div>
</div>
<div class="row">
<div class="p-1 col-sm-12">
<button id="flip-tab" type="button" class="btn btn-primary col-sm-12">Flip tab</button>
</div>
</div>
<div class="row">
<div class="p-1 col-sm-12">
<!-- Tab buttons -->
<ul class="nav nav-tabs">
<li class="nav-item">
<a href="#tab-no1" id="tab1" class="nav-link active" data-toggle="tab">Tab1</a>
</li>
<li class="nav-item">
<a href="#tab-no2" id="tab2" class="nav-link" data-toggle="tab">Tab2</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div id="tab-no1" class="tab-pane active">
<div class="row">
<div class="col-12 col-sm-6">
content-1-1
</div>
<div class="col-12 col-sm-6">
content-1-2
</div>
</div>
</div>
<div id="tab-no2" class="tab-pane">
<div class="row">
<div class="col-6 col-sm-6">
content-2-1
</div>
<div class="col-6 col-sm-6">
content-2-2
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="js/app.js"></script>
</body>
</html>
webpack.config.jsで、1つのjsファイルと1つのcssファイルを生成するように設定したので
jsファイルの読み込みは以下のように1つでOKだし、
<script src="js/app.js"></script>
cssファイルの読み込みも以下のように1つでOK
<link rel="stylesheet" href="css/app.css">
(ちなみに、cssファイルもjsにバンドルできるが、本稿ではcssは外だしというスタイルとしているのはMiniCssExtractPluginの項で説明したとおり)
index.js
以下のようなindex.jsを作成した
Bootstrapを**import 'bootstrap';**でインポートする
また、import './index.scss';でscssをインポートしている
import 'bootstrap';
import './index.scss';
$('button[id^=bt]').on('click', e => {
alert(`${$(e.target).attr('id')} "${$(e.target).text()}" clicked`);
});
$('.list-group-item').on('click', e => {
alert(`"${$(e.target).text()}" clicked`);
});
$('#flip-tab').on('click', e => {
$('.nav-tabs :not(.active)').tab('show')
});
$('a[data-toggle="tab"]').on('click', e => {
alert(`${$(e.target).attr('id')} clicked`);
});
あとは、Bootstrapスタイル(jQueryで操作する)でコードを書く。
$でjQueryが参照できているのは、さきほどwebpack.config.jsで以下を設定したため。
webpack.config.js(抜粋)new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery' }),
ここまでのファイルは以下のようなディレクトリ構成で配置している
[workdir]
├── node_modules
├── public
│ └── index.html
├── src
│ ├── index.js
│ └── index.scss
├── package.json
├── package-lock.json
└── webpack.config.js
ビルドしてwebpack-dev-serverで確認
実際にwebpack-dev-serverで確認してみる
npm start
すると、ブラウザが起動し、以下のようにBootstrapで作った画面が表示された。
たとえば、ここで、src/index.scssをいじって、以下のようにBootstrapのテーマカラーdarkを変更してみた。
$theme-colors: (
dark: #999999
);
↓
$theme-colors: (
dark: #ff0000
);
// padding for fixed nav-bar
body {
padding-top: 70px;
}
// padding for tab content
.tab-content {
background-color: transparent;
padding-top: 10px;
}
//customize bootstrap theme
$theme-colors: (
dark: #ff0000
);
//import boostrap's scss
@import "~bootstrap/scss/bootstrap.scss";
すると、上部のnav-barの背景色(スタイルをnavbar-darkにしているのでテーマカラーdarkに連動する)が即座に反映された↓
このようにBootstrapのスタイル適用や確認もwebpackな世界の恩恵をうけることができる。
まとめ
- Bootstrap4を用いた開発をnpmやWebpackな環境で便利に実施する方法について説明しました
- ソースコードは https://github.com/riversun/npm_webpack4_bootstrap4 にあります