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

React NativeをExpo無しで始めて、React Native for Webも動かすまで

More than 1 year has passed since last update.

Android / iOS共通で、サッとアプリケーションを作りたかったので、React Nativeを始めてみました。
また、PC上でも使いたかったので、Webでも使えるようにReact Native for Webもやってみました。
(そのまま、Electronでデスクトップアプリ化したい)

Expoというものがあり、それを利用するといろいろ楽にできるっぽいんですが、いくつか制約がついたりするようなので、避けました。
また、React Native for Webの方は、公式のREADMEやGetting Startedだけを見ても実装できず、困ったのでまとめました。

https://github.com/noboru-i/ReactNativeSample にあるのが、完成形とだいたい同じものです。

環境

macOS 10.13.2
Node.js 8.9.4

react 16.2.0
react-native 0.52.2
react-native-web 0.3.2

React Nativeをはじめる

https://facebook.github.io/react-native/docs/getting-started.html

"Quick Start"の方ではじめると、Expoが付いてくるようです。
今回は外したかったので、"Building Projects with Native Code"の方からはじめました。

Node.jsはnodebrewで最新をインストールしました。

続いて、getting startedに記載の通り、いろいろインストール。

brew install watchman
npm install -g react-native-cli

インストールしたCLIツールを使って、実際にプロジェクトを作成します。
("-"区切りのプロジェクト名にしようとしたら、エラーとなりました。。)

react-native init ReactNativeSample

動かしてみる

cd ReactNativeSample で、 react-native run-ios とすると、シミュレータとターミナルが起動して、サンプルアプリが起動します。

Androidについては、 ANDROID_HOME を環境変数で設定しておき、デバッグ可能な端末を繋いでおけば、iOSと同様にサンプルアプリが起動します。

さわってみる

ディレクトリ直下にある App.js を見ると、表示されているテキストがあります。
そこを修正して、reloadすると、変更が反映されます。iOSシミュレータの場合、Cmd+Rでreloadされます。
Android実機の場合は、端末を振って出てくるメニューから"Reload"を選択するとreloadされました。ちょっとめんどいですね。
共通部分に関しては、iOSシミュレータで開発を進めるのが良さそうです。

React Native for Webをはじめる

その前に、App.js を移動しておく

babelの設定的に、App.jssrc の下に移動しておきます。

mkdir src
mv App.js src

それに伴って、 index.jsimport App from './src/App'; に変えておきます。
react-native run-ios で、以前と同じ表示になることを確認しておきます。

React Native for Webの導入

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

READMEのInstallationにある通り、依存するパッケージをインストールします。

yarn add react react-dom react-native-web
yarn add --dev babel-plugin-react-native-web

続いて、 Getting startedに従って、いくつかのファイルを作成していきます。

まずは "Client-side rendering" にあるように、 index.web.js を作成します。

import App from './src/App';
import React from 'react';
import { AppRegistry } from 'react-native';

// register the app
AppRegistry.registerComponent('App', () => App);

AppRegistry.runApplication('App', {
  rootTag: document.getElementById('root')
});

続いて、entry pointが無いので、サンプルを探してきて、 web/public/index.html を下記のようにしました。
ここで作成したdivのidと、上のindex.web.jsのidを一致させる必要があります。

<!DOCTYPE html>
<html>
  <head>
    <title>ReactNativeSample</title>
    <meta name="viewport" content="width=device-width">
  </head>
  <body>
    <div id="root"></div>
    <script src="bundle.web.js"></script>
  </body>
</html>

また、"Getting started"の"Web packaging for existing React Native apps"にあるように、必要なパッケージのインストールを行います。

yarn add --dev babel-loader url-loader webpack webpack-dev-server

そして、 web/webpack.config.js を作成します。

const path = require('path');
const webpack = require('webpack');

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

// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
// published. If you depend on uncompiled packages they may cause webpack build
// errors. To fix this webpack can be configured to compile to the necessary
// `node_module`.
const babelLoaderConfiguration = {
  test: /\.js$/,
  // Add every directory that needs to be compiled by Babel during the build.
  include: [
    path.resolve(appDirectory, 'index.web.js'),
    path.resolve(appDirectory, 'src'),
    path.resolve(appDirectory, 'node_modules/react-native-uncompiled')
  ],
  use: {
    loader: 'babel-loader',
    options: {
      cacheDirectory: true,
      // Babel configuration (or use .babelrc)
      // This aliases 'react-native' to 'react-native-web' and includes only
      // the modules needed by the app.
      plugins: ['react-native-web'],
      // The 'react-native' preset is recommended to match React Native's packager
      presets: ['react-native']
    }
  }
};

// This is needed for webpack to import static images in JavaScript files.
const imageLoaderConfiguration = {
  test: /\.(gif|jpe?g|png|svg)$/,
  use: {
    loader: 'url-loader',
    options: {
      name: '[name].[ext]'
    }
  }
};

module.exports = {
  // your web-specific entry file
  entry: path.resolve(appDirectory, 'index.web.js'),

  // configures where the build ends up
  output: {
    filename: 'bundle.web.js',
    path: path.resolve(appDirectory, 'dist')
  },

  // ...the rest of your config

  module: {
    rules: [babelLoaderConfiguration, imageLoaderConfiguration]
  },

  plugins: [
    // `process.env.NODE_ENV === 'production'` must be `true` for production
    // builds to eliminate development checks and reduce build size. You may
    // wish to include additional optimizations.
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(
        process.env.NODE_ENV || 'development'
      ),
      __DEV__: process.env.NODE_ENV === 'production' || true
    })
  ],

  resolve: {
    // If you're working on a multi-platform React Native app, web-specific
    // module implementations should be written in files using the extension
    // `.web.js`.
    extensions: ['.web.js', '.js']
  }
};

その後、 ./node_modules/.bin/webpack-dev-server --content-base web/public/ -d --config ./web/webpack.config.js --inline --hot --colors を実行すると、webpackによるコンパイル、dev serverの起動が完了します。

http://localhost:8080/ にアクセスすることで、アプリと同じような画面の表示ができました。

付録

毎回実行コマンドを打つのがめんどうなので、 package.json にscriptを追加しました。

// ...
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "start:android": "react-native run-android",
    "start:ios": "react-native run-ios",
    "start:web":
      "webpack-dev-server --content-base web/public/ -d --config ./web/webpack.config.js --inline --hot --colors",
    "test": "jest"
  },
// ...

これにより、下記のように実行できるようになります。

yarn start:android
yarn start:ios
yarn start:web
Why do not you register as a user and use Qiita more conveniently?
  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
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