モチベーション
windowsPCでexpoの環境構築を行っていた際に、色々な沼にはまってしまった。
そのため、環境構築の手順や詰まった際の対処方法を共有したい。
概要
expoはReactNativeと組み合わせることで、android・iosを問わずにネイティブアプリを開発できるありがたいOSSです。
Reactの記法を基にTypeScript(JavaScript)でアプリを開発できるので、Webアプリ開発でReactを触っていた方にとってはかなり敷居が低いと思います。(色々異なる点はありますが)
今回はそんなexpoの環境構築に関して、通常の環境構築とdevContainerを用いた環境構築を紹介していきます。
通常の環境構築
操作自体はexpoのドキュメントに沿った内容となります。
- 使用した環境
- PC
- Windows 11
- node 22.14.0
- npm 10.9.0
- コードエディタ vscode
- iPhone 14
- PC
1. expoプロジェクトの作成
通常の環境構築時は、プロジェクトをOneDriveやDropboxなど自動同期系ソフトが管理するディレクトリには作成しないことが推奨されます
nodemodulesが同期されること自体あまり嬉しくないことや、同期中にファイルをロック?されることでexpoコマンドが正しく動作しない場合があります。
2022年頃まではexpo-cliのグローバルインストールが必要でしたが、expoのアップデートに伴いexpoモジュールに内包されたcliで操作が完結するようになりました
The New Expo CLI
プロジェクトを作成したいディレクトリでnpx create-expo-app@latest
またはnpx create-expo-app@latest プロジェクト名
を実行します。
PS C:\パス> npx create-expo-app@latest
Need to install the following packages:
create-expo-app@3.2.0
Ok to proceed? (y)
Creating an Expo project using the default template.
To choose from all available templates pass in the --template arg:
$ npx create-expo-app --template
To choose from all available examples pass in the --example arg:
$ npx create-expo-app --example
√ What is your app named? ... sample-app
√ Downloaded and extracted project files.
> npm install
npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
npm warn deprecated @babel/plugin-proposal-class-properties@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
npm warn deprecated @babel/plugin-proposal-nullish-coalescing-operator@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated @babel/plugin-proposal-optional-chaining@7.21.0: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
npm warn deprecated abab@2.0.6: Use your platform's native atob() and btoa() methods instead
npm warn deprecated domexception@4.0.0: Use your platform's native DOMException instead
npm warn deprecated rimraf@2.6.3: Rimraf versions prior to v4 are no longer supported
npm warn deprecated sudo-prompt@9.1.1: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
npm warn deprecated @xmldom/xmldom@0.7.13: this version is no longer supported, please update to at least 0.8.*
npm warn deprecated sudo-prompt@8.2.5: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
added 1125 packages, and audited 1126 packages in 3m
94 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
✅ Your project is ready!
To run your project, navigate to the directory and run one of the following npm commands.
実行が完了すると、必要なnodemodulesがインストールされた状態でexpoのプロジェクトディレクトリが作成されます。
2. スマホでExpo Goのインストール
AppStoreからExpo Goをインストールします。
3. スマホとPCのネットワーク設定
expoで書いたコードをスマホで動作確認するためには、スマホとPCが同じWi-Fi (プライベートなネットワーク)に属している ことが必要になります。
expoは動作確認用のコマンド実行時に、ホストPCのIPv4アドレスを使用してアプリを渡すためのサーバを立てるため、同じネットワーク空間にいない場合にはExpo Goからサーバに接続することができずにタイムアウトしてしまいます。
expoではMetro bundlerを使用して、TS(JS)のバンドル+(おそらく)バンドルファイルを渡すためのサーバを起動します。
スマホ側はサーバのURL(ホスト、ポート)からアクセスし、Expo Go上でバンドルファイルをダウンロード、実行することでアプリを使用することができます。
4. 実機でのアプリ動作確認
プロジェクトの階層でnpm start
を実行することでターミナル上にQRコードが表示されます。
表示されたQRコードをスマホで読み込むことで、Expo Goが自動で開きアプリの動作を確認することができます。
root ➜ /workspace/app $ npm start
> app@1.0.0 start
> expo start
Starting project at /workspace/app
Starting Metro Bundler
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
█ ▄▄▄▄▄ █▄▀ ▀ ▄ █▀█ ▄▄▄▄▄ █
█ █ █ █ █▀ ▄▀▀█ █ █ █
█ █▄▄▄█ █▄█▀ ▄▄█▄▀█ █▄▄▄█ █
█▄▄▄▄▄▄▄█▄█ █ █ ▀ █▄▄▄▄▄▄▄█
█ ▄█▀▄▀▄█ ▄ ▀▀▄▄▄██▄ ▄▀▄█
███ █▀ ▄█ ▄▄▀▄█▀█ ▀██▀███
█▄█▀ █ ▄▀▀▄▄▀ ▄█ ▄█ ▄ █ █▀█
█▀▄█▀ ▄█ ▄ █▄██ ▀▀█▀▀█ ▀█
████▄█▄▄▄▀███▄▄ ▄ ▄▄▄ ▀▄█▀█
█ ▄▄▄▄▄ ███ ▀██▀█ █▄█ █▄▄ █
█ █ █ █▀ ███▀▀▄▄ █▀ ▀█
█ █▄▄▄█ █ ▄▀▀▀ ▀▄▀▄█▄▄ ▄██
█▄▄▄▄▄▄▄█▄█▄▄▄██▄██████▄▄▄█
› Metro waiting on exp://192.168.0.59:8081
› Scan the QR code above with Expo Go (Android) or the Camera app (iOS)
› Web is waiting on http://localhost:8081
› Using Expo Go
› Press s │ switch to development build
› Press a │ open Android
› Press w │ open web
› Press j │ open debugger
› Press r │ reload app
› Press m │ toggle menu
› shift+m │ more tools
› Press o │ open project code in your editor
› Press ? │ show all commands
Logs for your project will appear below. Press Ctrl+C to exit.
先ほどターミナル上に表示されたQRコードは、
「› Metro waiting on exp://192.168.0.59:8081」の行にある「exp://192.168.0.59:8081」というURIを示しており、これがバンドルファイルを受け取るエンドポイントになります。
そのため、QRコードを読み込まなくてもSafariなどのブラウザで「exp://192.168.0.59:8081」を直打ちして実行することも可能です。
devContainerを用いた環境構築
- 使用した環境
- PC
- Windows 11
- DockerDesktop
- コードエディタ vscode
- 拡張機能 Dev Containers
- iPhone 14
- PC
1. devContainerの定義作成
プロジェクト用にディレクトリを準備し、中に.devcontainer/dencontainer.jsonを作成します。
ホストPCのIPアドレスに関しては、3. スマホとPCのネットワーク設定を実施した後のアドレスを使用してください。
{
"name": "Node.js & TypeScript",
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-22-bookworm",
"runArgs": [
"--name=EXPOAPP_SAMPLE",
"--rm"
],
"appPort": ["ホストPCのIPアドレス:8081:8081"],
"containerEnv": {
"REACT_NATIVE_PACKAGER_HOSTNAME": "ホストPCのIPアドレス"
},
"remoteUser": "root",
"workspaceFolder": "/workspace",
"mounts": [
"source=${localWorkspaceFolderBasename},target=${containerWorkspaceFolder},type=volume"
],
"customizations": {
"vscode": {
"extensions": [],
"settings": {
"editor.tabSize": 2,
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true
}
}
}
}
devcontainer.jsonの記述内容に関して重要な要素を以下にまとめました。
ポイントは、スマホからQRコードを読み込んでexpoに接続する流れを
「スマホ⇒ホストPCのIPアドレス:8081⇒ポートフォワードでコンテナのIPアドレス:8081⇒expoが起動したサーバ」
にすることです。
devcontainer.jsonでのポイント
1. addPort
ホストPCとコンテナのポート紐付け
expoは8081ポートで起動するため、ホストPCからコンテナの8081にポートフォワードする必要があります。 また、npm start時に表示されるQRコード(URL)においても8081が使用されており、スマホで読み込んだ際にはホストPCの8081に接続しにいきます。そのため、ホストとコンテナともに8081に指定することを推奨します。
2. containerEnv
コンテナ内の環境変数。
通常devcontainerの場合はコンテナのIPアドレスを使用してexpoがQRコードを作成するため、スマホから読み込んでもアドレスを解決できず接続できません。しかし、環境変数REACT_NATIVE_PACKAGER_HOSTNAMEにホストPCのIPアドレスを設定することで、expoがホストPCのIPアドレスを使用してQRコードを作成します。
addPortとcontainerEnvの設定内容の補足
コンテナへのポートフォワード以外はQRコードから接続するための設定なので、設定しなくてもスマホのブラウザから直打ちでホストPCのアドレス:ポートに接続すれば動作はします
3. mounts
ファイルストレージのマウント設定。
bindの場合、ホストPCのファイルストレージの指定領域をコンテナ内のファイルストレージにマウントします。
volumeの場合、Dockerが管理する指定領域をコンテナ内のファイルストレージにマウントします。volume名には半角英数+一部記号[a-zA-Z0-9][a-zA-Z0-9_.-]
のみ使用できます。
今回コンテナのワークスペース全体をvolumeマウントしているのは、OneDriveの同期領域から外すため+ホットリロードを有効にするためです。
OneDriveの同期領域から外せる理由
windowsOSでDockerDesktopを使用する場合は、自動的にWSLによる仮想Linux環境上でDockerがインストールされ、WSLのファイルストレージ(確認方法)のDocker管理領域でvolumeが保持されます。
そのため、OneDriveの管理領域でdevcontainerを定義した場合でも、volumeマウントを使用した場合には実際のコードやnodemodulesがwsl上に存在しているため同期から外すことができます。
ホットリロード
WSL+Dockerでwindowsのファイルをコンテナにbindマウントした場合、ファイル変更が検知できないらしい。
参考:Dockerを使っていてhot reload系が効かなかったとき、WSL+DockerでHot reload が動作しない
Docker Desktop on WSL2: The Problem with Mixing File Systems | by Manfred Lange | Level Up Coding
上記の記事によるとファイル変更を検知するinotifyという機能がWindowsのファイルシステムとLinuxのファイルシステムでまたがっていると機能しないためHot reload機能も動作しないようです.
個人的にはnode_modules以外はbindマウントしたかったのですが、
参考元にあった解決方法であるCHOKIDAR_USEPOLLING=1
またはCHOKIDAR_USEPOLLING=true
の指定、Nextjsで開発した際に効いたWATCHPACK_POLLING=true
の指定を試してもホットリロードが動かず、、、、
そのため、今回は全てをvolumeマウントして解決しました。
2. devContainerの起動
vscodeの左下にある「><」のマークを押し、Reopen in container
を選択することでdevcontainerが起動します。
vscodeの画面が切り替わり、左下の「><」のマークのあたりにDevContainerと表示されていれば成功です。
3. プロジェクトの作成⇒実機での動作確認
通常の環境構築の手順に沿って実施します。(3. スマホとPCのネットワーク設定 は実施済みのため飛ばして問題ありません)
まとめ
devcontainerは便利。