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

原文地址: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 哦:)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末航揉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子金刁,更是在濱河造成了極大的恐慌帅涂,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,496評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尤蛮,死亡現(xiàn)場離奇詭異媳友,居然都是意外死亡,警方通過查閱死者的電腦和手機产捞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,187評論 3 385
  • 文/潘曉璐 我一進店門醇锚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坯临,你說我怎么就攤上這事焊唬。” “怎么了看靠?”我有些...
    開封第一講書人閱讀 157,091評論 0 348
  • 文/不壞的土叔 我叫張陵赶促,是天一觀的道長。 經(jīng)常有香客問我挟炬,道長鸥滨,這世上最難降的妖魔是什么嗦哆? 我笑而不...
    開封第一講書人閱讀 56,458評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮婿滓,結(jié)果婚禮上老速,老公的妹妹穿的比我還像新娘。我一直安慰自己凸主,他們只是感情好烁峭,可當我...
    茶點故事閱讀 65,542評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著秕铛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缩挑。 梳的紋絲不亂的頭發(fā)上但两,一...
    開封第一講書人閱讀 49,802評論 1 290
  • 那天,我揣著相機與錄音供置,去河邊找鬼谨湘。 笑死,一個胖子當著我的面吹牛芥丧,可吹牛的內(nèi)容都是我干的紧阔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,945評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼续担,長吁一口氣:“原來是場噩夢啊……” “哼擅耽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起物遇,我...
    開封第一講書人閱讀 37,709評論 0 266
  • 序言:老撾萬榮一對情侶失蹤乖仇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后询兴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乃沙,經(jīng)...
    沈念sama閱讀 44,158評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,502評論 2 327
  • 正文 我和宋清朗相戀三年诗舰,在試婚紗的時候發(fā)現(xiàn)自己被綠了警儒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,637評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡眶根,死狀恐怖蜀铲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情汛闸,我是刑警寧澤蝙茶,帶...
    沈念sama閱讀 34,300評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站诸老,受9級特大地震影響隆夯,放射性物質(zhì)發(fā)生泄漏钳恕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,911評論 3 313
  • 文/蒙蒙 一蹄衷、第九天 我趴在偏房一處隱蔽的房頂上張望忧额。 院中可真熱鬧,春花似錦愧口、人聲如沸睦番。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,744評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽托嚣。三九已至,卻和暖如春厚骗,著一層夾襖步出監(jiān)牢的瞬間示启,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,982評論 1 266
  • 我被黑心中介騙來泰國打工领舰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夫嗓,地道東北人。 一個月前我還...
    沈念sama閱讀 46,344評論 2 360
  • 正文 我出身青樓冲秽,卻偏偏與公主長得像舍咖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子锉桑,可洞房花燭夜當晚...
    茶點故事閱讀 43,500評論 2 348

推薦閱讀更多精彩內(nèi)容