原文地址:https://yq.aliyun.com/articles/8184
React Native 的出現(xiàn)酝润,讓前端工程師擁有了使用 JavaScript 編寫原生 APP 的能力。相比之前的 Web app 來說璃弄,對于性能和用戶體驗提升了非常多要销。
但是 React Native 的代碼只兼容兩個平臺(iOS 和 Android),并沒有兼容 Web 端訪問夏块。這里是因為 Facebook 開發(fā)人員認為 Web 端天生兼容性就巨麻煩疏咐,而且平臺差異性是注定存在而且也要保留的,所以 React Native 的目標是 Learn once, write anywhere
脐供,而不是 Write once, run anywhere
浑塞。
然而 Write once, run anywhere
又是一個剛需。從產(chǎn)品還是用戶的角度試想一下政己,APP 的安裝成本還是很高的酌壕,如何讓用戶馬上體驗到你產(chǎn)品的功能再決定是否要安裝?此外歇由,尤其是重要的產(chǎn)品卵牍,除了 APP 客戶端之外,還要有一套兜底的 Web 端以便用戶在某些特殊場景下使用沦泌。React Native 可以讓你寫一份代碼跑在兩個平臺糊昙,但是你卻還要再寫一份 Web 的一模一樣的應(yīng)用。就顯得十分蛋疼了谢谦。
于是 React web 就出現(xiàn)了溅蛉。
React Web 介紹
簡單的一句話描述 React Web 就是:它幫你把 React Native 的組件做了一個 Web 端的實現(xiàn),并提供相關(guān)打包工具他宛,讓你可以直接打包出一份可以跑在 Web 端的代碼。
將 React Native 應(yīng)用創(chuàng)建一個 Web 版的幾個步驟
為了重點突出轉(zhuǎn)換過程欠气,這里使用 React Native init 的最簡 Demo 來做實驗(名字叫 Awes 代碼在 https://github.com/taobaofed/demo/tree/gh-pages/react-web )厅各。React Web 已經(jīng)把 React Native 比較復(fù)雜的 UI Explorer Demo 跑起來了,所以只要你的代碼能跑在 iOS 或者 Android 上面预柒,你基本不用擔心有什么組件上的問題队塘。當然如果有袁梗,可以馬上提 Issue 過來,我們有一個小組在支持 React web :)憔古。
第一步:安裝 React web 并進行相關(guān)配置
這一步操作主要是安裝 react-web
包以及相關(guān)依賴遮怜,并配置 webpack 打包腳本等。
為了簡化這一步操作鸿市,我們開發(fā)了命令行工具 react-web-cli
只需要執(zhí)行兩行命令即可锯梁。同時命令行工具還支持啟動調(diào)試服務(wù)器、打包等功能焰情,在后面介紹陌凳。
安裝 cli 工具:
npm install react-web-cli -g
安裝配置 React web 等:
react-web init <當前項目目錄>
執(zhí)行完成之后,會在你項目目錄下面 npm install
相關(guān)庫内舟,并自動創(chuàng)建web/webpack.config.js
文件合敦,里面有一份寫好的配置。此時目錄結(jié)構(gòu)為:
.├── README.md├── android/├── index.android.js├── index.ios.js├── ios/├── package.json└── web/ └── webpack.config.js
第二步:添加入口文件并進行相關(guān)配置
每個項目都需要有一個入口文件验游,通常用來引入調(diào)用其他組件并初始化項目充岛,比如index.ios.js
表示 iOS 平臺上的該項目的入口文件。為了符合 React Native 的文件命名規(guī)范耕蝉,我們創(chuàng)建一個 index.web.js
作為入口文件崔梗,并且需要在 webpack 中指定該文件為入口文件。打開 web/webpack.config.js
文件赔硫,修改 config
變量:
var config = { paths: { src: path.join(ROOT_PATH, '.'), index: path.join(ROOT_PATH, 'index.web'), },};
然后我們創(chuàng)建 index.web.js
文件炒俱。這個文件其實跟 index.ios.js
非常像,只是略有不同爪膊。主要區(qū)別在于:iOS 只需要 AppRegistry.registerComponent('Awes', () => Awes);
即可讓 Xcode 的 Native 代碼接收處理你的 JS 代碼权悟,而 Web 端是需要插入到 DOM 節(jié)點中才可以用。因此我們需要在 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
組件推盛。這樣配置部分就已經(jīng)處理完成了峦阁,執(zhí)行 react-web start
命令即可啟動調(diào)試服務(wù)器啦!
可以隨便修改試下耘成,跟 React Native 模擬器里面的體驗幾乎一樣榔昔。
第三步:測試并打包 Web 版本代碼
當你修改開發(fā)完,并對 Web 端也測試好了瘪菌,就可以打包發(fā)布了撒会。react-web-cli
工具打包的命令是:
react-web bundle
打包完成后,文件會存放在 web/output/
目錄下面师妙,可以直接打開 index.html
(如果 app 有請求操作诵肛,需要起本地服務(wù)器查看),再檢查一下就可以發(fā)布了默穴。
這個過程中發(fā)生了什么怔檩?
好奇的同學(xué)看到這里可能會有一些疑問褪秀,上面命令行工具的一些命令做了什么事情?為什么 React web 將 React Native 代碼打包出一份用在 Web 端的代碼薛训?React web 安全可靠嗎媒吗,里面都是什么東西?
這里簡單的介紹下 React web 的實現(xiàn)原理和上面步驟實際做的事情乙埃。
React Web 將 React Native 組件做了 Web 端的實現(xiàn)
React 將代碼與平臺環(huán)境分離闸英,多了一層,這樣開發(fā)者可以在平臺環(huán)境層面做一些處理膊爪,使得同樣一份代碼適應(yīng)更多的平臺環(huán)境等自阱。
比如 react-canvas 按照 React 的語法書寫代碼,在平臺環(huán)境層面做一些處理(將你 React 代碼運行并用 canvas 渲染)米酬,然后實現(xiàn)特定目標(在移動端提高性能)沛豌。
React Native 中,一份代碼能同時跑在 iOS 和 Android 上面赃额,也是一樣的道理加派。React Native 團隊在對應(yīng)平臺的 Native app 上面做了一些處理,使其可以解析執(zhí)行 React 語法的代碼跳芳。
還有同構(gòu)(isomorphic)的應(yīng)用芍锦,服務(wù)器端使用 React + Node.js 生成 HTML,客戶端使用 React 獲取進行客戶端相關(guān)交互和功能飞盆,也是一樣的道理娄琉。
為此, React v0.14.x 版本開始吓歇,專門分成兩個庫 react
和 react-dom
孽水,其實是把對瀏覽器平臺的特殊處理剝離了出來,單獨變成了 react-dom
庫城看。
React Native 比較特殊的地方在于女气,組件最底層的實現(xiàn)是 Native 的實現(xiàn),所以就不支持span
测柠、div
等標簽炼鞠。而動畫等,也是直接調(diào)用 Native 進行界面渲染轰胁。所以不支持 Web 端谒主,但是絕大部分組件,都是可以用 Web 技術(shù)進行模擬實現(xiàn)赃阀。動畫可以用 CSS3 霎肯、基礎(chǔ)元素可以用同等 HTML 標簽?zāi)M、布局以及兼容性問題可以用 CSS 來處理,所以 React web 只需要把 React Native 的組件用 Web 技術(shù)重新實現(xiàn)一遍姿现,借助 React 這一層,即可實現(xiàn)一份代碼運行在多個平臺上面肖抱。
舉一個非常簡單的例子备典,Text
組件:
React Native 的實現(xiàn) 是調(diào)用了很多 React Native 底層的代碼實現(xiàn)的。
對于 Web 端意述,輸出一行文本使用 <span>
標簽即可提佣,所以 React web 的實現(xiàn) 就直接搞一個 <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-web
的 module.exports
與 react-native
的保持一致即可讓代碼不替換也可以工作。
此外配合插件還可以實現(xiàn)另外一種引入方法子库,請看下面舱权。
通過 Haste 方法引入組件以提高性能
webpack 以及其他的支持 CommonJS 規(guī)范的打包工具,都會把文件中 require 的所有組件都打包在一起仑嗅。對于 React Native 來說代碼體積大小無關(guān)緊要宴倍,而在 Mobile web 來說,就要稍微重要一些了仓技。特別是如果你的項目只需要 Text
組件鸵贬,但由于 require('react-web')
結(jié)果把所有的組件全部打包進來了,就比較傷感浑彰。
基于 webpack 插件恭理,還可以用另一種方式引入組件以解決這個問題,你可以叫它 Haste
方式郭变。使用這種方式需要加載 webpack 插件 haste-resolver-webpack-plugin
颜价,默認的 webpack 配置已經(jīng)幫你加載好了,你可以直接在組件里面這樣用:
var Text = require('ReactText');
而不是以前那樣:
var {Text} = require('react-native');
這樣 webpack 打包時诉濒,對于前者周伦,只會把那一個組件內(nèi)容打包進來,因此可以減小體積未荒、提升性能专挪。這是怎么實現(xiàn)的呢?
加載了插件的 webpack 打包時,會先掃描所有組件并讀取組件頭部 @providesModule
的信息(比如 Text 組件的信息)寨腔,然后當其他文件中 require 了這個組件名稱速侈,就會自動定位到這個文件進行打包。同時還可以區(qū)分平臺迫卢,即便是同一個名字倚搬,打包時會區(qū)分平臺去打包對應(yīng)的文件(根據(jù) index.xxx.js 的命名規(guī)則確定文件)。
一些存在的問題
在 Web 端兼容性是個非常麻煩頭疼的事情乾蛤,React Web 已經(jīng)盡力幫你抹平兼容性問題和代碼差異每界,盡可能的讓你減少改動就可以創(chuàng)建 Web 版本的應(yīng)用。但受限于 Web 端的一些固有限制(比如請求跨域)家卖,不可避免的就會有一些需要你改代碼的地方眨层。
為此,可以通過 if (Platform.OS == 'web')
的方式判斷目標平臺上荡,并針對性的做一些平臺兼容性處理趴樱。同樣的,也可以將 web
替換為 ios
或者 android
判斷其他平臺榛臼。
在 React web 官方文檔上面已經(jīng)列出來了一些平臺差異問題伊佃,這里就不再贅述了。
歡迎踴躍嘗試沛善,遇到問題可以隨時提 Issue 哦:)