2021.04.08

React Native とFlutter におけるWeb アプリ開発

こんにちは。次世代システム研究室のT.M です。

はじめに

React NativeFlutter はクロスプラットフォームのアプリケーションフレームワークとして有名です。クロスプラットフォームのフレームワークとしてどちらが良いかを判断するために、前回の記事では、React Native とFlutter のandroid アプリにおけるパフォーマンス比較を行いました。そもそも、クロスプラットフォームは、android とiOS だけでなく、Web やWindows / MacOS を指す言葉です。そこで、本稿ではWeb アプリケーションの開発について比較を行います。

React Native

React Native では、標準ではandroid / iOS アプリの開発しか行うことができません。同一ソースコードでのWeb アプリを開発するためには、React Native for Web を利用する必要があります。

React Native for Web では、以下のライブラリが必要です。
npm install react-dom react-native-web
また、React Native のコードをWeb 用のコードにトランスパイルする必要があります。今回はwebpack を利用します。
npm install --save-dev babel-loader url-loader webpack webpack-cli babel-plugin-react-native-web
Web アプリのためには、index.html が必要です。web というディレクトリを用意して、そこに配置します。
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello, world!</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="bundle.web.js"></script>
  </body>
</html>
android / iOS アプリには、index.js が必要であり、そこでルートとなるApp コンポーネントの登録をします。Web アプリでは、同様にApp コンポーネントの登録が必要ですが、それだけではなく、index.html のroot エレメントにそのコンポーネントを紐づける必要があります。この紐付けは、Web のみに必要なものなので、Web だけで処理をするindex.web.js を作成します。
import React from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';
import App from './src/App'

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

AppRegistry.runApplication('App', { rootTag: document.getElementById('root') });
アプリケーションのソースコードの変更は以上になります。残りは、webpack などのツールを利用して、トランスパイルを行い、Web アプリ対応をしていきます。
webpack のconfig である、以下のようなwebpack.config.js をweb/ に配置します。
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'),
    path.resolve(appDirectory, 'node_modules/react-native-uncompiled')
  ],
  use: {
    loader: 'babel-loader',
    options: {
      cacheDirectory: true,
      presets: ['module:metro-react-native-babel-preset'],
      plugins: ['react-native-web']
    }
  }
};

const imageLoaderConfiguration = {
  test: /\.(gif|jpe?g|png|svg)$/,
  use: {
    loader: 'url-loader',
    options: {
      name: '[name].[ext]'
    }
  }
};

module.exports = {
  entry: [
    path.resolve(appDirectory, 'index.web.js')
  ],

  output: {
    filename: 'bundle.web.js',
    path: path.resolve(appDirectory, 'web')
  },

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

  resolve: {
    alias: {
      'react-native$': 'react-native-web'
    },
    extensions: [ '.web.js', '.js' ]
  }
}
babel のreact-native-web のプラグインを呼び出し、react-native のimport 文をreact-native-web に書き換える、ということをしています。

上記設定にしたがって、bundle.web.js を作成します。
./node_modules/.bin/webpack --config web/webpack.config.js

Flutter

Flutter では、標準でandroid / iOS アプリだけではなく、Web アプリの開発をすることができます。Web アプリは安定版ではなかったのですが、先日のFlutter2 のバージョンアップで安定版となりました。

Flutter でのWeb アプリの作成のためには、特別な設定などは不要です。
flutter build web
上記コマンドで、main.dart が作成されるので、web/index.html を呼び出せば、Web アプリが実行されます。

Flutter のWeb アプリでは、html レンダラーとcanvaskit レンダラーがあります。html レンダラーはデータサイズが小さいが、パフォーマンスが低い、という問題があります。反対に、canvaskit レンダラーはパフォーマンスが高いが、データサイズが大きい、という問題があります。デフォルトでは、PC の場合、canvaskit レンダラーを、モバイルの場合、html レンダラーを選択するようになっています。

PC でもhtml レンダラーを指定したい場合は、以下のコマンドでビルドをすることができます。
flutter build web --web-renderer html
また、モバイルでcanvaskit レンダラーを指定したい場合、以下のコマンドでビルドをすることができます。
flutter build web --web-renderer canvaskit
各ビルドでは、以下のような通信が行われます。canvaskit レンダラーの時だけ、2.5MB のcanvaskit.wasm というファイルをダウンロードしていることがわかります。

まとめ

React Native およびFlutter のWeb アプリケーションに関する比較を行いました。Flutter の方が圧倒的に容易にWeb アプリケーションの開発をすることができました。android / iOS だけではなく、Web も一つのコードで開発を行うのならば、Flutter の方が良いと考えます。

React Native はandroid / iOS のためだけのフレームワークであり、Flutter はクロスプラットフォームのアプリ開発のためのフレームワークとして、進んでいることを実感しました。今後の開発にとても期待しています。

Pocket

関連記事