前言
最近幾天要做一個React Native
簡單加載WebView的一個工程,支持加載遠(yuǎn)程url及本地html。本以為沒多大難度,結(jié)果因為初學(xué)React及React網(wǎng)上靠譜資料少的原因的還是踏了不少坑,特此記錄一下。
創(chuàng)建WebView工程
在創(chuàng)建React Native
WebView工程之前,我們需要搭建React Native的開發(fā)環(huán)境,在這我就不細(xì)說了,可以參考這篇文章React Native 中文網(wǎng) - 搭建開發(fā)環(huán)境。這篇文章很詳細(xì),照著步驟走下來基本沒啥問題。
搭好開發(fā)環(huán)境就可以開始創(chuàng)建工程了。cd
到要創(chuàng)建的工程目錄,執(zhí)行以下命令:
react-native init loadGameReactProject
看到如下打印的日志,就說明已經(jīng)創(chuàng)建成功了。
...
Run instructions for iOS:
? cd /Users/hangzhouyuewan/Documents/WorkSpace/Exercise/React/loadGameReactProject && react-native run-ios
- or -
? Open ios/loadGameReactProject.xcodeproj in Xcode
? Hit the Run button
Run instructions for Android:
? Have an Android emulator running (quickest way to get started), or a device connected.
? cd /Users/hangzhouyuewan/Documents/WorkSpace/Exercise/React/loadGameReactProject && react-native run-android
提示:也可以使用
--version
參數(shù)(注意是兩個杠)創(chuàng)建指定版本的項目。例如react-native init MyApp --version 0.44.3
。注意版本號必須精確到兩個小數(shù)點。
創(chuàng)建好以后可以通過命令cd /Users/hangzhouyuewan/Documents/WorkSpace/Exercise/React/loadGameReactProject && react-native run-ios
或者直接在Xcode打開ios/loadGameReactProject.xcodeproj
然后運行。
運行效果如下:
這個是默認(rèn)的界面,顯然不是我想要的,可以在React Native
工程目錄下,來修改App.js
文件來修改界面。
加載WebView
通過上面的步驟我已經(jīng)創(chuàng)建了一個React Native工程。但是內(nèi)容肯定不是我想要的。需要修改為可以既可以加載本地html
,也可以加載遠(yuǎn)程url
的界面。參考了React Native中文網(wǎng)-WebView的介紹。直接將代碼粘貼過去。
import React, { Component } from 'react';
import { WebView } from 'react-native';
class MyWeb extends Component {
render() {
return (
<WebView
source={{uri: 'https://github.com/facebook/react-native'}}
style={{marginTop: 20}}
/>
);
}
}
運行一成功后,刷新界面,結(jié)果報錯。報錯截屏如下:
百思不得其解。經(jīng)過仔細(xì)比對后,發(fā)現(xiàn)是因為將類從模塊中導(dǎo)出。這樣就沒法調(diào)用到該MyWeb
類,從而導(dǎo)致加載失敗。添加導(dǎo)出代碼如下。
export default class MyWeb extends Component {
render() {
return (
<WebView
source={{uri: 'https://github.com/facebook/react-native'}}
style={{marginTop: 20}}
/>
);
}
}
這里在class
添加了export default
,運行一下,界面刷新成功,展示了web頁面。export
是ES6
的語法。export
可以導(dǎo)出各種類型的變量、常量、類、函數(shù)、文件等。這里使用export default
導(dǎo)出默認(rèn)的類。詳細(xì)介紹可以看這篇文章ES6 模塊。還有React Native中文社區(qū)的這篇文章React/React Native 的ES5 ES6寫法對照表
WebView加載方式的分類
WebView
既可以通過加載遠(yuǎn)程url、也可以通過直接嵌入html代碼,也可以通過加載本地靜態(tài)html文件來渲染界面。通過開啟設(shè)置是否開啟useWebKit
選項可以使用WKWebView來實現(xiàn)。下面我們分別就這三種加載方式來闡述。
1.加載遠(yuǎn)程URL
加載遠(yuǎn)程URL像上面示例一樣,只需要直接給WebView的屬性source
賦值url,就能進(jìn)行加載。如下:
export default class MyWeb extends Component {
render() {
return (
<WebView
source={{uri: 'https://github.com/facebook/react-native'}}
style={{marginTop: 20}}
/>
);
}
}
2.直接嵌入html代碼
直接加載html
只需要直接給WebView的屬性source
賦值url,就能進(jìn)行加載。如下:
export default class MyWeb extends Component {
render() {
return (
<WebView
source={{html: '<h1>Hello World!</h1'}}
style={{marginTop: 20}}
/>
);
}
}
3.加載本地靜態(tài)的html文件
查看文章React Native 中使用 WebView 加載本地 html找到加載本地html
的方法。在iOS
中要加載一個本地靜態(tài)的html
文件,可以將本地的html
放入到和App.js
同級目錄下,然后通過如下方式加載:
export default class MyWeb extends Component {
render() {
return (
<WebView
source={require('./test.html')}
/>
);
}
}
這樣運行起來測試一下,發(fā)現(xiàn)沒問題。但因為我的App是要加載一個H5
小游戲,本地html
還引用了一些js及資源文件。這時候去加載會發(fā)現(xiàn)就加載不了了。
export default class MyWeb extends Component {
render() {
return (
<WebView
source={require('./game/index.html')}
/>
);
}
}
運行一下。結(jié)果顯示黑屏。其實本地html
文件已經(jīng)加載出來了,但是因為html
無法加載資源文件,所以導(dǎo)致黑屏了。還得繼續(xù)想辦法,解決本地html
加載本地資源的問題。通過搜搜網(wǎng)上的資料,基本上都說這種加載方式在dev環(huán)境下是可以加載本地資源的。通過攔截js
轉(zhuǎn)換成OC
WebView的代碼,如下:
從上面可以發(fā)現(xiàn),js中本地html
的加載轉(zhuǎn)換成了localhost
url方式進(jìn)行加載。所以加載靜態(tài)html
文件是可以的,至于html
不能加載本地文件,姑且猜測是因為沒有開啟本地Web
服務(wù)的原因。這里有待后續(xù)研究如何解決。
回到上面的話題,既然上面這種方式無法加載引用本地資源的html
,我們還得找解決方法來解決這個問題。在ReactNative 打包IOS應(yīng)用程序這篇文章中提到,當(dāng)把App發(fā)布到AppStore中時,需要將JavaScript
和圖片等本地資源打包成離線資源,再添加到Xcode中,然后一起發(fā)布到App Store中。至于如何添加及實現(xiàn),且看下章。
加載引用本地資源的Html方法
上面提到在App發(fā)布到App Store中時,需要將資源打包成離線資源。下面我們就看一下具體的打包步驟。打包離線資源需要使用命令react-native bundle
。
一、生成bundle
文件
在ios
目錄下新建bundle
目錄
-
1)通過命令行執(zhí)行命令打包
進(jìn)入項目目錄,運行以下打包命令。react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.ios.jsbundle --assets-dest ./ios/bundle
-
--entry-file
,ios或者android入口的js名稱。這里是index.js
-
--platform
,平臺名稱(ios或者android) -
--dev
,設(shè)置為false
的時候?qū)?code>JavaScript進(jìn)行優(yōu)化處理。 -
--bundle-output
,生成的jsbundle
文件的名稱,這里是./ios/bundle/index.ios.jsbundle
。 -
--assets-dest
圖片以及其他資源存放的目錄,這里放在./ios/bundle
目錄下。
-
-
2)通過腳本打包
在package.json
中添加編譯命令{ "scripts":{ "bundle-ios":"node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.ios.jsbundle --assets-dest ./ios/bundle" } }
直接在終端運行
npm run bundle-ios?
即可生成bundle。
二、在Xcode中集成
離線包生成完之后,可以在ios
目錄下看到一個bundle
目錄,這個目錄就是bundle
生成的離線資源。
需要在Xcode中添加資源到項目中,必須使用Create folder references
添加文件夾,否則不起作用。
-
Add Files to "loadGameReactProject"
添加截屏.png
-
選擇
bundle
文件,在option中Create folder references
選擇引用.png -
這樣添加到項目中的文件夾是藍(lán)色的
添加完成.png
三、修改AppDelegate.m
加載bundle
的方式
在AppDelegate.m
文件中的- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
方法改為如下實現(xiàn):
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
//#if DEBUG
//
// return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
//#else
// return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
//#endif
return [[NSBundle mainBundle] URLForResource:@"bundle/index.ios" withExtension:@"jsbundle"];
}
修改debug狀態(tài)
將項目由debug
狀態(tài)改成release
狀態(tài)選擇Generic iOS Device,運行一下。
-
再選擇模擬器或者真機運行,這時發(fā)現(xiàn)
WebView
可以加載起來但是缺顯示空白。
setSource截屏.png
通過上圖我們攔截setSource:
方法中的webview加載request方法,打印request
的URL
,可以知道加載的URL是沒有問題的。那問題必然出在加載后回調(diào)以及跟js的交互這一塊。這時我們只需要將webview的delegate設(shè)置為nil就可以了。如下圖:
設(shè)置delegate為nil.png
問題到此就解決了。很明顯這并不是一個很好的解決方案,后續(xù)有待進(jìn)一步探索解決。