本文讲的是一种在 React Web 和 React Native 共享应用程序逻辑,但在每个平台保持独立的渲染特性的方法。示例应用程序可以在 这里 上找到。
应用
React Native:
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
start
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活动QQ群:555973817,我们将不定期做技术分享活动。
请转载的亲们,注意转发时注意自带作者信息一栏并本自媒体公号:MaxLeap,尊重原创作者及译者的劳动成果~ 谢谢配合~
原文链接:/react/306659.html