zenpachi blog

Vueのbundleが遅くて辛いのでviteに乗り換える

このエントリーをはてなブックマークに追加

Vueを使用したプロジェクトの開発時のbundleが遅すぎて非常に辛い。
chunkやらなんやら高速化するための対応は色々しているのだが、それでも遅くて開発時に困っていたので解消方法を探してたらviteというものを発見し、導入してみたのでそのメモ。

viteとは

開発中にbundleを行わず、ES Module importを使用するビルドツール。Vueの開発者であるEvan You氏が開発している。
bundleしないので当然非常に早い。 .vueファイルなどは読み込んだタイミングでコンパイルされる。画面で実際にimportされたコードのみがコンパイルされるので、アプリケーション全体のbundleやコンパイルを待つ必要がない。

なお、2020/8/16時点でのステータスはbeta。1.0が近々リリースされるらしい。

とりあえずvite使ってみる

Getting started

$ npm init vite-app <project-name>
$ cd <project-name>

作られるpackage.jsonは以下

{
  "name": "sample",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "dependencies": {
    "vue": "^3.0.0-rc.1"
  },
  "devDependencies": {
    "vite": "^1.0.0-rc.1",
    "@vue/compiler-sfc": "^3.0.0-rc.1"
  }
}

開発サーバを起動させてみる

$ npm install
$ npm run dev 

> project-name@0.0.0 dev /your/project/path
> vite

vite v1.0.0-rc.4

  Dev server running at:
  > Local:    http://localhost:3000/
  > Network:  http://192.168.3.9:3000/

大体1秒程度で立ち上がる。非常に小規模なサンプルプロジェクトなのでbundleしてもそこまでの差は出ないかもしれないが、なかなか速そう。

開発サーバを起動

portの変更

--port オプションを使う。

$ npx vite --port 8080
vite v1.0.0-rc.4

  Dev server running at:
  > Local:    http://localhost:8080/
  > Network:  http://192.168.3.9:8080/

これで8080番portでDev serverが立ち上がった。 configファイルに記載することでもportを変更できる。viteでは vite.config.js というファイルで設定する。

module.exports = {
  port: 8080,
}

HTTPS/2化

ドキュメントにある通り--https オプションを使えば勝手にself-signed cert を作ってくれてHTTP/2も有効になる。

$ npx vite --https
vite v1.0.0-rc.4

  Dev server running at:
  > Local:    https://localhost:3000/
  > Network:  https://192.168.3.9:3000/

大抵の場合これで事足りる気がするが、何らかの理由で自前の証明書を使いたい場合は設定できる。

const fs = require('fs')

module.exports = {
  https: true,
  httpsOptions: {
    key: fs.readFileSync('./cert/key.pem'),
    cert: fs.readFileSync('./cert/cert.pem')
  }
}

CSSプリプロセッサ

CSSにscssを使用する場合はモジュールの追加が必要。

$ npm i sass --save-dev

これでVueのSFCのstyleに対してscssが使用できる。

<style lang="scss" scoped>
.hoge {
  color: red;
  .fuga {
    color: blue;
  }
}
</style>

vue-cliを使用した既存プロジェクトからの乗り換え

vue2.xへの対応

既存プロジェクトはVue2系を使用しているが、そもそもviteは2系に対応していない。
3系はまだbeta版なのですぐにプロダクトに導入するのは難しいのでなにか方法がないかと探したらありがたいことに2系に対応するためのpluginを作っている方がいた。
内容の精査はできていないが、開発時にしか使わない想定だし最悪なにかあれば現状通りvue-cliによるbuildに戻せばいいだけなのでとりあえず使わせていただく。

$ npm i vite-plugin-vue2 --save-dev

私が試したタイミングではviteの最新版は 1.0.0-rc.3 、npm上のvite-plugin-vue2の最新版は 0.0.3 だったが、微妙にバージョンの互換性がなく、Vueのtemplate内を変更した時にHMRがうまくいかずエラーが出てしまう。
vite-plugin-vue2の最新のソースを落としてきて自前でbuildするか、viteの 1.0.0-rc.1 を使用すれば解消する。
たぶん近いうちに最新版も対応されると思うが一応メモ。

→ 追記: vite-plugin-vue2が 0.1.3 にupdateされて解消した。

configファイルでvite-plugin-vue2を使用する。

const { createVuePlugin } = require('vite-plugin-vue2')

module.exports = {
  plugins: [createVuePlugin()],
}

これでVue2系が使用できた。

aliasの対応

vue-cliでは src ディレクトリに対して @ をaliasとして使っていたのでviteでもそのaliasが使えるようにする。

importが import HelloWorld from '/@/components/HelloWorld.vue' のような形であれば、configファイルに以下のようにaliasを設定すれば対応できる。

import path from 'path'

module.exports = {
  alias: {
    '/@/': path.resolve(__dirname, '/src'),
  },
}

ただし、vite/config.ts at master · vitejs/vite · GitHub あたりを読んだところfsディレクトリへのaliasは / で開始/終了していないといけないらしい。
つまり import HelloWorld from '@/components/HelloWorld.vue' などのように記述していた場合aliasが使用できないのでresolverを書くことで対応する必要がある。

import path from 'path'

const aliasRegx = /^@\//

const myResolver = {
  alias (id) {
    if (id.match(aliasRegx)) {
      return path.resolve('/src', id.replace(aliasRegx, ''))
    }
  },
}

module.exports = {
  resolvers: [myResolver],
}

拡張子の省略に対応する

既存プロジェクトではimportにおいて .vue.scss を省略している。
viteでは省略がサポートされている拡張子は '.mjs', '.js', '.ts', '.jsx', '.tsx', '.json' のみのようなので、configファイルのresolverで対応する。 (vite/resolver.ts at master · vitejs/vite · GitHub

import path from 'path'
import fs from "fs"

const aliasRegx = /^@\//
const supportedExts = ['.vue', '.scss']

const isFile = file => {
  try {
    return fs.statSync(file).isFile()
  } catch (e) {
    return false
  }
}

const resolveExt = _path => {
  let resolvedPath = _path
  if (!isFile(path.join(__dirname, resolvedPath))) {
    for (const ext of supportedExts) {
      if (isFile(path.join(__dirname, `${resolvedPath}${ext}`))) {
        resolvedPath += ext
        break
      }
      if (isFile(path.join(__dirname, `${resolvedPath}/index${ext}`))) {
        resolvedPath += `/index${ext}`
        break
      }
    }
  }
  return resolvedPath
}

const myResolver = {
  alias (id) {
    if (id.match(aliasRegx)) {
      return path.resolve('/src', id.replace(aliasRegx, ''))
    }
  },
  requestToFile (req) {
    if (resolveExt(req) !== req) {
      return path.resolve(__dirname, resolveExt(req))
    }
  },
}

module.exports = {
  resolvers: [myResolver],
}

CORSへの対応

viteの開発サーバ(koa)がデフォルトだとCORSに対応していないので、必要がある場合は対応しておく。

$ npm install @koa/cors --save-dev
import cors from '@koa/cors'

module.exports = {
  configureServer: ({ app }) => {
    app.use(cors({ origin: '*' }))
  },
}

感想など

個人的なプロジェクトで使ってみた限りでは非常に速くなって快適。
業務のプロダクトの方に導入してみようとしたところ、使っているライブラリがESModule importに対応していなかったりでクリアできていない部分が残っている。
個人的なプロジェクトやこれから始める小規模なプロジェクトで使ってみるにはいいんじゃないですかね。もうすぐVue3系もリリースされるはずだし。
いずれにしても開発時のみと考えればとりあえずあまり考えずに導入してみても良さそう。

このエントリーをはてなブックマークに追加