前言
最近几天要做一个React Native
简单加载WebView的一个工程,支持加载远程url及本地html。本以为没多大难度,结果因为初学React及React网上靠谱资料少的原因的还是踏了不少坑,特此记录一下。
创建WebView工程
在创建React Native
WebView工程之前,我们需要搭建React Native的开发环境,在这我就不细说了,可以参考这篇文章React Native 中文网 - 搭建开发环境。这篇文章很详细,照着步骤走下来基本没啥问题。
搭好开发环境就可以开始创建工程了。cd
到要创建的工程目录,执行以下命令:
react-native init loadGameReactProject
看到如下打印的日志,就说明已经创建成功了。
...
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
参数(注意是两个杠)创建指定版本的项目。例如react-native init MyApp --version 0.44.3
。注意版本号必须精确到两个小数点。
创建好以后可以通过命令cd /Users/hangzhouyuewan/Documents/WorkSpace/Exercise/React/loadGameReactProject && react-native run-ios
或者直接在Xcode打开ios/loadGameReactProject.xcodeproj
然后运行。
运行效果如下:
这个是默认的界面,显然不是我想要的,可以在React Native
工程目录下,来修改App.js
文件来修改界面。
加载WebView
通过上面的步骤我已经创建了一个React Native工程。但是内容肯定不是我想要的。需要修改为可以既可以加载本地html
,也可以加载远程url
的界面。参考了React Native中文网-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}}
/>
);
}
}
运行一成功后,刷新界面,结果报错。报错截屏如下:
百思不得其解。经过仔细比对后,发现是因为将类从模块中导出。这样就没法调用到该MyWeb
类,从而导致加载失败。添加导出代码如下。
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
可以导出各种类型的变量、常量、类、函数、文件等。这里使用export default
导出默认的类。详细介绍可以看这篇文章ES6 模块。还有React Native中文社区的这篇文章React/React Native 的ES5 ES6写法对照表
WebView加载方式的分类
WebView
既可以通过加载远程url、也可以通过直接嵌入html代码,也可以通过加载本地静态html文件来渲染界面。通过开启设置是否开启useWebKit
选项可以使用WKWebView来实现。下面我们分别就这三种加载方式来阐述。
1.加载远程URL
加载远程URL像上面示例一样,只需要直接给WebView的属性source
赋值url,就能进行加载。如下:
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,就能进行加载。如下:
export default class MyWeb extends Component {
render() {
return (
<WebView
source={{html: '<h1>Hello World!</h1'}}
style={{marginTop: 20}}
/>
);
}
}
3.加载本地静态的html文件
查看文章React Native 中使用 WebView 加载本地 html找到加载本地html
的方法。在iOS
中要加载一个本地静态的html
文件,可以将本地的html
放入到和App.js
同级目录下,然后通过如下方式加载:
export default class MyWeb extends Component {
render() {
return (
<WebView
source={require('./test.html')}
/>
);
}
}
这样运行起来测试一下,发现没问题。但因为我的App是要加载一个H5
小游戏,本地html
还引用了一些js及资源文件。这时候去加载会发现就加载不了了。
export default class MyWeb extends Component {
render() {
return (
<WebView
source={require('./game/index.html')}
/>
);
}
}
运行一下。结果显示黑屏。其实本地html
文件已经加载出来了,但是因为html
无法加载资源文件,所以导致黑屏了。还得继续想办法,解决本地html
加载本地资源的问题。通过搜搜网上的资料,基本上都说这种加载方式在dev环境下是可以加载本地资源的。通过拦截js
转换成OC
WebView的代码,如下:
从上面可以发现,js中本地html
的加载转换成了localhost
url方式进行加载。所以加载静态html
文件是可以的,至于html
不能加载本地文件,姑且猜测是因为没有开启本地Web
服务的原因。这里有待后续研究如何解决。
回到上面的话题,既然上面这种方式无法加载引用本地资源的html
,我们还得找解决方法来解决这个问题。在ReactNative 打包IOS应用程序这篇文章中提到,当把App发布到AppStore中时,需要将JavaScript
和图片等本地资源打包成离线资源,再添加到Xcode中,然后一起发布到App Store中。至于如何添加及实现,且看下章。
加载引用本地资源的Html方法
上面提到在App发布到App Store中时,需要将资源打包成离线资源。下面我们就看一下具体的打包步骤。打包离线资源需要使用命令react-native bundle
。
一、生成bundle
文件
在ios
目录下新建bundle
目录
-
1)通过命令行执行命令打包
进入项目目录,运行以下打包命令。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
,设置为false
的时候将会对JavaScript
进行优化处理。 -
--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"
-
选择
bundle
文件,在option中Create folder references
-
这样添加到项目中的文件夹是蓝色的
三、修改AppDelegate.m
加载bundle
的方式
在AppDelegate.m
文件中的- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
方法改为如下实现:
- (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状态
将项目由debug
状态改成release
状态选择Generic iOS Device,运行一下。
-
再选择模拟器或者真机运行,这时发现
WebView
可以加载起来但是缺显示空白。
通过上图我们拦截setSource:
方法中的webview加载request方法,打印request
的URL
,可以知道加载的URL是没有问题的。那问题必然出在加载后回调以及跟js的交互这一块。这时我们只需要将webview的delegate设置为nil就可以了。如下图:
问题到此就解决了。很明显这并不是一个很好的解决方案,后续有待进一步探索解决。