在 React Web 和 原生 App 中共享代码

作者: 商领云 | 来源:发表于2016-06-07 12:01 被阅读386次

    本文讲的是一种在 React Web 和 React Native 共享应用程序逻辑,但在每个平台保持独立的渲染特性的方法。示例应用程序可以在 这里 上找到。

    应用

    React Native:

    React NativeReact Native

    React Web:

    React WebReact Web

    这个应用程序本身是一个非常简单的 Hello World 应用程序。它不仅会显示"Hello World",而且当你点击它时,颜色会从红色改变成蓝色!哇!

    动机

    不管是 Web 还是 Mobile, React 都是很棒的。 所以为什么不在你的两个实现之间共享代码?

    React Native 并不是为了实现"一次编写,到处运行"的框架。Facebook 一直称之为"一次学习,到处编写"的框架-这就是说你需要为你的平台进行定制。即便这样,你仍然可以在应用程序之间共享大量的逻辑。

    在这篇文章我将讨论如何吸取这两种思想的优点,达到一个融合共通的状态。在坚持每个平台定制呈现方式的同时,我们会共享所有应用程序逻辑。

    我们会假设你已经具备以下知识: React, React NativeRedux

    初始安装 + 目录概述

    首先我们需要初始化我们的项目。我们要按照取决于 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.htmlweb.dev.config.jsweb.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.jsindex.android.jsapp/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 定义的脚本:

    1. start
    2. ios-bundle
    3. ios-dev-bundle
    4. android-bundle
    5. android-dev-bundle
    6. web-bundle
    7. web-dev

    <b>start</b>

    start 是用来打包和运行 native 程序的。当你打开要么 xcode 项目或 android studio 并点击"run"时,它通过start命令启动一个 node 服务器。每次你改了 JavaScript,你不需要重建和重新编译你的应用程序,你只需刷新,这些更改将奇迹般地生效。由于本文不是 React Native 引导,我不会介绍更多信息 - 你可以去 React Native Getting Started 查看。

    内置

    通过ios-bundleios-dev-bundleandroid-bundleandroid-dev-bundle,此脚本生成 JavaScript bundle (是否压缩取决于你是否开启了dev 选项),并将其放置在恰当的地方,以使可以在设备上本地运行。另外,你可以去 React Native Getting Started 查看关于本地运行的更多信息。

    web

    web-dev 命令在 3001 端口 打开了一个webpack 服务器上,它利用热加载和 redux-time-machine-magic 的特性,以使你可以进行还原和重放你的操作。

    web-bundle 命令创建压缩版的 JavaScript bundle(也压缩 css),并将其放置在web/publicindex.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,尊重原创作者及译者的劳动成果~ 谢谢配合~

    相关文章

      网友评论

        本文标题:在 React Web 和 原生 App 中共享代码

        本文链接:https://www.haomeiwen.com/subject/iamddttx.html