Help us understand the problem. What is going on with this article?

React Native for WebでHello world

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)であり、モバイル端末上のブラウザ向けに作られています。
今後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に携わっていました。 

nacam403
CureAppで色々やっているエンジニアリングマネージャーです。
https://cureapp.co.jp/engineers.html
cureapp
「治療アプリ」という診療現場における新しい疾患治療ツールを開発するプログラム医療機器ベンチャーです。
https://cureapp.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした