gulp4 + webpack4でつくるフロント開発環境

今回はフロントエンドの開発環境についてのメモ。
gulpwebpackなどの環境設定について。

まだgulp?

gulpってもう古くない?まだ使ってんの?
ってツッコミがありそうですが、僕の開発環境ではまだまだ現役です!

gulp.jsから卒業!や、もういらない!など、検索するといろいろな記事が出てきますが、個人的にはJSとCSSさえビルドできればなんでも一緒です。

そうなるとますますgulpじゃなくてもええやん。ってなりそうですが、
昔から特にgulpで消耗したこともないですし、他に変えるコストを考えるとバージョン上げて使い続ける方が楽だったので使い続けています。

とは言え、gulpを使っていてもフロントの開発環境はすぐに新しくなって変わっていくので、古くなったサイトの環境が今と合わなくてよく開発環境の立ち上げ方や使い方を忘れたりします。

そんなときのために今の開発環境を備忘録として残しておこうと思いました。

開発環境の構成


【2020.07.28】node v14.5.0で動かしています。


現状つかっている構成はこんな感じ.

gulpfile.babel.js
├── /tasks
     ├── browsersync.js
     ├── css.js
     ├── html.js
     ├── img.js
     ├── js.js
     ├── svg.js
     └── watch.js
├── config.js
├── index.js
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js

.babelrc
package.json

では詳しくみていきます。

package.json

こちらは扱うモジュールの依存関係を記載しています。
yarnnpmでインストールするとdevDependenciesに記載のあるパッケージがnode_moduleにインストールされます。

{
    //・・・基本設定部分省略

    "scripts": {
        "dev": "NODE_ENV=development gulp",
        "production": "NODE_ENV=production gulp",
        "build": "NODE_ENV=production gulp build"
    },
    "devDependencies": {
        "@babel/core": "^7.7.0",
        "@babel/plugin-proposal-optional-chaining": "^7.10.4",
        "@babel/plugin-transform-runtime": "^7.6.2",
        "@babel/polyfill": "^7.7.0",
        "@babel/preset-env": "^7.7.1",
        "@babel/preset-react": "^7.7.0",
        "@babel/register": "^7.7.0",
        "@babel/runtime": "^7.7.1",
        "@barba/core": "^2.7.2",
        "@barba/css": "^2.1.12",
        "@barba/prefetch": "^2.1.8",
        "autoprefixer": "^9.7.1",
        "babel-loader": "^8.0.6",
        "browser-sync": "^2.26.7",
        "css-mqpacker": "^7.0.0",
        "del": "^5.1.0",
        "exports-loader": "^1.1.0",
        "gulp": "^4.0.2",
        "gulp-base64": "^0.1.3",
        "gulp-cached": "^1.1.1",
        "gulp-csso": "^4.0.1",
        "gulp-diff-build": "^1.0.2",
        "gulp-if": "^3.0.0",
        "gulp-imagemin": "^7.1.0",
        "gulp-notify": "^3.0.0",
        "gulp-plumber": "^1.1.0",
        "gulp-postcss": "^8.0.0",
        "gulp-progeny": "^0.4.1",
        "gulp-rename": "^2.0.0",
        "gulp-sass": "^4.0.1",
        "gulp-sass-glob": "^1.0.9",
        "gulp-svgmin": "^3.0.0",
        "gulp-webp": "^4.0.1",
        "gulp.spritesmith": "^6.11.0",
        "imagemin-jpegtran": "^7.0.0",
        "imagemin-mozjpeg": "^9.0.0",
        "imagemin-pngquant": "^9.0.0",
        "imports-loader": "^1.1.0",
        "intersection-observer": "^0.11.0",
        "jquery": "^3.4.1",
        "jquery.easing": "^1.4.1",
        "lazysizes": "^5.1.2",
        "lodash": "^4.17.4",
        "node-sass": "^4.13.0",
        "object-fit-images": "^3.2.3",
        "picturefill": "^3.0.2",
        "pngquant": "^3.0.0",
        "postcss-assets": "^5.0.0",
        "postcss-cssnext": "^3.0.2",
        "postcss-flexbugs-fixes": "^4.1.0",
        "postcss-import": "^12.0.1",
        "postcss-loader": "^3.0.0",
        "terser-webpack-plugin": "^3.0.7",
        "through2": "^4.0.2",
        "uglifyjs-webpack-plugin": "^2.0.0",
        "vinyl": "^2.1.0",
        "vinyl-named": "^1.1.0",
        "vinyl-source-stream": "^2.0.0",
        "webpack": "^4.41.2",
        "webpack-encoding-plugin": "^0.3.1",
        "webpack-merge": "^5.0.9",
        "webpack-stream": "^5.1.1"
    },
    "dependencies": {
        "core-js": "3.6.5",
        "css-loader": "^4.0.0",
        "regenerator-runtime": "^0.13.3",
        "sass-loader": "^9.0.2"
    }
}

今回紹介するCSSとJSのビルドに必要なものだけをピックアップしました。
それでも多いですよね・・・まぁ多くても少なくても個人的にはあまり関係ないっちゃないのですが、依存しているプラグインが多いと、そのうちのどれかのプラグインの開発が終わってしまったときに問題が起こるかもしれません。

.babelrc

ES6で書きたいので.babelrc にbabelの設定を書いて gulpfile.jsgulpfile.babel.jsにしています。

.babelrcの中身はこちら

{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "node": "current",
        "browsers": ["last 2 versions"]
      },
      "useBuiltIns": "entry",
      "corejs": 3
    }]
  ],
  "plugins": ["@babel/plugin-proposal-optional-chaining"]
}

polyfillの適用など基本的なbabelの設定です。
こちらの設定内容については今回は省略します。

gulpfile.babel.jsをファイル分割

gulpfile.babel.jsはファイルではなくディレクトリにして直下にindex.jsを配置します。これは数年前にどこかで見た記事を参考にしたのですが、よく調べると公式ドキュメントに書いてありました。
この方法を使うとgulp実行する際にgulpfile.babel.js/index.jsを読み込んでくれるので、タスクごとにファイル分割して管理しやすくなります。

こちらがそのindex.jsの中身です。

import gulp from 'gulp';
import { watcher } from './tasks/watch';
import { deleteCssDir, buildCss } from './tasks/css'
import { deleteJsDistDir, buildJs, buildJsAll } from './tasks/js'
import { browsersync } from './tasks/browsersync'

//Default
exports.default = gulp.series(watcher, browsersync);

//build CSS&JS
exports.build = gulp.series(deleteCssDir, deleteJsDistDir, buildCss, buildJsAll);

タスクはgulpfile.babel.js/tasks以下にそれぞれの役割ごとにファイル分割して設置。require-dirモジュールでディレクトリ内の全てのファイルを読み込みます。gulp.task()の書き方が非推奨になっていたのでrequire-dirは使用をやめ、importで必要なタスクのみを読み込むようにしました。

タスクを読み込んだらdefaultタスクを書いておきます。ここではwatcherbrowsersyncというタスクを直列でgulp起動時に実行するようにしています。直列というのはwatcherbrowsersyncという順番に実行するということです。

設定ファイル(config.js)について

gulpfile.babel.js内にあるconfig.jsについて。

gulpfile.babel.js

├── /tasks
     ├── browsersync.js
     ├── css.js
     ├── html.js
     ├── img.js
     ├── js.js
     ├── svg.js
     └── watch.js
├── config.js <= こちらのファイル
├── index.js
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js

.babelrc
package.json

こちらには主にフォルダやファイルのパスや、glob(ワイルドカードでファイル名のセットを指定するパターン)を指定したものなどをまとめているファイルです。

中身はこんな感じです。

import path from 'path'
//各パス設定
export const paths = {
  serverDir       : 'notes.sharesl.net',
  themeDir        : path.join(__dirname, '../'),
  imageDir        : path.join(__dirname, '../assets/images'),
  imageminDir     : path.join(__dirname, '../assets/imagemin'),
  spriteDir       : path.join(__dirname, '../assets/sprite'),
  spriteminDir    : path.join(__dirname, '../assets/spritemin'),
  jsSrcDir        : path.join(__dirname, '../assets/js'),
  jsDistDir       : path.join(__dirname, '../assets/dist'),
  sassDir         : path.join(__dirname, '../assets/sass'),
  cssDir          : path.join(__dirname, '../assets/css'),
  svgDir          : path.join(__dirname, '../assets/svg'),
  svgminDir       : path.join(__dirname, '../assets/svg'),
  jsEntryFileName : 'entry.js'
}

//ファイルマッチパターン
export const globs = {
  html    : `${paths.themeDir}**/*.(html|php)`,
  svg     : `${paths.svgDir}/**/*.svg`,
  img     : `${paths.imageDir}/**/*.+(jpg|jpeg|png|gif|svg)`,
  sprite  : `${paths.spriteDir}/*.+(jpg|jpeg|png)`,
  sprites : `${paths.spriteDir}/**/*.+(jpg|jpeg|png)`,
  sass    : `${paths.sassDir}/**/*.scss`,
  js      : `${paths.jsSrcDir}/**/*.+(js|vue)`,
  entry   : `${paths.jsSrcDir}/**/entry.js`
}

//CSS対応ブラウザのバージョン
export const browsers = [
'> 5% in JP',
'last 2 versions',
'ie >= 11',
'Android >= 4',
'iOS >= 8'
]

const config = { paths, globs, browsers }
export default config

これを作っておけば、新しいプロジェクトを作るときにgulpfile.babel.jsをコピーしてきて、こちらのファイルをちょいちょいっと書き換えるだけですぐに環境が整います。globspathsは自由に追加・変更できますので新しいgulpタスクを作った場合にもすぐ対応できます。

gulpタスクの詳細

次にタスクの中身です。
自動でブラウザリロードできるBrowsersyncや画像・svgの圧縮
などタスクを切り分けていろいろ作っていますが、
今回はCSSとJSのみをメモしておきます。
また機会があれば画像圧縮のタスクは使えるので紹介したいと思います。

① CSSビルド(gulpfile.babel.js/tasks/css.js)

import gulp                     from 'gulp'
import sass                     from 'gulp-sass'
import nodesass                 from 'node-sass'
sass.compiler = nodesass;
//エラーでgulpが終了するのを止める
import plumber                  from 'gulp-plumber'
//デスクトップ通知
import notify                   from 'gulp-notify'
//小さい画像をbase64に変換
import base64                   from 'gulp-base64'
//PostCss
import postcss                  from 'gulp-postcss'
import cssnext                  from 'postcss-cssnext'
import cssImport                from 'postcss-import'
//flexboxのバグを自動修正
import flexBugsFixes            from 'postcss-flexbugs-fixes'
//ファイル名から画像パスやサイズを取得
import assets                   from 'postcss-assets'
//メディアクエリを整理する
import mqpacker                 from 'css-mqpacker'
//cssを圧縮する
import csso                     from 'gulp-csso'
//config
import {paths, globs, browsers} from '../config'
//cache
import diff                     from 'gulp-diff-build'
import cache                    from 'gulp-cached'
import progeny                  from 'gulp-progeny'
import browserSync              from 'browser-sync'
//@importのglobを有効にする
import sassGlob                 from 'gulp-sass-glob'
//ファイル削除
import del                      from 'del'

//出力済みファイルを削除
function deleteCssDir(done) {
  return del([paths.cssDir], done);
}
exports.deleteCssDir = deleteCssDir;

const processors = [
assets({
  baseUrl     : `${paths.serverDir}/`,
  basePath    : paths.themeDir,
  loadPaths   : [
  'assets/images/',
  'assets/svg/',
  ],
  relative    : true,
  cachebuster : true,
}),
cssnext({
  browsers,
  features : {
    autoprefixer : {
      grid: true
    }
  }
}),
mqpacker({
  sort: true
}),
flexBugsFixes,
cssImport({
  path: [ 'node_modules' ]
})
];

function buildCss() {
  return gulp.src(globs.sass, {
    allowEmpty : true,
    sourcemaps : true
  })
  .pipe(plumber({
    errorHandler: notify.onError('<%= error.message %>')
  }))
  .pipe(sassGlob())
  .pipe(diff())
  .pipe(cache('sass'))
  .pipe(progeny())
  .pipe(sass({
    outputStyle: 'expanded',
  }))
  .pipe(base64({
    baseDir      : paths.imageDir,
    extensions   : ['svg', 'png', /\.jpg#datauri$/i],
    exclude      : [/\.server\.(com|net)\/dynamic\//, '--live.jpg'],
    maxImageSize : 8 * 1024,
    debug        : true
  }))
  .pipe(postcss(processors))
  .pipe(csso({
    restructure : false,
    sourceMap   : false,
    debug       : true
  }))
  .pipe(gulp.dest(paths.cssDir, {
    sourcemaps : '.'
  }))
  .pipe(notify('buildCss finished'))
  .pipe(browserSync.reload({
    stream: true
  }));
}
exports.buildCss = buildCss;

2020.07.28非推奨になったgulp.task()のままになっていたのでタスクを新しい書き方に更新しました。


長年ちまちま更新してきたタスクなのでちょっと長いですね・・・笑
人によっては必要ない機能もあるかもしれませんが、30ファイルほどのSassをコンパイルしても1回目で0.6秒程度、2回目はキャッシュで早くなるので0.3秒程度となり、まぁ許容範囲だと思います。

モジュールに関しては結構たくさんあるのでさすがにひとつずつは説明できませんので、これは必須!というものだけピックアップしておきます。

gulp-postcss

こちらは生成するCSSを最適化・カスタマイズできるPostCSSが使えるプラグイン。
色々と便利なオプションが設定できますが、有名どころではcssnextAutoprefixerでしょうか。
僕はcss-mqpackerというメディアクエリを整理するプラグインを使いたくて使い始めたのですが、いろんなカスタマイズができることを知り手放せなくなりました。

gulp-cached + gulp-progeny

こちらはセットで使うのが普通かなと思いますのでまとめてご紹介。
gulp-cachedは文字通りキャッシュしてくれるので差分のみがビルドされます。
gulp-progenyはSassで@importなどを使用している場合にその依存関係を解決してコンパイルしてくれます。


詳しくはこちらの記事をどうぞ。

gulpでCSSの差分ビルド

https://qiita.com/73cha/items/270e2dc33c63292dd184
gulp-csso

こちらはCSSの圧縮プラグイン。
ファイルの軽量化には必須ですね。

他にも便利なプラグインを使っていますが、なくてもなんとかなるものばかりなので割愛させていただきます。プラグインの名前で検索したらすぐ出てくるものばかりです。

② JSビルド(gulpfile.babel.js/tasks/js.js)

import gulp            from 'gulp';
import {globs, paths}  from '../config';
//エラーでgulpが終了するのを止める
import plumber         from 'gulp-plumber';
//デスクトップ通知
import notify          from 'gulp-notify';
import path            from 'path';
import fs              from 'fs';
import through         from 'through2';
import vinyl           from 'vinyl';
import diff            from 'gulp-diff-build';
//import cache           from 'gulp-cached';
//webpackでファイル結合時に名前変更
import named           from 'vinyl-named';
import gulpif          from 'gulp-if';
import webpack         from 'webpack';
import webpackStream   from 'webpack-stream';
import webpackConfig   from '../webpack.config';
import browserSync     from 'browser-sync';
//ファイル削除
import del                      from 'del'

//出力済みファイルを削除
function deleteJsDistDir(done) {
  return del([paths.jsDistDir], done);
}
exports.deleteJsDistDir = deleteJsDistDir;

//処理中のファイル名を入れる
let proccessings = [];

//jsのエントリーポイントファイルかどうか
const isEntryFile = (file) => {
  let isEf = true;
  if(!isExistFile(file.path)){
    isEf = false;
  }
  else {
    isEf = path.basename(file.path) === paths.jsEntryFileName;
  }
  return isEf;
}

//ファイルの存在チェック
const isExistFile = (file) => {
  try {
    fs.statSync(file);
    return true
  } catch(err) {
    if(err.code === 'ENOENT') return false
  }
}

//バンドル実行中のファイルかどうか
const isProccessing = (filePath) => {
  return (proccessings.indexOf(filePath) >= 0);
}

//bundleされたファイルのみを次の処理に通す
const passThroughBundled = () => {
  proccessings = [];
  return through.obj(function (file, enc, cb) {
    if( file.isNull() ){
      cb(null,file);
    }
    else {
      const basename = path.basename(file.path);
      const isBundled = basename.indexOf('bundle.js') !== -1;
      if(isBundled){
        this.push(file);
      }
      cb();
    }
  });
}

function buildJs() {
  return gulp.src(globs.js, {
    allowEmpty : true
  })
  .pipe(plumber({
    errorHandler: notify.onError('<%= error.message %>')
  }))
  .pipe(diff())
  .pipe(cache('js'))
  .pipe(named((file) => {
    return file.relative ? path.parse(file.relative).dir : 'code';
  }))
  .pipe(gulpif(isEntryFile, webpackStream(webpackConfig, webpack)))
  .pipe(passThroughBundled())
  .pipe(gulp.dest(paths.jsDistDir))
  .pipe(notify('buildJs finished'))
  .pipe(browserSync.reload({
    stream: true
  }));
}
exports.buildJs = buildJs;

function buildJsAll() {
  return gulp.src(globs.entry, {
    allowEmpty: true
  })
  .pipe(plumber({
    errorHandler: notify.onError('<%= error.message %>')
  }))
  .pipe(named((file) => {
    return file.relative ? path.parse(file.relative).dir : 'code';
  }))
  .pipe(gulpif(isEntryFile, webpackStream(webpackConfig, webpack)))
  .pipe(gulp.dest(paths.jsDistDir))
  .pipe(notify('build:js-all finished'))
  .pipe(browserSync.reload({
    stream: true
  }));
}
exports.buildJsAll = buildJsAll;

こちらは基本的にはJSそのものは何も処理しません。することと言えば保存した時に差分のあった場合だけをwebpackに流したり、globで指定するentryファイルの存在チェックをしたりというフィルター処理でgulpの無駄な実行時間を減らす処理を書いています。ファイル結合や圧縮などはすべてwebpackに任せて処理を分けています。

ただし、タスクだけはbuildJsbuildJsAllに分けています。
本番モードでgulp-diff-buildなどの差分ビルドを通さずにすべてのJSを確実にバンドルしたい時はbuildJsAll、開発モードで更新したファイルだけをwebpackに通したい時はbuildJs、という使い方をしています。開発の時はいちいち全部のファイルを通されても時間かかって困るので極力タスクの実行時間は短くしておきたいのでこうしています。

ここでもいくつかモジュールを紹介しておきます。

gulp-diff-build

こちらはファイルを監視して変更があった場合のみ、ファイルをストリームに流します。つまり変更がある場合のみgulpタスクを実行するのでwatchした時に保存するたびに連続でタスクが実行されてしまうのを防止します。
これはかなり重宝しています!快適!

gulp-if

gulpのタスク処理で条件分岐したい場合にこちらを使います。
webpackにストリームを流す際に余計なファイルが含まれていないかチェックするのに使っています。

through2

これはgulpに独自の処理を入れたい時に使います。
プラグインの組み合わせだけではどうにもならないけど、ごく簡単な処理をはさみたい時があるのでこちらを使っています。JSビルド処理の中ではpassThroughBundledという独自の関数を作成し、webpackを通した後にbundle.jsという名前の入ったファイルのみをストリームに流すようにしています。こちらで意図しない余計なファイルがビルドされてしまうのを避けています。

webpack-stream

こちらはgulpwebpackを連携するためのプラグイン。webpackは絶対必要なのでこれが無いと始まりません。

③Webpack

続いてwebpackを見ていきます。
これも人によって書き方が分かれるところですが、僕は1ファイルでなく3ファイルで管理しています。

  • webpack.common.js:開発・本番モード共通
  • webpack.dev.js:開発モード(JSを圧縮しない)
  • webpack.prod.js:本番モード(JSを圧縮)

webpack4から開発モードと本番モードが出来たので、圧縮処理を省いたものを開発モード、圧縮処理を入れたものを本番モードにしています。
具体的には以下コードで見ていきます。

webpack.common.js
import webpack                from 'webpack'
import EncodingPlugin         from 'webpack-encoding-plugin'
import VueLoaderPlugin        from 'vue-loader/lib/plugin'
import {paths}                from './config.js'

module.exports = {
  cache    : true,
  output   : {
    filename : '[name].bundle.js',
  },
  optimization: {
    splitChunks: {
      name   : 'vendor',
      chunks : 'initial',
    }
  },
  plugins  : [
  new VueLoaderPlugin(),
  new webpack.optimize.OccurrenceOrderPlugin(),
  new webpack.optimize.AggressiveMergingPlugin(),
  new EncodingPlugin({
    encoding: 'utf-8'
  }),
  new webpack.ProvidePlugin({
    $                : 'jquery',
    jQuery           : 'jquery',
    objectFitImages  : 'object-fit-images',
    anime            : ['animejs/lib/anime.es.js', 'default'],
  })
  ],
  module: {
    rules: [
    {
      test: /\.js$/,
      exclude: [
      /(node_modules|bower_components)/
      ],
      use: [{
        loader: 'babel-loader'
      }]
    },
    {
      test: /\.vue$/,
      loader: 'vue-loader',
      options: {
        loaders: {
          js: 'babel-loader'
        }
      }
    }
    ]
  },
  resolve: {
    modules    : ["node_modules"],
    alias      : {
      '@js' : paths.jsSrcDir,
      'vue$': 'vue/dist/vue.esm.js'
    }
  }
};

こちらは共通設定です。通常のJSと、Vue.jsを最近よく使うのでそれに対応したものです。Typescriptもそろそろやってかないとと思っていますが、まだ手が出せていません…

設定について少し見ていきます。

cache

trueに設定するとwebpackのキャッシュを有効にします。

output

これはエントリーファイルの親ディレクトリが名前になるようにgulpwebpackを使って調整しています。どういうことかというと、

js/
└──  /common
     ├── entry.js => エントリーファイル ①②をインポート
     ├── main.js  => モジュール化した処理①
     └── utils.js => モジュール化した処理②
└── /reserve
     ├── entry.js => エントリーファイル ③④をインポート
     ├── main.js => モジュール化した処理③
     └── utils.js => モジュール化した処理④

JSを設置する場合はこういうファイル構成にして使います。
この場合、common.bundle.jsreserve.bundle.jsという名前のファイルがビルドされるということです。

optimization.splitChunks

webpack4で追加された機能。以前はCommonsChunkPluginというものがあったらしいですが使っていませんでした。こちら最近かなり重宝しています!
具体的に何ができるかというと、複数ファイルにまたがって使われている共通モジュールをバンドルして別ファイルとして出力する、ということができます。

先ほどの例で言うと、

optimization: {
    splitChunks: {
      name   : 'vendor',
      chunks : 'initial',
    }
},

こういう設定がしてある場合、common.bundle.jsreserve.bundle.jsというファイルの他にvendor.bundle.jsという共通ファイルが出来上がります。読み込む時は以下のようになります。

<script type='text/javascript' src='vendor.bundle.js'></script>
<script type='text/javascript' src='common.bundle.js'></script>

これの何が良いかというと3点あります。

1点目は、webpackの処理が早くなること。jQueryVue.jsなどの大きいライブラリなどを共通ファイルとして出力してくれるのですが、そういったファイルは基本読み込むだけなので、保存するたびに書き換えることはまずないでしょう。そのためsplitChunksを使って一旦作成されたvendor.bundle.jsのような共通モジュールはキャッシュが有効になり、変更されたファイルだけがビルドされるようになるのでビルド処理が格段に早くなります。けっこう大きいアプリケーションの開発などをしている時はwebpackのビルドが遅いと開発にならなくなってきますのでこれだけでもかなり助かります。

続いて2点目。ファイルサイズが小さくなること。これは処理を共通化することで生まれるメリットです。ここで言えばcommon.bundle.jsreserve.bundle.jsvendor.bundle.jsを利用するので二重になっていた部分を削減できます。

最後に3点目。読み込む時にキャッシュが効く。
どのページでもvendor.bundle.jsを読み込むことになるので、ページごとにJSの内容を変える場合などでも共通化されていない部分だけを読み込めばいいので読み込み速度も早くなります。

plugins

ここには外部プラグインやwebpackにある機能などを追加できます。

  • VueLoaderPlugin:vue.js用
  • webpack.optimize.OccurrenceOrderPlugin:ファイルサイズを縮小
  • webpack.optimize.AggressiveMergingPlugin:コードを圧縮
  • EncodingPlugin:ファイル形式を指定
  • webpack.ProvidePlugin:あらかじめ読み込むモジュールを指定
module

ここではloaderの設定を書きます。babel-loaderでES6対応、vue-loaderで.vue拡張子のファイルに対応します。

resolve

aliasにパスを指定しておくと読み込みが楽になります。paths.jsSrcDirがわかりにくいのでちょっと置き換えます。

resolve: {
    modules    : ["node_modules"],
    alias      : {
      '@js' : path.join(__dirname, '../assets/js'),
      'vue$': 'vue/dist/vue.esm.js'
    }
}

設定しておくとJSファイル内でimport '../assets/js/common/main.js'みたいに書いているものをimport '@js/common/main.js'というようにファイルの相対位置などを気にせずに呼び出すことができるようになります。

webpack.dev.js

こちらは開発モードの設定ファイルです。

import { merge }  from 'webpack-merge'
import common     from './webpack.common.js'

module.exports = merge(common, {
  mode     : 'development',
  devtool  : 'cheap-module-eval-source-map',
});

webpack-mergeというプラグインを使ってwebpack.common.jsと設定をマージします。ここではソースマップだけ設定しておきます。

webpack.prod.js

こちらは本番モードの設定ファイルです。

import { merge }       from 'webpack-merge'
import common          from './webpack.common.js'
import TerserPlugin    from 'terser-webpack-plugin'

module.exports = merge(common, {
  mode         : 'production',
  optimization : {
    minimizer  : [
    new TerserPlugin({
      terserOptions: {
        compress: {drop_console: true}
      }
    })
    ],
  }
});

開発モードとの変更点は以下です。
・ソースマップを出力しない
・JSを圧縮する

JSの圧縮はけっこう時間がかかりますし、Chrome DevToolsツールなどからソースを見る時にわかりにくくなるので、開発モードでは使用しない方が良いです。

最後に

僕が個人的に使っている環境なのでかなり偏ったものになっているかもしれません。このままコピペで他のプロジェクトに使うことはできないと思います。ただ全体ではなく部分でもだれかの参考になればと思います。

今回はCSSとJSのビルドタスクだけを紹介しましたが、htmlの圧縮や画像圧縮など細々したタスクがたくさん増えて複雑化してきました。

今後、TypeScriptを使うとなった時にはもっとシンプル構成で新しい環境にしたいなぁ〜。

記事一覧

RELATED

inoue
TIPS

【CSS + JS】テキストの無限ループアニメーション

inoue
TIPS

【Shopify】カートへの遷移を飛ばしてチェックアウトに進むボタンを設置する

inoue
TIPS

【JS】PhotoSwipe v5を使って画像をポップアップ表示する

inoue
TIPS

gulpでjpg・pngからAVIF画像を生成する

inoue
TIPS

【JS】MutationObserverでDOMを監視[PhotoSwipe(v5)+Swiper(v8)連携]

inoue
TIPS

【Google Sheets API】 スプレッドシートのデータをJSONで取得する

NEW POSTS

inoue
TRY

【WordPress】GA4連携の人気記事ランキング機能を自作プラグイン化してみた

inoue
TRY

【netlify】ビルドイメージを更新 [ Ubuntu Xenial 16.04 → Ubuntu Focal 20.04 ]

inoue
TIPS

【PHP】Google Business Profile APIを使ってクチコミを取得する

inoue
TRY

スクリーンショットのAPI「screendot」を使ってみた

inoue
TIPS

【window.matchMedia】メディアクエリでhoverが使えるデバイスを判定

inoue
TIPS

lax.jsの使い方【スクロール連動アニメーションの実装】

ブログ記事一覧
  • HOME
  • TIPS
  • gulp4 + webpack4でつくるフロント開発環境