はじめに
vite×react環境でmswをGetting Started通りに設定したときworkerの設定で動かない部分がありました。mswをreactのentryファイルに設定する部分です。この記事ではこれを解決する方法を示します。
コードと原因
mswに書かれているviteでは動かないコードはこちらです。
// src/index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
if (process.env.NODE_ENV === 'development') {
const { worker } = require('./mocks/browser')
worker.start()
}
ReactDOM.render(<App />, document.getElementById('root'))
viteではsrc/index.js
ではなく、src/main.tsx
に同様のコードが書かれています。さらに、これはreact18より前の書き方なのでそこら辺はよしなに読み取ってください(今回のとは直接関係ないです)。このコードは開発環境であれば./mocks/browser
に定義されたworkerを取得してそれを起動するコードです。これによって開発環境においてのみ定義されたapiのmockを利用できます。
本題となる箇所ですが、
const { worker } = require('./mocks/browser')
です。vite環境では、require
が定義されていないのでUncaught ReferenceError: require is not defined
となります。require
ができないのはrequire
がCommonJSの仕様であるからで、viteの開発環境ではNative ESMを利用することから動作しないのだと考えられます。
解決策1(簡単)
if節ではなく外でesmのインポートをします。これが一番簡単な修正です。
// src/main.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { worker } = from './mocks/browser'
if (import.meta.env.DEV) {
worker.start()
}
ReactDOM.render(<App />, document.getElementById('root'))
変更箇所はprocess.env.NODE_ENV === 'development'
がimport.meta.env.DEV
となったところと、require
でインポートしていたところからファイルの先頭でesmの(import...from...
のような)インポートするようになったところです。
前者はviteの正しい環境判定法はこちらなので修正しておきました。
後者が本題です。問題の原因はrequire
を使ったインポートなのでesmのインポート形式で行うようにしました。
これで万事解決と言いたいところですが、パフォーマンスの面でこの解決方法には懸念があります。importをif節の外、ファイルのトップにおいたことによってこのファイルが呼び出されたときに必ずインポートされるようになってしまいます。そのため使用しない本番環境でもmswがバンドルされてしまうようになってbuild後のファイルが膨れ上がります。私の環境では300KiB程も膨れ上がることを確認できました。そのため、ファイルサイズを気にする場合はこの方法は避けた方が良いです(気にしないことはほとんどない気もしますが)。
解決策2(おすすめ)
dynamic importします。この方法は多少変更が増えますが、解決策1のようなパフォーマンスの心配はないです。
// src/maix.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
const prepare = async (): Promise<void> => {
if (import.meta.env.DEV) {
const { worker } = await import('./mocks/browser');
worker.start()
}
return Promise.resolve()
}
prepare().then(() => {
ReactDOM.render(<App />, document.getElementById('root'))
})
prepare
という非同期関数を作って、それが呼び出された後にreactのrenderを開始するようにしました。prepare
という非同期関数は開発環境であれば、workerの呼び出しと起動をします。それ以外の環境ではすぐにresolveします。こうすることで、開発環境でのみmswを呼び出すことに成功して、先ほどのようなパフォーマンスの損失を避けることができます。
とは言え、これだとreactのrenderに変更を加えているため不安になる方もいらっしゃると考えられます。しかし、この方法と似た変更をmswの公式でも紹介されているため安心して使うことができます。コードの面からも開発環境以外ではPromise.resolve()
しているだけなので動作に影響はないです。
解決策3
viteのプラグインを入れてrequireを可能にするやり方です。この方法はあまり推奨できないので書きません。