本文讲的是一种在 React Web 和 React Native 共享应用程序逻辑,但在每个平台保持独立的渲染特性的方法。示例应用程序可以在 这里 上找到。
应用
React Native:
React NativeReact Web:
React Web这个应用程序本身是一个非常简单的 Hello World 应用程序。它不仅会显示"Hello World",而且当你点击它时,颜色会从红色改变成蓝色!哇!
动机
不管是 Web 还是 Mobile, React 都是很棒的。 所以为什么不在你的两个实现之间共享代码?
React Native 并不是为了实现"一次编写,到处运行"的框架。Facebook 一直称之为"一次学习,到处编写"的框架-这就是说你需要为你的平台进行定制。即便这样,你仍然可以在应用程序之间共享大量的逻辑。
在这篇文章我将讨论如何吸取这两种思想的优点,达到一个融合共通的状态。在坚持每个平台定制呈现方式的同时,我们会共享所有应用程序逻辑。
我们会假设你已经具备以下知识: React, React Native 和 Redux。
初始安装 + 目录概述
首先我们需要初始化我们的项目。我们要按照取决于 Facebook 的入门指南 中的步骤 ︰
$ npm install -g react-native-cli
$ react-native init ReactNativeWebHelloWorld
现在,我们的目录看起来是这样:
ReactNativeWebHelloWorld
|-- android
|-- ios
|-- node_modules
|-- .flowconfig
|-- .gitignore
|-- .watchmanconfig
|-- index.android.js
|-- index.ios.js
+-- package.json
这包含我们需要为我们的 iOS 和 Android 应用程序的所有文件。现在,我们创建下列目录和文件来配置和运行我们的 Web 应用程序 ︰
ReactNativeWebHelloWorld
+-- web
|-- public
| +-- index.html
+-- webpack
|-- web.dev.config.js
+-- web.prod.config.js
index.html, web.dev.config.js 和 web.prod.config.js 的内容都可以在 GitHub 上找到 - 我们会更晚些时候分析他们 (但如果你现在就想点击他们,那就点吧!)。
当我们改好目录结构后,接着安装依赖项:
$ npm install --save babel babel-polyfill ...
$ npm install --save-dev autoprefixer babel-core ...
依赖项的完整列表,请查阅 package.json。
最后,我们为应用程序初始化的所有文件。我们将会做一个相当通用的 React/Redux 应用程序 ︰
ReactNativeWebHelloWorld
+-- app
|-- actions
|-- constants
|-- reducers
|-- store
|-- native
| |-- components
| |-- containers
| +-- style
+-- web
|-- components
|-- containers
+-- style
这时候就已经相当清晰了。我们为不同的应用程序有三个不同的入口点 ︰ index.ios.js、 index.android.js 和 app/web/index.js。IOS 和 Android 的入口文件从app/native加载组件和容器, web 入口文件从 app/web 加载组件和容器。这给我们带来我们了:
应用程序代码结构
我不会去分析所有的文件,但要指出一些 Web 和 Native 的关键差异。
让我们看看应用程序入口文件,index.ios.js 看起来是这样:
import React, { Component, AppRegistry } from 'react-native';
import Root from './app/native/containers/Root';
import configureStore from './app/store/configureStore.prod.js';
const store = configureStore();
class ReactNativeHelloWorld extends Component {
render() {
return (
<Root store={store} />
);
}
}
AppRegistry.registerComponent('ReactNativeWebHelloWorld', () => ReactNativeHelloWorld);
app/web/index.js 是这样:
import React from 'react';
import { render } from 'react-dom';
import Root from './containers/Root';
import configureStore from '../store/configureStore';
// load our css
require('./styles/style.less');
const store = configureStore();
const rootElement = document.getElementById('root');
render( <Root store={store} />, rootElement );
所以我们关心的区别是什么?
需要注意的主要事情是顶级组件如何渲染他们自己。在 Native 部分,我们必须在 app registry 中显式定义,而在 web 中我们可以使用 ReactDom,把Root直接渲染在根元素中。
那是什么意思!?
基本上,React Native 和 React Web 在实例化顶级组件上有着不同的方式。
正是这些差异,要求我们坚持每个平台独特的渲染逻辑。
让我们也检查两种情况下HelloWorld组件的render方法。在 Native 中,它看起来是这样:
render() {
const { onPress, color } = this.props;
const style = StyleSheet.create({
helloWorld: { color: color, textAlign: 'center' }
});
return (
<View>
<Text onPress={onPress} style={style.helloWorld}>Hello World</Text>
</View>
);
}
Web 中,它看起来是这样:
render() {
const { onClick, color } = this.props;
return (
<div className="hello-world" onClick={onClick} style={ {color: color} }>Hello World</div>
);
}
这就又证明了为什么我们需要坚持每个平台独特的渲染逻辑。 React Native 处理 <View>s
和<Text>s
,而 web 处理<div>s
和<span>s
。不仅如此,而且事件系统和样式系统都不一样。
我们也来看看什么他们共享什么...
实例化 HelloWorld 组件时, app/native/containers/App.js 的定义:
<HelloWorld
onPress={() => dispatch(toggleColor())}
color={color}
/>
app/web/containers/App.js 的定义:
<HelloWorld
onClick={() => dispatch(toggleColor())}
color={color}
/>
这两个dispatch方法都是从react-redux导入的,toggleColor也是从相同的actions文件导入。只的渲染方式不同! 应用程序逻辑是共享的! 这是一个大的飞跃!
相比一个接一个的比较相似性和差异性,我们去看看在package.json中定义的脚本,让你可以生成并运行这个个程序......
配置的脚本
在开发和生产环境运行
package.json有 8 定义的脚本:
- start
- ios-bundle
- ios-dev-bundle
- android-bundle
- android-dev-bundle
- web-bundle
- web-dev
<b>start</b>
start 是用来打包和运行 native 程序的。当你打开要么 xcode 项目或 android studio 并点击"run"时,它通过start命令启动一个 node 服务器。每次你改了 JavaScript,你不需要重建和重新编译你的应用程序,你只需刷新,这些更改将奇迹般地生效。由于本文不是 React Native 引导,我不会介绍更多信息 - 你可以去 React Native Getting Started 查看。
内置
通过ios-bundle、 ios-dev-bundle、 android-bundle和android-dev-bundle,此脚本生成 JavaScript bundle (是否压缩取决于你是否开启了dev 选项),并将其放置在恰当的地方,以使可以在设备上本地运行。另外,你可以去 React Native Getting Started 查看关于本地运行的更多信息。
web
web-dev 命令在 3001 端口 打开了一个webpack 服务器上,它利用热加载和 redux-time-machine-magic 的特性,以使你可以进行还原和重放你的操作。
web-bundle 命令创建压缩版的 JavaScript bundle(也压缩 css),并将其放置在web/public的index.html 中,你可以搭配任何静态文件服务器的使用它。
清除缓存
有时候,当 React Native 工作时,你会确信你已经改变了一些东西,但它(缓存)仍然会导致你的 App 出问题! 哦不! 我们该怎么办!
npm run clear-cache
进一步的配置
Webpack 把 PLATFORM_ENV环境变量设置为了 web 。你可以使用此环境变量有条件地加载不同的文件,取决于你在构建 Web 还是 Native 程序。举个例子 - 你可以基于此抽象出本地存储机制之间的区别。
你得到什么?
嗯,首先,这是很有趣的事情。我们有时候会单纯为了精神愉悦做某些事情。但如果这对你来说还不够,找出你的应用程序中的 action/reducer/request 层面的 bug,你可以一次性搞定他们,而不需要为你的 Web 和 Native 程序分别处理。
它还能加速你在多平台上的开发速度。经过2天的努力,我就能让我的一个 react/redux 的应用程序功能完整的运行在手机上了。
作者信息 原文作者:Jon Kaufman
原文链接:http://jkaufman.io/react-web-native-codesharing/
翻译自 MaxLeap团队_UX成员:Jason Huang
中文翻译首发链接:https://blog.maxleap.cn/archives/866
请转载的亲们,转发时请注意自带作者信息一栏并本自媒体公号:MaxLeap,尊重原创作者及译者的劳动成果~ 谢谢配合~
网友评论