React Native for Web(npm名はreact-native-web)というライブラリを紹介します。

概要

https://github.com/necolas/react-native-web

もともとWebの世界(ブラウザで表示するフロントエンドの世界)で生まれたReactを、モバイルのネイティブアプリの世界に広げたものがReact Nativeですが、それを更に発展させて、React Nativeで書いたReactコンポーネントをWebでも動かせるようにするものがReact Native for Webです。一周回ってWebに先祖返りといった感じです。

ネイティブアプリとWebとでReactコンポーネントを共通化できることが一番の利点です。
その需要が今はニッチなのか、ちょっとマイナーなライブラリですが、何気にGitHubにはスターが8000以上ついてます。

有名どころでは Twitter LiteがReact Native for Web(以下、RN Webと略します)を使っています。1
Twitter Liteは最近注目されつつあるProgressive Web App (PWA)であり、モバイル端末上のブラウザ向けですから、同じくモバイル端末で動くネイティブアプリ版Twitterとの共通化がかなり図られているのでしょう。
今後PWAが本格的に普及してきたら、Twitterのようにネイティブアプリとの二本立ての需要、あるいはネイティブアプリからの移行の需要によって、RN Webは更に脚光を浴びるかもしれません。

この記事では、とっかかりとしてRN WebでHello worldを表示するレベルまでの例を紹介します。

サンプルプロジェクト

ディレクトリ構成

今回のサンプルのディレクトリ構成は以下の通りです(細かい部分は省略)。
RNアプリでもあり、RN Webアプリでもあるプロジェクトの例です。
もともとRNアプリだった既存プロジェクトに、あとからRN Webを追加したという想定で紹介します(そういう形でなくても、RN Webは使えます)。

/
  node_modules/
  android/
  ios/
  web/
    index.html
    webpack.config.js
  src/
    App.js
  index.js
  index.web.js
  package.json

RNアプリなのでandroidディレクトリ、iosディレクトリがあります。アプリの入り口はindex.jsです(これらはこの記事では気にしない部分)。
以下の部分がRN Webのために追加したものです。

  • web/index.html
  • web/webpack.config.js
  • index.web.js

android、iosと並んで、第3のプラットフォームとしてwebがある格好です。

また、いくつか必要なnpmがあるのでインストールしておきます。

// reactもdependenciesに必要だが、RNアプリには既にインストールされているはずなので、この例では省略。
npm install --save react-dom react-native-web
npm install --save-dev babel-plugin-react-native-web

// 今回はJavaScriptのバンドラーとしてWebpackを使うので、これらもインストール。
// あくまで今回の例ではWebpackを使うからであり、RN WebにはWebpackが必要というわけではない。
npm install --save-dev webpack webpack-dev-server

ソースコード

HTMLとJavaScriptの内容は以下の通りです。なにせWeb用ですのでHTMLが必要となるわけです。また、アプリの入り口はindex.jsではなくindex.web.jsというRN Web用のものを別途用意します。どちらも数行程度のものです。

web/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello, world!</title>
  </head>
  <body>
    <div id="root"></div>

    <!-- 今回の例ではWebpackを使い、最終的にbundle.web.jsというファイルを生成する。よってHTMLではそれを読み込む。 -->
    <script src="bundle.web.js"></script>
  </body>
</html>

App.js
// いたって普通のRNコンポーネント。
// Textなどを、react-native-webではなくreact-nativeからimportしている。

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

// Hello worldを表示するだけ
const App = () => (
  <View style={styles.box}>
    <Text style={styles.text}>Hello, world!</Text>
  </View>
);

// ちょっとしたスタイルの指定
const styles = StyleSheet.create({
  box: { backgroundColor: '#cccccc' },
  text: { fontWeight: 'bold' }
});

export default App;
index.web.js
import React from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';
import App from './src/App'

AppRegistry.registerComponent('App', () => App);

// 以下の行がRN Web特有。RN用のindex.jsとの違いはこれだけ。
AppRegistry.runApplication('App', { rootTag: document.getElementById('root') });

ビルド設定

webpack.config.jsの内容は以下の通りです(公式ドキュメントに載っている例を一部簡略化しただけ)。
ポイントはreact-native-webというBabelプラグインの存在です。これが何者なのかは後述します。
それ以外はさほど特別な点は無く、流し読みしてOKです。

webpack.config.js
const path = require('path');
const webpack = require('webpack');

const appDirectory = path.resolve(__dirname, '../');

const babelLoaderConfiguration = {
  test: /\.js$/,
  include: [
    path.resolve(appDirectory, 'index.web.js'),
    path.resolve(appDirectory, 'src')
  ],
  use: {
    loader: 'babel-loader',
    options: {
      cacheDirectory: true,
      plugins: ['react-native-web'], // ここがポイント
      presets: ['react-native']
    }
  }
};

module.exports = {
  entry: path.resolve(appDirectory, 'index.web.js'),
  output: {
    filename: 'bundle.web.js',
    path: path.resolve(appDirectory, 'dist')
  },
  module: {
    rules: [
      babelLoaderConfiguration
    ]
  }
}

ブラウザ上で実行

先ほどのWebpack設定のもと、bundle.web.jsを生成してindex.htmlと同じ位置に置き、index.htmlをブラウザで開けばHello worldの画面が表示されます(開発時はwebpack-dev-serverなどを使うとお手軽)。

image.png

RNアプリと同じ画面をブラウザで表示することができました。
HTMLの中身を見てみると、divタグ(RN Webによって生成されたclass属性がみっちり指定されている)が何層かあって、"Hello, world!"が表示されています。

RN Webが動く仕組み

RNにView、Textなどのコンポーネントが用意されているのと同様に、RN Webにも同名のコンポーネントが用意されています(一部除く)。
そしてRN WebのViewなどが、内部ではdivなどの具体的なHTMLタグを作るように実装されています。
こうすることで、RNのコンポーネントをWeb用にも使いまわせるようになっています。

ところで先ほどの例のJavaScriptコードでは、importのfromがreact-nativeになっており、react-native-webが登場していません。
なんでこれで動くかというと、先ほど触れたreact-native-webというBabelプラグイン(npm名はbabel-plugin-react-native-web)のおかげなのです。
このプラグインが、ざっくり言うとimportのfromをreact-nativeからreact-native-webに変換しています。

まとめると、以下の様にしてRN Webは動いています。

  1. Babelプラグインによって、RNをimportしている箇所が、RN Webをimportするように差し替わる。
  2. RN Webのコンポーネント(View、Text)などが、最終的にdivなどのHTMLタグとなる。

参考記事リンク

react-native-web はクロスプラットフォームを加速するのか


  1. GitHubのプロフィールなどを見ると、RN Webの作者であるNicolasさん自身が、Twitter Liteに携わっていました。 

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.