7
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Bootstrap 4 × Webpack 4 な開発環境をつくる方法

Last updated at Posted at 2019-09-23

概要

  • Bootstrap4 を適用したWebフロントエンドアプリをWebpack4な環境(+npm,babelなど)で開発する方法のメモです
  • 期待される効能
    • <script>タグや<link>タグをつかってBootstrapのたくさんある関連ファイル(jsやcssの類)を読み込まなくて済むのでサーバーへのリクエスト数を減らせる
    • ビルド過程でs(a)css→cssコンパイルも同時に行えるのでスタイル変更・確認がはかどる
    • webpack-dev-serverを使えばオートリロード機能でjsアプリ部分の開発もはかどる
  • 本稿で作ったサンプルはこちら
    (1つのJSファイルと1つのCSSファイルのみ構成にできる)
  • 全ソースコードはこちら

環境

  • ES6
  • Bootstrap 4.3.1
  • Webpack 4.40.2

必要なnpmパッケージのインストール

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は以下のような感じになる(抜粋)

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"
  }

(package.jsonのソースコード全文はこちら)

webpack.config.jsを作る

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.jsplugins: []以下に着目。
以下のようにするとjsコードの中から、指定した変数名でjqueryモジュールを参照できるようにする。
ここでは$
jQuerywindow.jQueryとしてjQueryを参照できるようになった。
これでjsコード内で**import $ from 'jquery'**などと、いちいち書く必要はなくなる。

 new webpack.ProvidePlugin({
     $: 'jquery',
     jQuery: 'jquery',
     'window.jQuery': 'jquery'
 }),

ポイント2: scssのコンパイルをして、cssファイルを生成する

webpack.config.jsmodule: {rules: [] 以下に着目。

MiniCssExtractPluginをつかって、cssを外部ファイルとして生成する。

Webpack3まで使われていたextract-text-webpack-plugindeprecatedなので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']**のように書くと

webpack.config.js
{
         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に書く。

package.json
  "browserslist": [
    "last 2 versions",
    "Android 4.0",
    "iOS 8.1"
  ],

(package.jsonのソースコード全文はこちら)

index.scss

*.scssファイルをsrcに以下のようなindex.scssとして置いた場合、

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";

(index.scssのソースコード全文はこちら)

ビルド時にcss/app.cssとして生成される。

その他のフロントエンド関連のwebpack設定は**こちら**にて説明している。

index.html

次に、Bootstrap4向けに以下のようなindex.htmlを用意した

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>

(index.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をインポートしている

index.js
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で作った画面が表示された。

image.png

たとえば、ここで、src/index.scssをいじって、以下のようにBootstrapのテーマカラーdarkを変更してみた。

$theme-colors: (
        dark: #999999
);

$theme-colors: (
        dark: #ff0000
);
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: #ff0000
);

//import boostrap's scss
@import "~bootstrap/scss/bootstrap.scss";

すると、上部のnav-barの背景色(スタイルをnavbar-darkにしているのでテーマカラーdarkに連動する)が即座に反映された↓

image.png

このようにBootstrapのスタイル適用や確認もwebpackな世界の恩恵をうけることができる。

まとめ

7
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?