React Nativeで作成したアプリをReact Native for Web化する方法を解説します。
React Native for Webについてはこちらで解説していますので、ご参照ください。
React Native for Webとは何かについて解説
- React NativeアプリをReact Native for Web化する方法がわかる
- react-native-webの基本の使い方がわかる
- React 17.0.2
- React Native 0.65.1
- react-native-web 0.17.5
React Nativeアプリを作成する
まずは、React Nativeでアプリを作成しましょう。
モグモグさん
すでに作成済みの方はスキップでOKです!
【ReactNative入門 】Macで開発環境を作って始める
react-native-web関連パッケージを追加
続いて、react-native-webとwebpack等のパッケージを追加しましょう。
$ yarn add react-native-web
// npmの方
$ npm install react-native-web
$ yarn add -D babel-plugin-react-native-web webpack webpack-cli webpack-dev-server html-webpack-plugin react-dom babel-loader url-loader @svgr/webpack
// npmの方
$ npm install --save-dev babel-plugin-react-native-web webpack webpack-cli webpack-dev-server html-webpack-plugin react-dom babel-loader url-loader @svgr/webpack
詳細なドキュメントはこちらを参照ください。
index.htmlファイルを追加
ルートファイルとなるindex.html
を追加します。
ディレクトリは任意ですが、/web/index.html
として作成しています。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>RN Web example</title>
<style>
#app-root {
display: flex;
flex: 1 1 100%;
height: 100vh;
}
</style>
</head>
<body>
<div id="app-root"></div>
</body>
</html>
index.web.jsを作成
すでに、index.js
があると思いますが、React Native for Web用にindex.web.js
を作成します。
プロジェクトルートに配置しましょう。
import React from 'react';
import {AppRegistry} from 'react-native';
import App from './App';
AppRegistry.registerComponent('App', () => App);
AppRegistry.runApplication('App', {
rootTag: document.getElementById('app-root'),
});
webpack.config.jsを作成
続いてwebpack.config.js
を追加します。こちらもプロジェクトルートに配置です。
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const appDirectory = path.resolve(__dirname);
const {presets} = require(`${appDirectory}/babel.config.js`);
const compileNodeModules = [
// Add every react-native package that needs compiling
// e.g. 'react-native-gesture-handler',
].map(moduleName => path.resolve(appDirectory, `node_modules/${moduleName}`));
const babelLoaderConfiguration = {
test: /\.js$|tsx?$/,
// Add every directory that needs to be compiled by Babel during the build.
include: [
path.resolve(appDirectory, 'index.web.js'),
path.resolve(appDirectory, 'App.js'), // TypeScriptを使用している場合はApp.tsx
path.resolve(appDirectory, 'src'),
...compileNodeModules,
],
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets,
plugins: ['react-native-web'],
},
},
};
const svgLoaderConfiguration = {
test: /\.svg$/,
use: [
{
loader: '@svgr/webpack',
},
],
};
const imageLoaderConfiguration = {
test: /\.(gif|jpe?g|png)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
},
},
};
module.exports = {
entry: {
app: path.join(appDirectory, 'index.web.js'),
},
output: {
path: path.resolve(appDirectory, 'dist'),
filename: 'bundle.web.js',
},
resolve: {
extensions: ['.web.tsx', '.web.ts', '.tsx', '.ts', '.web.js', '.js'],
alias: {
'react-native$': 'react-native-web',
},
},
module: {
rules: [
babelLoaderConfiguration,
imageLoaderConfiguration,
svgLoaderConfiguration,
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(appDirectory, './web/index.html'),
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
// See: https://github.com/necolas/react-native-web/issues/349
__DEV__: JSON.stringify(true),
}),
],
};
ポイント
大きなポイントは、alias
の箇所です。
alias: {
'react-native$': 'react-native-web',
},
これで、import {} from react-native
の箇所をreact-native-web
に変更しています。
imageLoaderConfiguration
とsvgLoaderConfiguration
は必須ではないです。
compileNodeModules
はこの例では空ですが、必要に応じて利用しているパッケージを追加してください。
App.js (App.tsx)を修正
例を簡単にするために余計なApp.js(もしくはApp.tsx)
をシンプルにします。
モグモグさん
興味がある方や既存のアプリに組み込んでいる方は、いろいろなUIがReact Native for Webでどうなるか試してみてください!
import React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
Text,
useColorScheme,
View,
} from 'react-native';
const App = () => {
const isDarkMode = useColorScheme() === 'dark';
return (
<SafeAreaView>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View>
<Text>Hello react native web</Text>
</View>
</ScrollView>
</SafeAreaView>
);
};
export default App;
package.jsonにScriptを追加
react-native-web
をビルドするためにスクリプトを追加します。
{
...
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"web-build": "rm -rf dist/ && webpack --mode=production --config webpack.config.js",
"web": "webpack serve --mode=development --config webpack.config.js"
},
...
}
追加されたスクリプト
$ yarn web / npm web
ローカルサーバーが立ち上がります。
$ yarn web-build / npm web-build
本番用にビルドされ、dist
ディレクトリにファイルが配置されます。
ビルド
最後に動作確認をしましょう。
$ yarn web / npm web
localhost:8080を開くと画面が表示されます。
まとめ
モグモグさん
お疲れ様でした!
React Nativeを開発しつつ、Webにも適用できる可能性があるのでユースケースによっては非常に便利だと思います。
画像や他のComponentsなどのUIを試してみてください。
ReactアプリをReact Native for Web化する方法についてはこちらで解説しています。
おそらく、webpack.config.jsのbabelLoaderConfigurationのところに、1行追加した方が良いように思いますが、いかがでしょうか?
const babelLoaderConfiguration = {
test: /\.js$|tsx?$/,
// Add every directory that needs to be compiled by Babel during the build.
include: [
path.resolve(appDirectory, ‘index.web.js’),
path.resolve(appDirectory, ‘App.tsx’),
path.resolve(appDirectory, ‘App.js’), // この行追加
path.resolve(appDirectory, ‘src’),
…compileNodeModules,
],
ご指摘ありがとうございます。
TypeScriptを使用する形と混合しておりましたので、該当箇所を修正して更新いたしました。
ありがとうございます!