Expo Web(React Native for web)チュートリアル

React Nativeは、JavaScriptフレームワークであるReactで開発された、ネイティブアプリ(iOSとAndroidの)を作成するフレームワークです。React Nativeは、iOSとAndroidの開発言語でもネイティブ開発ができる反面、Expoは純粋にJavaScriptとReactでのみ開発し、ネイティブ開発は、あらかじめ指定されたモジュールのみを使用するSDKとビルド環境を提供しています。Expoは自由度と機能支援率が低い一方、JavaScriptだけで素早くアプリを作ることができ、別途ネイティブ駆動環境を用意して、自分のPCにインストールしなくてもよいというメリットがあります。

最近、React NativeはReact Native for webを発表し、もう1つのプラットフォームを追加しました。Expoもこれに従い、6月にリリースされたSDK 33バージョンでは、Expo Webベータ版のサポートを開始しました。Expo Webは、モバイルウェブを実装できるようにPWA(Progressive Web Apps)をサポートします。React NativeとExpoが提供するコンポーネントで、ネイティブアプリを開発できますが、Webでも同じコンポーネントに戻れるようにしました。Reactがそうであるように、Webでは当然React DOMがレンダリングに使用されます。ここでは「React Native for web」よりもExpo Webを重点的に調べようと思います。
Expo Webのベータ版を使って、インストールと実行、駆動、配布、テスト方法などを調べてみましょう。

特徴(長所、短所)

React Nativeは、JavaScriptとReactフレームワークで作成し、マルチプラットフォームで動作する特徴があります。正式にサポートしているプラットフォームは、iOSとAndroidです。Windowsは、かつてMicrosoftが直接サポートしていましたが、現在はWindows 10とOSが統合し、Windows 10基盤で再構築しています。
ブラウザのサポート範囲は、Chrome、Firefox、Edge、Safari 7+、IE 10+です。

Reactは仮想DOM基盤で、実際のレンダリングはReact DOMのようなレイヤーが処理する構造になっています。仮想DOMとレンダリングレイヤーを分離したため、レンダリングレイヤーを複数のプラットフォームに合わせて実装することができます。つまり、React Nativeでサポートするコンポーネントを使ってアプリを作成すると、プラットフォームに合わせてレンダリングされます。次のサンプルコードは、expo init既定の設定で自動生成されたコードです。TextViewのようなReact Nativeのコンポーネントは、プラットフォームに依存したコンポーネントですが、ランタイムでは各プラットフォームに合わせてレンダリングが実行されます。

import React from "react";
import { StyleSheet, Text, View } from "react-native";

export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.tsx to start working on your app!</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center"
  }
});

すべての機能が同じではない

iOSとAndroid間にも止むを得ずネイティブ機能を統合できないコンポーネントや機能があります。各OSのネイティブ機能が異なり、ストアポリシーによって特定プラットフォームでのみ動作するコンポーネントや機能になっているため、追加されたWebの場合でも、ネイティブサポートの有無によって機能が異なります。Expoでソースコードを作成すると、プラットフォームによって分岐をキャッチする場合があるように、Webのサポート範囲も異なります。例えば、連絡先(Contacts)機能は、iOSとAndroidにありますが、Webにはありません。Expo Webの機能リストは、こちらから確認できます。

インストールと実行

SDK 33のリリースノートにあるように非常に簡単にインストールして実行できます。

npm install -g expo-cli
expo init awesome-expo-web-example

expo initの場合、一部オプションを設定することができ、tabsを選択するとreact-navigationを使って、タブ基盤の様々な画面を設定した例題が利用できます。この例では、WebとiOS、Androidを実行してみましょう。

インストール後、app.jsonファイルを開くと、プラットフォームにwebが追加されたことを確認できます。

{
  "expo": {
    "name": "awesome-expo-web-example",
    "slug": "awesome-expo-web-example",
    "privacy": "public",
    "sdkVersion": "33.0.0",
    "platforms": [
      "ios",
      "android",
      "web"
    ],
    ...
  }
}

Expo Webを実行してみましょう。

cd awesome-expo-web-example
expo start -w

しかし、Webのバージョンがエラーを出力し、実行できませんでした。GitHubでイシューが登録されていますが、Web実行でのみ発生する問題で、パッチになるのを待つ必要があります。Web版のテストのため、次のパスのソースから画像ファイルの名前を変更すると実行され、iOSとAndroidは固定ソースを元に戻す必要があります。

node_modules/@expo/samples/ExpoLinksView.js

<Image
  source={require("./assets/images/expo-icon@2x.png")}
  ...
/>
  • -w オプションは、Webバージョンをブラウザで実行させるオプションである。
  • -i オプションは、iOSバージョンを実行させるオプションである。
  • -a オプションは、Androidバージョンを実行させるオプションである。

Web、iOS、Androidすべて1つのソースで、それぞれのプラットフォームに合わせて駆動されることを確認できます。左からWeb、iOS、Android実行画面です。

Expo Web構築

配布ビルドも非常に簡単です。build:iosbuild:androidbuild:webが追加されました。しかし、まだベータ版のため、プロダクションビルドで実行すると、エラーが発生しています。残念ながらまだデベロップメントモードでビルドが必要です。

Webpack設定拡張

Webpack設定を拡張して、デベロップメントモードでビルドします。@expo/webpack-configをインストールし、webpack.config.jsファイルを作成して設定を拡張できます。

yarn add -D @expo/webpack-config

webpack.config.jsファイルの作成

const createExpoWebpackConfig = require("@expo/webpack-config");

module.exports = function(env, argv) {
  env.mode = "development";
  const config = createExpoWebpackConfig(env, argv);
  return config;
};

Web構築

expo build:web

ビルドはWebpackを用いて、JavaScriptとリソースが作成されました。ビルド結果はweb-buildフォルダ内に作成されます。

PWAサポート

Expo WebはPWA(Progressive Web App)をサポートします。Expoのapp.jsonファイルの情報をもとに、PWAの設定ファイルであるmanifest.jsonファイルを生成します。ビルドされたindex.htmlを見ると、service-workerも使用していることが分かります。

<script>
  if ("serviceWorker" in navigator) {
    window.addEventListener("load", function() {
      navigator.serviceWorker.register("/service-worker.js");
    });
  }
</script>

Chrome開発ツールのAudits(lighthouse)で確認すると、PWAのサポート評価が表示されます。

静的ファイルの追加

Expoで開発したJavaScriptのソースやインポートファイルの他に、Webアプリでサービスする静的ファイルを追加することができます。ソースのルートフォルダにwebというフォルダを追加して、下位に必要な静的ファイルを追加できます。

/web
   ㄴTOAST UI Grid v4.0.png
   ㄴother resources.file

ファイルを追加した後、再びexpo build:webでビルドすると、web-build/TOAST UI Grid v4.0.pngファイルが追加されます。

GitHubを使ったWeb配布

Expo Webがベータ版であるため、プロダクションモードでのビルドがうまくいかない状態ですが、今後パッチされたとき、ビルドされたWebアプリを簡単に配布できるヒントを共有します。

Expo Webで構築されたWebアプリは、どのサーバーでも配布すればすぐにサービスができ、簡単にGitHubページを使って配布ができます。Expo WebをGitHubページに配置してみましょう。

GitHubストアを新規作成し、次のようなプロセスでGitHubページに配置できます。

git remote add origin https://github.com/dongsik-yoo/awesome-expo-web-example.git

npm i --save-dev gh-pages

npx gh-pages -d web-build

3つのプラットフォームからストーリーブックテスト

ストーリーブックは、コンポーネント単位のUIを開発してテストするのに有用なE2Eツールです。ExpoをデベロップメントモードでiOSとAndroidで開発すると、多少速度が遅いため、ストレスを感じるときがあります。ストーリーブックでUIを作成すると、その快適さと即時反応が開発速度を高める大きな助けとなるでしょう。したがって、Expo開発時にストーリーブックを使用できるのは大きな利点だと思われます。ストーリーブックはReact Nativeにも対応していますが、テストしてみるとExpo Webバージョンにも対応していることが分かります。

ストーリーブックReact Native環境構築

インストールガイドを参照して、簡単に自動インストール、環境設定ができます。

npx -p @storybook/cli sb init --type react_native

Reactストーリーブックのインストールとは異なり.storybookフォルダではなく、storybookフォルダが作成されます。インストール後は、エントリーのファイルをストーリーブックUIに変更する必要があります。テストのためにソースコードを変更しなければならない欠点がありますが、シミュレーターよりもより速いサイクルで開発できるメリットがあります。

App.jsファイルの変更

/*
 ストーリーブックのため、他の部分はすべて注釈処理します。
*/
export default from "./storybook";

index.web.jsファイルの追加
ストーリーブックのReact Nativeバージョンでは、プラットフォーム別に異なるコンポーネントを使ってテストが必要な場合、テストファイル名の形式をindex.{platform}.jsファイルで作成します。生成されたstorybook/stories/Buttonフォルダを見ると、次のように両方のプラットフォーム用のファイルがあらかじめ作成されています。

  • index.ios.js
  • index.android.js

ここでindex.web.jsファイルを追加すると、Expo Webをストーリーブックでテストできます。

import React from "react";
import { TouchableHighlight } from "react-native";

export default function Button({ onPress, children }) {
  return <TouchableHighlight onPress={onPress}>{children}</TouchableHighlight>;
}

React Native Webテスト

再度、expo start -wでExpo Webを実行すると、ブラウザでストーリーブックUIが表示され、react-nativeのコンポーネントを使用することができます。ストーリーブックサーバーが起動しないので、少し中途半端な感じですが、HMRなどUI開発には問題がなく、iOSとAndroidの端末でも同じように動作しています。実行後、開発者ツールで見てみると<TouchableHighlight>コンポーネントがdivでレンダリングされたことを確認できます。

3つのプラットフォームでストーリーブックを実行する

左からWeb、iOS、Androidのストーリーブックテストの実行画面です。

さいごに

「React Native for web」ベースのExpo Webはまだベータ段階なので、前述のようにプロダクションビルド時のエラーやGitHubページで動作されない問題があります。しかし、SDK 34ではベータを卒業してRC(Release Candidate)版をリリースする予定で、SDK 35から正式リリースを予定しています。

TOAST Meetup 編集部

TOASTの技術ナレッジやお得なイベント情報を発信していきます
pagetop