jackmiwamiwa devblog

フロントエンドをメインに行ったことをまとめていくサイトになります。

webpackでjsのオプション・配置場所をカスタマイズする

既存だと出力時にbodyの下に

<script src="/assets/js/bundle-e57c2548e14548c673e0.js"></script>

headの下に

<link href="/assets/css/bundle-e57c2548e14548c673e0.css" rel="stylesheet">

のように出力されるので、出力される場所の変更・ハッシュ値の変更などを行う

既存の状態

  • 変更前のhtml
<!DOCTYPE html>
<html lang="ja">
  <head>
    <link href="/assets/css/bundle-a1e0d6d9e5b3b4d09222.css" rel="stylesheet">
  </head>
  <body>
      <header>ヘッダー</header>
      <p>テキスト</p>
      <footer>フッター</footer>
    <script src="/assets/js/bundle-a1e0d6d9e5b3b4d09222.js"></script>
  </body>
</html>
  • webpackの設定

最小限なので、一部を省略。

const { resolve, join } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

const buildDir = resolve(__dirname, 'dist')

module.exports = {
  entry: {
    bundle: './assets/js/common.js', // コンパイル前のjsファイルを指定
  },
  output: {
    path: buildDir,
    publicPath: '/',
    filename: join('assets/js', '[name]-[hash].js'), // js出力を行うファイル場所・ファイル名の指定
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html', // 出力するhtmlのファイルの場所・ファイル名
      template: 'pug/index.pug', // pugのファイル箇所を記載
    }),
    new MiniCssExtractPlugin({
      filename: join('assets/css', '[name]-[hash].css'),
      ignoreOrder: true,
    }),
  ],
}

css, jsを出力されないように変更

injectの設定を行う

www.npmjs.com

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      inject: false, // ここにinjectの設定を行う
      filename: 'index.html', // 出力するhtmlのファイルの場所・ファイル名
      template: 'pug/index.pug', // pugのファイル箇所を記載
    }),
  ],
}

injectをfalseに設定

<!DOCTYPE html>
<html lang="ja">
  <head></head>
  <body>
    <header>ヘッダー</header>
    <p>テキスト</p>
    <footer>フッター</footer>
  </body>
</html>

injectをheadに設定

<!DOCTYPE html>
<html lang="ja">
  <head>
    <link href="/assets/css/bundle-a1e0d6d9e5b3b4d09222.css" rel="stylesheet">
    <script src="/assets/js/bundle-a1e0d6d9e5b3b4d09222.js"></script>
  </head>
  <body>
    <header>ヘッダー</header>
    <p>テキスト</p>
    <footer>フッター</footer>
  </body>
</html>

injectをbodyに設定

<!DOCTYPE html>
<html lang="ja">
  <head>
    <link href="/assets/css/bundle-a1e0d6d9e5b3b4d09222.css" rel="stylesheet">
  </head>
  <body>
    <header>ヘッダー</header>
    <p>テキスト</p>
    <footer>フッター</footer>
    <script src="/assets/js/bundle-a1e0d6d9e5b3b4d09222.js"></script>
  </body>
</html>

jsのファイルにdeferを追加したい時

scriptLoadingのオプションを「defer」に行うことでjsにdeferを追加することができる

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      scriptLoading: 'defer', // deferの追記を行う
      filename: 'index.html', // 出力するhtmlのファイルの場所・ファイル名
      template: 'pug/index.pug', // pugのファイル箇所を記載
    }),
  ],
}
<!DOCTYPE html>
<html lang="ja">
  <head>
    <script defer src="/assets/js/bundle-a1e0d6d9e5b3b4d09222.js"></script>
    <link href="/assets/css/bundle-a1e0d6d9e5b3b4d09222.css" rel="stylesheet">
  </head>
  <body>
    <header>ヘッダー</header>
    <p>テキスト</p>
    <footer>フッター</footer>
  </body>
</html>

jsのdeferの設定を個別に分けたい場合

「a.js」にはdeferをつけて、「b.js」にはつけないなどの設定を行いたいときに、html-webpack-injectorを使用。

github.com

インストール

$ npm i -D html-webpack-injector

or

$ yarn add -D html-webpack-injector

使い方

const { resolve, join } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HtmlWebpackInjector = require('html-webpack-injector') // html-webpack-injector追加

const buildDir = resolve(__dirname, 'dist')

module.exports = {
  entry: {
    bundle_head: './assets/js/common.js', // コンパイル前のjsファイルを指定(bundle_head追加)
    sample: './assets/js/common.js', // 検証用に追加
  },
  output: {
    path: buildDir,
    publicPath: '/',
    filename: join('assets/js', '[name].js'), // [hash]削除
  },
  plugins: [
    new HtmlWebpackPlugin({
      chunksConfig: {
        defer: ['bundle_head'], // deferをつけるものの名前を変更
      },
      filename: 'index.html',
      template: 'pug/index.pug',
    }),
    new HtmlWebpackInjector(), // html-webpack-injector追加
  ],
}
  • 出力結果
<!DOCTYPE html>
<html lang="ja">
  <head>
    <script defer src="/assets/js/bundle_head.js"></script>
    <link href="/assets/css/bundle_head-73f6ef1c604bc2d9aa0f.css" rel="stylesheet">
  </head>
  <body>
    <header>ヘッダー</header>
    <p>テキスト</p>
    <footer>フッター</footer>
    <script src="/assets/js/sample.js"></script>
  </body>
</html>

html-webpack-injectorを少しだけ調整

現状だと

  • jsのファイルにハッシュ値をつけるとdeferの対応ができない
  • ファイル名_headにしなければ内に配置されない

ため、少し変更を行う。

対応方法

1. https://github.com/architgarg/html-webpack-injector/blob/master/index.jsのファイルを持ってくる

github.com

2. 正規表現の内容を変更

筆者の正規表現スキルがあまりないので、あんまり綺麗ではないですが、、、

- const regex = new RegExp(`(\/${name}\-\+\.)|(${name}\-\\.)`) // 既存
+ const regex = /bundle\-[a-zA-Z0-9]/ // bundle-[hash]のみに反映をする

3. _headを記載しなくてもの中に配置されるようにする

以下のような変更の方法だと全てのjsファイルがに配置されてしまうので、注意。

- if ((chunk.attributes.src && chunk.attributes.src.includes("_head")) || chunk.attributes.href)
+ if (chunk.attributes.src || chunk.attributes.href)

変更後のコード

webpack

const { resolve, join } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HtmlWebpackInjector = require('./webpack.htmlinjector') // 上記の設定したファイルを格納したもの(webpack.htmlinjector.js)を読み込む

const buildDir = resolve(__dirname, 'dist')

module.exports = {
  entry: {
    bundle: './assets/js/common.js',
    sample: './assets/js/common.js',
  },
  output: {
    path: buildDir,
    publicPath: '/',
    filename: join('assets/js', '[name].js'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      chunksConfig: {
        defer: ['bundle', 'sample'], // 出力を行うjsファイルを記載
      },
      filename: 'index.html',
      template: 'pug/index.pug',
    }),
    new HtmlWebpackInjector(),
  ],
}

出力後のhtml

<!DOCTYPE html>
<html lang="ja">
  <head>
    <script defer src="/assets/js/bundle-4ed47f9b6282c1bead75.js"></script>
    <script src="/assets/js/sample-4ed47f9b6282c1bead75.js"></script>
    <link href="/assets/css/bundle-4ed47f9b6282c1bead75.css" rel="stylesheet">
  </head>
  <body>
    <header>ヘッダー</header>
    <p>テキスト</p>
    <footer>フッター</footer>
  </body>
</html>