🌏

WebpackでVue3+TypeScript+Sass+ESlint+Babelの環境を構築しよう

2020/09/30に公開

はじめに

こんにちは、都内でフロントエンドのエンジニアをしているものです。
自己紹介は横に置いておいて Vue3 が2,3週間前ほどに無事リリースされましたね。
https://github.com/vuejs/vue-next
Vue3 は従来の Vue2 よりも、TypeScript を適用しやすくなっていて...
というのが今後の活躍に期待できるところです!

React でよくない?というような声も上がっておりますが、プロジェクト構成のそこまで大きくないプロジェクトにおいて有用であったり、レガシープロジェクトからモダンプロジェクトに切り替えるにあたって、React よりも導入コストが低いこともあり、切り替え安いというメリットもあります。
また、Vue はアジア圏内の方々がコミッターに積極的に参加しているなど、見守っていきたいところです。

そこで、モダンなフロント環境でより良い開発体験を得るために、Vue3 + TypeScript のフロント環境を Webpack で構築しようということでこの記事を書くにあたりました。

Webpack の設定や.tsconfigの設定などで苦労したポイントなどお話しできたらと思います。

この記事の対象者としては以下の方を想定しています。

  • フロントエンドの環境構築を少し触ったことがある
  • Webpack の環境構築に辛くなったことがある

構成要件

まず、 Webpack の環境として以下を含む構成想定しています。

  • Vue3
  • TypeScript
  • Sass
  • ESlint(airbnb)
  • Babel

ディレクトリ構成は以下を参考にしてください
https://github.com/olt556/vue3-template

準備

必要なモジュールのインストールとnpmスクリプトの設定

まず、package.jsonを以下に設定してください。

{
  "name": "vue3-tmplate",
  "version": "1.0.0",
  "description": "",
  "main": "main.ts",
  "scripts": {
    "build-dev": "webpack",
    "build-prod": "webpack --env.prod",
    "start": "webpack-dev-server",
    "lint-check": "./node_modules/.bin/eslint src -c ./.eslintrc --ext ts,tsx,vue; exit 0",
    "lint-fix": "./node_modules/.bin/eslint --fix src -c ./.eslintrc --ext ts,tsx,vue; exit 0"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "^3.0.0"
  },
  "devDependencies": {
    "@babel/core": "^7.11.6",
    "@babel/preset-env": "^7.11.5",
    "@typescript-eslint/eslint-plugin": "^4.3.0",
    "@typescript-eslint/parser": "^4.3.0",
    "@vue/compiler-sfc": "^3.0.0",
    "babel-loader": "^8.1.0",
    "css-loader": "^4.2.2",
    "eslint": "^7.10.0",
    "eslint-config-airbnb-base": "^14.2.0",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-prettier": "^3.1.4",
    "eslint-plugin-vue": "^7.0.0-beta.4",
    "eslint-plugin-vue-scoped-css": "^1.0.0",
    "html-webpack-plugin": "^3.2.0",
    "mini-css-extract-plugin": "^0.11.2",
    "node-sass": "^4.14.1",
    "sass-loader": "^10.0.2",
    "ts-loader": "^8.0.4",
    "typescript": "^4.0.3",
    "vue-loader": "^16.0.0-beta.8",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.9.0"
  }
}

その後、

npm i

を実行します。

とりあえず必要なnpmモジュールがインストールできました。

npm スクリプトについては、プロダクションビルドと開発環境用のビルドに分け、
vue, ts, tsxファイルに ESlint が当てられるように設定しています。

Babelの設定

Babel 用の設定は一応IE11に対応できるようにします。
.babelrcは以下の通りです。

{
  "presets": [
    [
      "@babel/preset-env",
        {
          "targets": {
            "ie": 11,
            "esmodules": true
          },
          "useBuiltIns": "usage",
          "corejs": 3
        }
      ]
  ]
}

ESLintの設定

ESLint 用の設定は、Vue3 に適用可能にした上で、Scoped CSS や TS ファイルにも効くようにします。 airbnb の ESlint 設定をほんの少し改造したものを利用します。
.eslintlrcは以下の通りです。

{
  "extends": [
    "airbnb-base",
    "plugin:@typescript-eslint/recommended",
    "plugin:vue/vue3-recommended",
    "plugin:vue-scoped-css/vue3-recommended"
  ],
  "plugins": [
    "@typescript-eslint",
    "vue"
  ],
  "env": {
    "node": true,
    "es6": true
  },
  "parser": "vue-eslint-parser", // これがないとVueファイルのtemplateがパースできません
  "parserOptions": {
    "parser": "@typescript-eslint/parser", // TSファイル用のパーサをここで定義します
    "ecmaFeatures": {
      "jsx": true
    },
    "extraFileExtensions": [
      "vue", // Vueファイルのパース用に必要です
      "ts",
      "tsx"
    ],
    "project": "./tsconfig.json", // .tsconfigを読み込みます
    "useJSXTextNode": true
  },
  "rules": {
    "import/no-unresolved": "off", // aliasを利用する場合必要です
    "semi": "off",
    "trailingComma": "off"
  }
}

tsconfigの設定

.tsconfigについては以下のように記述します。
内容については、必要に応じてコメントで記載しています。

{
  "compilerOptions": {
    "sourceMap": true,
    "allowJs": true, // JSを許可します
    "strict": true, // 型の恩恵を受けるために必須です
    "module": "ESNext", // moduleを読み込む際はESNext形式で読み込みます
    "target": "es5", // es5の形式にトランスパイルします
    "lib": [ // 各種ライブラリを呼び出せます
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "esModuleInterop": true, // モジュールのimport形式のESModules形式とCommonJS形式との互換性のために必須
  "baseUrl": "./",
  "paths": {
    "@/*": "src/*",
  },
  "include": [
    "src",
  ],
  "exclude": [
    "node_modules"
  ],
}

webpackの環境設定

webpack.config.jsの設定を以下に記述します。

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const {
  VueLoaderPlugin
} = require('vue-loader')

module.exports = (env = {}) => ({
  mode: env.prod ? 'production' : 'development',
  devtool: env.prod ? 'source-map' : '',
  entry: path.resolve(__dirname, './src/main.ts'),
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'bundle.js'
  },
  devServer: {
    contentBase: path.resolve(__dirname, './dist'),
    port: 8080,
    historyApiFallback: true, // without no routing
  },
  resolve: {
    modules: [path.resolve(__dirname, 'node_modules')],
    alias: {
      '@': path.resolve(__dirname, './src'),
      'vue': '@vue/runtime-dom' // これがないとvue-compilerがVueファイルを認識しません
    },
    extensions: ['.vue', '.ts', '.tsx']
  },
  module: {
    rules: [
      {
        test: [/\.vue$/],
        loader: 'vue-loader',
      },
      {
        test: /\.sass$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: { hmr: !env.prod }
          },
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              sassOptions: { indentedSyntax: true }
            }
          }
        ]
      },
      {
        test: [/\.ts$/, /\.tsx$/],
        use: [
          {
            loader: 'babel-loader'
          },
          {
            loader: 'ts-loader',
            options: {
              appendTsSuffixTo: [/\.vue$/] // これがないとTSファイル内でVueファイルを読み込めません
            }
          }
        ]
      }
    ],
  },
  plugins: [
    new VueLoaderPlugin(), // VueLoaderPluginがVue3用に必要です
    new HtmlWebpackPlugin({
      publicPath: 'dist',
      filename: 'index.html',
      template: 'src/html/index.html',
    }),
    new MiniCssExtractPlugin({
      publicPath: 'dist',
      filename: '[name].css',
    }),
    new webpack.DefinePlugin({
      __VUE_OPTIONS_API__: 'true',
      __VUE_PROD_DEVTOOLS__: 'false'
    })
  ],
})

注意事項

eslint-plugin-vuevue-loaderについては、2020/09/30現在ベータ版であるため、今後破壊的変更が加えられる可能性もあります。

おわりに

長々と読んでいただいてありがとうございます。
フロントの環境構築の参考になれば幸いです。
質問や間違いの指摘などありましたら気軽にお願いします!

Discussion