(一) 前言
React Native 三端(Web、iOS、Android)同构是指在使用 React Native封装组件 的代码下,让其在浏览器中运行出和在 React Native 环境下一样的页面。
对于使用 React Native 开发的页面,如果又单独为 Web 平台重复写一份代码代价是极大的,而 React Native 三端同构能以最小代价做到一份代码三端复用。并且也需要提供Web和App各自能独立使用的代码方案。
(二)问题
我们知道react-native主流路由react-navigation
路由机制类似于栈,当我们每次push一个新路由,他会加载在路由栈内,直到页面退出,才从栈中移除。而web路由代表的react-router-dom,其实会只加载一个路由页面,switch都会默认只匹配一个路由。
所以,针对两种不同路由机制,最好方案是,使用不同路由配置,这样尽量将组件封装成通用,以适配两端应用。
(三) 创建react-native 项目工程
直接以cli方式新建 react-native init react_native_web_demo, 此时我们已经有个rn项目工程,
然后替换默认[email protected] 到 @babel/core@7
$ yarn remove babel-core
$ yarn add @babel/core --dev
$ cd ios && pod init
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
platform :ios, '9.0'
target 'react_native_web_demo' do
# this is very important to have!
rn_path = '../node_modules/react-native'
pod 'yoga', path: "#{rn_path}/ReactCommon/yoga/yoga.podspec"
pod 'React', path: rn_path, subspecs: [
'Core',
'RCTActionSheet',
'RCTAnimation',
'RCTGeolocation',
'RCTImage',
'RCTLinkingIOS',
'RCTNetwork',
'RCTSettings',
'RCTText',
'RCTVibration',
'RCTWebSocket'
]
end
# very important to have, unless you removed React dependencies for Libraries
# and you rely on Cocoapods to manage it
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == "React"
target.remove_from_project
end
end
end
"setup": "yarn install && cd ios && pod install && cd .. && react-native link",
"android": "react-native run-android",
"iPhoneX": "react-native run-ios --simulator 'iPhone X'",
"clear": "watchman watch-del-all && rm -rf $TMPDIR/react-* && rm -rf node_modules/ && yarn install && yarn start -- --reset-cache",
$ yarn setup
$ yarn start
$ yarn iPhoneX
$ yarn android
这是一个初始化rn项目设置完成
(四) 新增web配置文件
$ yarn add react-dom react-native-web react-art
$ yarn add webpack-cli webpack-dev-server webpack html-webpack-plugin babel-loader url-loader --dev
react-native-web
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const appDir = path.resolve(__dirname, '../');
module.exports = {
entry: {
bundle: path.resolve(appDir, 'index.web'),
},
output: {
filename: 'bundle.web.js',
path: path.resolve(appDir, 'dist'),
},
module: {
rules: [{
test: /\.(js|jsx)$/,
include: [
path.resolve(appDir, 'src'),
path.resolve(appDir, 'react-native-web'),
],
use: [{
loader: 'babel-loader',
options: {
cacheDirectory: true,
babelrc: false,
},
}],
},
{
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[ext]',
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(appDir, 'public/index.html'),
}),
],
resolve: {
extensions: ['.js', '.json', '.android.js', '.ios.js'],
alias: {
'react-native$': 'react-native-web',
},
modules: ['web_modules', 'node_modules'],
},
};
"web": "NODE_ENV=development webpack-dev-server -d --config ./web-build/webpack.config.js --inline --hot --colors",
"build": "NODE_ENV=production webpack -p --config ./web-build/webpack.config.js"
import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
import App from './src/App';
AppRegistry.registerComponent(appName, () => App);
AppRegistry.runApplication(appName, {
rootTag: document.getElementById('root')
});
最后,在根文件下新建src,将App.js 移动到src里面。这样目前就可以使用web开发命令了,
并修改App.js
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
web: 'This is web',
});
(五) 结语
在第四步中,我们虽然有一个简单的web开发环境,但是就目前真正开发还有一段距离,比如