126
88

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

React Native for WebでHello world

Last updated at Posted at 2018-03-26

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用のものを別途用意します。どちらも数行程度のものです。

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に携わっていました。

126
88
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
126
88

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?