[转] 三步将你的 React Native 项目运行在 Web 浏览器上面

React Native 的出现,让前端工程师拥有了使用 JavaScript 编写原生 APP 的能力。相比以前的 Web app 来讲,对于性能和用户体验提高了很是多。html

可是 React Native 的代码只兼容两个平台(iOS 和 Android),并无兼容 Web 端访问。这里是由于 Facebook 开发人员认为 Web 端天生兼容性就巨麻烦,并且平台差别性是注定存在并且也要保留的,因此 React Native 的目标是 Learn once, write anywhere,而不是 Write once, run anywhere前端

然而 Write once, run anywhere 又是一个刚需。从产品仍是用户的角度试想一下,APP 的安装成本仍是很高的,如何让用户立刻体验到你产品的功能再决定是否要安装?此外,尤为是重要的产品,除了 APP 客户端以外,还要有一套兜底的 Web 端以便用户在某些特殊场景下使用。React Native 可让你写一份代码跑在两个平台,可是你却还要再写一份 Web 的如出一辙的应用。就显得十分蛋疼了。react

因而 React web 就出现了。android

React Web 介绍

简单的一句话描述 React Web 就是:它帮你把 React Native 的组件作了一个 Web 端的实现,并提供相关打包工具,让你能够直接打包出一份能够跑在 Web 端的代码。webpack

将 React Native 应用建立一个 Web 版的几个步骤

为了重点突出转换过程,这里使用 React Native init 的最简 Demo 来作实验(名字叫 Awes 代码在 https://github.com/yujiangshui/react-web-example )。React Web 已经把 React Native 比较复杂的 UI Explorer Demo 跑起来了,因此只要你的代码能跑在 iOS 或者 Android 上面,你基本不用担忧有什么组件上的问题。固然若是有,能够立刻提 Issue 过来,咱们有一个小组在支持 React web :)。ios

第一步:安装 React web 并进行相关配置

这一步操做主要是安装 react-web 包以及相关依赖,并配置 webpack 打包脚本等。git

为了简化这一步操做,咱们开发了命令行工具 react-web-cli 只须要执行两行命令便可。同时命令行工具还支持启动调试服务器、打包等功能,在后面介绍。github

安装 cli 工具:web

npm install react-web-cli -g

安装配置 React web 等:npm

react-web init <当前项目目录>

执行完成以后,会在你项目目录下面 npm install 相关库,并自动建立 web/webpack.config.js 文件,里面有一份写好的配置。此时目录结构为:

.
├── README.md
├── android/
├── index.android.js
├── index.ios.js
├── ios/
├── package.json
└── web/
    └── webpack.config.js

第二步:添加入口文件并进行相关配置

每一个项目都须要有一个入口文件,一般用来引入调用其余组件并初始化项目,好比 index.ios.js 表示 iOS 平台上的该项目的入口文件。为了符合 React Native 的文件命名规范,咱们建立一个 index.web.js 做为入口文件,而且须要在 webpack 中指定该文件为入口文件。打开 web/webpack.config.js 文件,修改 config 变量:

var config = {
  paths: {
    src: path.join(ROOT_PATH, '.'),
    index: path.join(ROOT_PATH, 'index.web'),
  },
};

而后咱们建立 index.web.js 文件。这个文件其实跟 index.ios.js 很是像,只是略有不一样。主要区别在于:iOS 只须要 AppRegistry.registerComponent('Awes', () => Awes); 便可让 Xcode 的 Native 代码接收处理你的 JS 代码,而 Web 端是须要插入到 DOM 节点中才能够用。所以咱们须要在 index.web.js 最下面添加以下代码:

AppRegistry.registerComponent('Awes', () => Awes);
if (Platform.OS == 'web') {
  var app = document.createElement('div');
  document.body.appendChild(app);
  AppRegistry.runApplication('Awes', {
    rootTag: app
  });
}

而后在最上面 require 部分须要引入 Platform 组件。这样配置部分就已经处理完成了,执行 react-web start 命令便可启动调试服务器啦!

能够随便修改试下,跟 React Native 模拟器里面的体验几乎同样。

第三步:测试并打包 Web 版本代码

当你修改开发完,并对 Web 端也测试好了,就能够打包发布了。react-web-cli 工具打包的命令是:

react-web bundle

打包完成后,文件会存放在 web/output/ 目录下面,能够直接打开 index.html (若是 app 有请求操做,须要起本地服务器查看),再检查一下就能够发布了。

这个过程当中发生了什么?

好奇的同窗看到这里可能会有一些疑问,上面命令行工具的一些命令作了什么事情?为何 React web 将 React Native 代码打包出一份用在 Web 端的代码?React web 安全可靠吗,里面都是什么东西?

这里简单的介绍下 React web 的实现原理和上面步骤实际作的事情。

React Web 将 React Native 组件作了 Web 端的实现

React 将代码与平台环境分离,多了一层,这样开发者能够在平台环境层面作一些处理,使得一样一份代码适应更多的平台环境等。

  • 好比 react-canvas 按照 React 的语法书写代码,在平台环境层面作一些处理(将你 React 代码运行并用 canvas 渲染),而后实现特定目标(在移动端提升性能)。
  • React Native 中,一份代码能同时跑在 iOS 和 Android 上面,也是同样的道理。React Native 团队在对应平台的 Native app 上面作了一些处理,使其能够解析执行 React 语法的代码。
  • 还有同构(isomorphic)的应用,服务器端使用 React + Node.js 生成 HTML,客户端使用 React 获取进行客户端相关交互和功能,也是同样的道理。

为此, React v0.14.x 版本开始,专门分红两个库 reactreact-dom ,实际上是把对浏览器平台的特殊处理剥离了出来,单独变成了 react-dom 库。

React Native 比较特殊的地方在于,组件最底层的实现是 Native 的实现,因此就不支持 spandiv 等标签。而动画等,也是直接调用 Native 进行界面渲染。因此不支持 Web 端,可是绝大部分组件,都是能够用 Web 技术进行模拟实现。动画能够用 CSS3 、基础元素能够用同等 HTML 标签模拟、布局以及兼容性问题能够用 CSS 来处理,因此 React web 只须要把 React Native 的组件用 Web 技术从新实现一遍,借助 React 这一层,便可实现一份代码运行在多个平台上面。

举一个很是简单的例子,Text 组件:

  • React Native 的实现 是调用了不少 React Native 底层的代码实现的。
  • 对于 Web 端,输出一行文本使用 <span> 标签便可,因此 React web 的实现 就直接搞一个 <span> 标签,绑一些事件什么的就 OK 了。

UI Explorer demo 中能跑起来的 React Native 组件,你均可以放心的用。

webpack 帮你切换打包目标

作出了兼容 Web 端的组件,那打包的时候岂不是要把全部要打包的组件中的 require('react-native') 所有更换成 require('react-web')?否则怎么用的个人 Web 组件打包?

强大的 webpack 附带了 alias 配置项能够帮你解决这个问题:

resolve: {
  alias: {
    'react-native': 'react-web',
    'ReactNativeART': 'react-art',
  },
  extensions: ['', '.js', '.jsx'],
},

这样在打包时,但凡 require('react-native') 的地方全都用 react-web 包替换,而 react-webmodule.exportsreact-native 的保持一致便可让代码不替换也能够工做。

此外配合插件还能够实现另一种引入方法,请看下面。

经过 Haste 方法引入组件以提升性能

webpack 以及其余的支持 CommonJS 规范的打包工具,都会把文件中 require 的全部组件都打包在一块儿。对于 React Native 来讲代码体积大小可有可无,而在 Mobile web 来讲,就要稍微重要一些了。特别是若是你的项目只须要 Text 组件,但因为 require('react-web') 结果把全部的组件所有打包进来了,就比较伤感。

基于 webpack 插件,还能够用另外一种方式引入组件以解决这个问题,你能够叫它 Haste 方式。使用这种方式须要加载 webpack 插件 haste-resolver-webpack-plugin,默认的 webpack 配置已经帮你加载好了,你能够直接在组件里面这样用:

var Text = require('ReactText');

而不是之前那样:

var {Text} = require('react-native');

这样 webpack 打包时,对于前者,只会把那一个组件内容打包进来,所以能够减少体积、提高性能。这是怎么实现的呢?

加载了插件的 webpack 打包时,会先扫描全部组件并读取组件头部 @providesModule 的信息(好比 Text 组件的信息),而后当其余文件中 require 了这个组件名称,就会自动定位到这个文件进行打包。同时还能够区分平台,即使是同一个名字,打包时会区分平台去打包对应的文件(根据 index.xxx.js 的命名规则肯定文件)。

一些存在的问题

在 Web 端兼容性是个很是麻烦头疼的事情,React Web 已经尽力帮你抹平兼容性问题和代码差别,尽量的让你减小改动就能够建立 Web 版本的应用。但受限于 Web 端的一些固有限制(好比请求跨域),不可避免的就会有一些须要你改代码的地方。

为此,能够经过 if (Platform.OS == 'web') 的方式判断目标平台,并针对性的作一些平台兼容性处理。一样的,也能够将 web 替换为 ios 或者 android 判断其余平台。

React web 官方文档上面已经列出来了一些平台差别问题,这里就再也不赘述了。

欢迎踊跃尝试,遇到问题能够随时提 Issue 哦:)

相关文章
相关标签/搜索