2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

viteを用いたmswの設定。

Last updated at Posted at 2022-10-24

はじめに

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を可能にするやり方です。この方法はあまり推奨できないので書きません。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?