React Native加载WebView及遇坑总结

前言

最近几天要做一个React Native简单加载WebView的一个工程,支持加载远程url及本地html。本以为没多大难度,结果因为初学React及React网上靠谱资料少的原因的还是踏了不少坑,特此记录一下。

创建WebView工程

在创建React NativeWebView工程之前,我们需要搭建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然后运行。

运行效果如下:

初始化运行成功截屏.png

这个是默认的界面,显然不是我想要的,可以在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}}
      />
    );
  }
}

运行一成功后,刷新界面,结果报错。报错截屏如下:

加载WebView报错

百思不得其解。经过仔细比对后,发现是因为将类从模块中导出。这样就没法调用到该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页面。exportES6的语法。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')}
      />
    );
  }
}
game存放目录.png

运行一下。结果显示黑屏。其实本地html文件已经加载出来了,但是因为html无法加载资源文件,所以导致黑屏了。还得继续想办法,解决本地html加载本地资源的问题。通过搜搜网上的资料,基本上都说这种加载方式在dev环境下是可以加载本地资源的。通过拦截js转换成OCWebView的代码,如下:

RCTWebView断点.png

从上面可以发现,js中本地html的加载转换成了localhosturl方式进行加载。所以加载静态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"

    添加截屏.png
  • 选择bundle文件,在option中Create folder references

    选择引用.png
  • 这样添加到项目中的文件夹是蓝色的

    添加完成.png

三、修改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截屏.png

    通过上图我们拦截setSource:方法中的webview加载request方法,打印requestURL,可以知道加载的URL是没有问题的。那问题必然出在加载后回调以及跟js的交互这一块。这时我们只需要将webview的delegate设置为nil就可以了。如下图:
    设置delegate为nil.png

    问题到此就解决了。

    很明显这并不是一个很好的解决方案,后续有待进一步探索解决。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342