React Native 的出現,讓前端工程師擁有了使用 JavaScript 編寫原生 APP 的能力。相比以前的 Web app 來講,對於性能和用戶體驗提高了很是多。javascript
可是 React Native 的代碼只兼容兩個平臺(iOS 和 Android),並無兼容 Web 端訪問。這裏是由於 Facebook 開發人員認爲 Web 端天生兼容性就巨麻煩,並且平臺差別性是註定存在並且也要保留的,因此 React Native 的目標是 Learn once, write anywhere ,而不是 Write once, run anywhere 。 html
然而 Write once, run anywhere 又是一個剛需。從產品仍是用戶的角度試想一下,APP 的安裝成本仍是很高的,如何讓用戶立刻體驗到你產品的功能再決定是否要安裝?此外,尤爲是重要的產品,除了 APP 客戶端以外,還要有一套兜底的 Web 端以便用戶在某些特殊場景下使用。React Native 可讓你寫一份代碼跑在兩個平臺,可是你卻還要再寫一份 Web 的如出一轍的應用。就顯得十分蛋疼了。 前端
因而 React web 就出現了。java
簡單的一句話描述 React Web 就是:它幫你把 React Native 的組件作了一個 Web 端的實現,並提供相關打包工具,讓你能夠直接打包出一份能夠跑在 Web 端的代碼。react
爲了重點突出轉換過程,這裏使用 React Native init 的最簡 Demo 來作實驗(名字叫 Awes 代碼在 https://github.com/taobaofed/demo/tree/gh-pages/react-web )。 React Web 已經把 React Native 比較複雜的 UI Explorer Demo 跑起來了 ,因此只要你的代碼能跑在 iOS 或者 Android 上面,你基本不用擔憂有什麼組件上的問題。固然若是有,能夠立刻提 Issue 過來,咱們有一個小組在支持 React web :)。 android
這一步操做主要是安裝 react-web 包以及相關依賴,並配置 webpack 打包腳本等。 webpack
爲了簡化這一步操做,咱們開發了命令行工具 react-web-cli 只須要執行兩行命令便可。同時命令行工具還支持啓動調試服務器、打包等功能,在後面介紹。 ios
安裝 cli 工具:git
npm install react-web-cli -g
安裝配置 React web 等:github
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 端也測試好了,就能夠打包發佈了。 react-web-cli 工具打包的命令是:
react-web bundle
打包完成後,文件會存放在 web/output/ 目錄下面,能夠直接打開 index.html (若是 app 有請求操做,須要起本地服務器查看),再檢查一下就能夠發佈了。
好奇的同窗看到這裏可能會有一些疑問,上面命令行工具的一些命令作了什麼事情?爲何 React web 將 React Native 代碼打包出一份用在 Web 端的代碼?React web 安全可靠嗎,裏面都是什麼東西?
這裏簡單的介紹下 React web 的實現原理和上面步驟實際作的事情。
React 將代碼與平臺環境分離,多了一層,這樣開發者能夠在平臺環境層面作一些處理,使得一樣一份代碼適應更多的平臺環境等。
爲此, React v0.14.x 版本開始,專門分紅兩個庫 react 和 react-dom ,實際上是把對瀏覽器平臺的特殊處理剝離了出來,單獨變成了 react-dom 庫。
React Native 比較特殊的地方在於,組件最底層的實現是 Native 的實現,因此就不支持 span 、 div 等標籤。而動畫等,也是直接調用 Native 進行界面渲染。因此不支持 Web 端,可是絕大部分組件,都是能夠用 Web 技術進行模擬實現。動畫能夠用 CSS3 、基礎元素能夠用同等 HTML 標籤模擬、佈局以及兼容性問題能夠用 CSS 來處理,因此 React web 只須要把 React Native 的組件用 Web 技術從新實現一遍,藉助 React 這一層,便可實現一份代碼運行在多個平臺上面。
舉一個很是簡單的例子, Text 組件:
在 UI Explorer demo 中能跑起來的 React Native 組件,你均可以放心的用。
作出了兼容 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-web 的 module.exports 與 react-native 的保持一致便可讓代碼不替換也能夠工做。
此外配合插件還能夠實現另一種引入方法,請看下面。
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 官方文檔上面已經列出來了一些平臺差別問題 ,這裏就再也不贅述了。
連接:
三步將 React Native 項目運行在 Web 瀏覽器上面