三步將 React Native 項目運行在 Web 瀏覽器上面

kitu3842 8年前發布 | 71K 次閱讀 前端技術 React Native

來自: http://taobaofed.org/blog/2016/03/11/react-web-intro/

React Native 的出現,讓前端工程師擁有了使用 JavaScript 編寫原生 APP 的能力。相比之前的 Web app 來說,對于性能和用戶體驗提升了非常多。

但是 React Native 的代碼只兼容兩個平臺(iOS 和 Android),并沒有兼容 Web 端訪問。這里是因為 非死book 開發人員認為 Web 端天生兼容性就巨麻煩,而且平臺差異性是注定存在而且也要保留的,所以 React Native 的目標是 Learn once, write anywhere ,而不是 Write once, run anywhere 。

然而 Write once, run anywhere 又是一個剛需。從產品還是用戶的角度試想一下,APP 的安裝成本還是很高的,如何讓用戶馬上體驗到你產品的功能再決定是否要安裝?此外,尤其是重要的產品,除了 APP 客戶端之外,還要有一套兜底的 Web 端以便用戶在某些特殊場景下使用。React Native 可以讓你寫一份代碼跑在兩個平臺,但是你卻還要再寫一份 Web 的一模一樣的應用。就顯得十分蛋疼了。

于是 React web 就出現了。

React Web 介紹

簡單的一句話描述 React Web 就是:它幫你把 React Native 的組件做了一個 Web 端的實現,并提供相關打包工具,讓你可以直接打包出一份可以跑在 Web 端的代碼。

將 React Native 應用創建一個 Web 版的幾個步驟

為了重點突出轉換過程,這里使用 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 :)。

第一步:安裝 React web 并進行相關配置

這一步操作主要是安裝 react-web 包以及相關依賴,并配置 webpack 打包腳本等。

為了簡化這一步操作,我們開發了命令行工具 react-web-cli 只需要執行兩行命令即可。同時命令行工具還支持啟動調試服務器、打包等功能,在后面介紹。

安裝 cli 工具:

npm install react-web-cli -g

</div>

安裝配置 React web 等:

react-web init <當前項目目錄>

</div>

執行完成之后,會在你項目目錄下面 npm install 相關庫,并自動創建 web/webpack.config.js 文件,里面有一份寫好的配置。此時目錄結構為:

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

</div>

第二步:添加入口文件并進行相關配置

每個項目都需要有一個入口文件,通常用來引入調用其他組件并初始化項目,比如 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'),
  },
};

</div>

然后我們創建 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
  });
}

</div>

然后在最上面 require 部分需要引入 Platform 組件。這樣配置部分就已經處理完成了,執行 react-web start 命令即可啟動調試服務器啦!

可以隨便修改試下,跟 React Native 模擬器里面的體驗幾乎一樣。

第三步:測試并打包 Web 版本代碼

當你修改開發完,并對 Web 端也測試好了,就可以打包發布了。 react-web-cli 工具打包的命令是:

react-web bundle

</div>

打包完成后,文件會存放在 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 版本開始,專門分成兩個庫 react 和 react-dom ,其實是把對瀏覽器平臺的特殊處理剝離了出來,單獨變成了 react-dom 庫。

React Native 比較特殊的地方在于,組件最底層的實現是 Native 的實現,所以就不支持 span 、 div 等標簽。而動畫等,也是直接調用 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'],
},

</div>

這樣在打包時,但凡 require('react-native') 的地方全都用 react-web 包替換,而 react-web 的 module.exports 與 react-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');

</div>

而不是以前那樣:

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

</div>

這樣 webpack 打包時,對于前者,只會把那一個組件內容打包進來,因此可以減小體積、提升性能。這是怎么實現的呢?

加載了插件的 webpack 打包時,會先掃描所有組件并讀取組件頭部 @providesModule 的信息(比如 Text 組件的信息 ),然后當其他文件中 require 了這個組件名稱,就會自動定位到這個文件進行打包。同時還可以區分平臺,即便是同一個名字,打包時會區分平臺去打包對應的文件(根據 index.xxx.js 的命名規則確定文件)。

一些存在的問題

在 Web 端兼容性是個非常麻煩頭疼的事情,React Web 已經盡力幫你抹平兼容性問題和代碼差異,盡可能的讓你減少改動就可以創建 Web 版本的應用。但受限于 Web 端的一些固有限制(比如請求跨域),不可避免的就會有一些需要你改代碼的地方。

為此,可以通過 if (Platform.OS == 'web') 的方式判斷目標平臺,并針對性的做一些平臺兼容性處理。同樣的,也可以將 web 替換為 ios 或者 android 判斷其他平臺。

React web 官方文檔上面已經列出來了一些平臺差異問題 ,這里就不再贅述了。

歡迎踴躍嘗試,遇到問題可以隨時提 Issue 哦:)

</div>

 本文由用戶 kitu3842 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!