React Native for Web(npmパッケージ名は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)であり、モバイル端末上のブラウザ向けに作られています。
今後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用のものを別途用意します。どちらも数行程度のものです。
<!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>
// いたって普通の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;
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です。
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などを使うとお手軽)。
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は動いています。
- Babelプラグインによって、RNをimportしている箇所が、RN Webをimportするように置換される。
- RN Webのコンポーネント(View、Text)などが、最終的にdivなどのHTMLタグとなる。
参考記事リンク
react-native-web はクロスプラットフォームを加速するのか
-
GitHubのプロフィールなどを見ると、RN Webの作者であるNicolasさん自身が、Twitter Liteに携わっていました。 ↩