【React Native 实战:构建电商 App】06-使用 WebView 和运营页通讯

React Native 实战:构建电商 App

知识点:

  • 创建一个浏览网页的组件
  • 注入 JS 并和网页通讯

RN 默认提供了一个 WebView 组件,可以使用这个组件在 App 内打开一个网页,就像在微信里做的那样。

但是电商 App 其实需要这个页面有很多和 App 的交互,比如打开商品详情页、加入购物车、分享等操作,这里就需要使用自定义 App 和 WebView 通讯协议的手段了。

网页浏览页

在 home 下新建一个 browser.js 文件,项目中只要是打开网页的都会跳转到这里,需要注意一下,变量不要使用 url,在有些情况会发生错误。

将传入的 webPath 参数放入 state 中,根据参数加载网页,这里要注意中文的问题,同时给网页加一个时间戳,禁止网页缓存在手机上。

let urls = decodeURIComponent(this.props.navigation.state.params.webPath || '');
        if (urls.indexOf('?') > 0) {
            urls += '&t=' + Date.now();
        } else {
            urls += '?t=' + Date.now();
        }
        this.state = {
            webpath: urls,
            title: ''
        }

设置 webview 的一些属性,注意安卓要启用 js 功能,iOS 中要注意兼容网页的 http 和 https 请求。

<WebView
                //添加一个引用
                ref="webview"
                //设置浏览地址
                source={{ uri: this.state.webpath }}
                //启用安卓的js功能
                javaScriptEnabled={true}
                //正常的滚动停止速度
                decelerationRate="normal"
                //允许https和http一起使用
                mixedContentMode="always"
                //显示loading动画
                startInLoadingState={true}
                //开启缩放
                scalesPageToFit={true}
                //网页互动消息通知
                onMessage={(t) => this.onMessage(t)}
                onLoad={(e) => {
                    this.loaded()
                    log('网页加载成功', e.nativeEvent)
                }}
                onError={(e) => {
                    log('网页加载失败', e.nativeEvent)
                }} />

修改首页的 2 个入口,将入口的位置调整 html 修改为跳转刚才写好的页面。

点击快捷入口的人气排行就能看到打开的网页了,这里注意不要打开姐拼了这个入口,它的里面有用户验证,不验证就是一个白屏。

enter image description here

监听 webview 的跳转事件,通过网页反馈的 title 设置当前页面的标题onNavigationStateChange={(e) => this.webChange(e)}

注意去掉非标题的字符,让本身的标题看起来更好看一些。

if (!event || !event.title) return;
        if (event.title.indexOf("http") < 0 && event.title.indexOf("about:") < 0) {
            this.setState({
                title: event.title
            });
        }

添加互动能力

RN 的 webview 通过监听 postMessage 发送的消息来达到和网页互相传递消息的功能,这里我们就利用这个方法来做网页和 App 之间的互动。

当网页加载完成之后,我们在网页中插入几个自定义的 js 方法,然后通过 possMessage 的方法按照订好的协议将消息回传给 App,这样就实现了网页和 App 的互动。通过因为所有实现方法都是后注入的,对于调用的人来说是非常方便的,不管后期有什么变化,js 的接口是不会变的。

javascript=`window.postMessage("测试消息")`
    //网页加载成功之后
    loaded() {
        this.refs.webview.injectJavaScript(this.javascript);
    }
    //网页回调消息到APP
    onMessage(t) {
        try {
            let data = t.nativeEvent.data;
            log(data)
        } catch (e) {
            logWarm(e.message)
        }
    }

注意:我们使用的是在网页加载完之后再注入 JS,网页加载完成之前注入会有很多不可定的因素,所以要先注入,不过这个注入不能太多,太多就会不生效。如果 JS 非常多,请把逻辑写在 JS 文件中,然后让需要使用的网页去引用,你只需要注入启动的逻辑就好了。

通讯协议

我们这里强制要求所有通讯协议都是 JSON 格式转化的字符串,JSON 中必须是一个数组,第一个数组即是方法名,后面的就是方法的参数。

这里强调一下兼容问题,WebView 在低版本的手机上会遇到兼容问题,比如不支持 ES 6、使用的是严格模式等等,所以需要在写 JS 的时候非常的严格,不能写新的东西,同时还要兼容几个低版本手机。

这里在注入 JS 的时候将本身提供的几个方法循环注入,可以节省很多代码,同时也将 App 自身的一些信息也注入到 JS 中,这样网页中的 JS 就可以非常方便的调用到 App 中的信息了,如果你愿意,甚至可以模仿微信的形式来做这些。

enter image description here

在 JS 注入之后加载网页的 ready 方法和事件,同时兼容到 JS 提前加载和正常延迟加载的情况。

我们这里使用的是将字符串转化成 JSON 对象的形式来做消息通讯,window.postMessage(JSON.stringify(data));

在接受到消息之后通过格式化字符串,我们就得到了一个完整的方法名 + 参数的数组对象,这里通过类似反射的形式得到当前需要执行的方法,使用 App 中的 this 作为方法内部的 this 来调用方法。

//网页回调消息到APP
    onMessage(t) {
        try {
            let data = t.nativeEvent.data;
            if (data) {
                let args = JSON.parse(data);
                let name = args.shift();
                if (this[name]) this[name].call(this, ...args)
            }
        } catch (e) {
            logWarm(e.message)
        }
    }

这里我们简单的实现几个网页需要调用的方法,在初始化的 SDK 中有一个简单的发布订阅模式,具体的实现方式在网页中,有兴趣的可以看看。

/**
     * 打开详情页
     * @param {*} id 
     */
    openDetail(id) {
        if (/^[0-9]+$/.test(id + '')) {
            this.props.navigation.navigate('Goods', {
                id: id
            });
        } else {
            this.props.navigation.navigate('Goods', {
                sku: id
            });
        }
    }
setShareInfo(title, desc, shareList, img, viewurl) {
        this.setState({
            title: title,
        });
    }

网页中的 SDK 兼容了非 App 环境下的一些东西,并且实现了一个简单的事件监听触发,可以通过这个小方法去监听一些事件,然后在 App 中使用插入 emit 的方式触发事件,比如分享的回调、支付的回调等等。

enter image description here

正式环境下只需要保证 SDK 存在,并且可以判断到是否在 App 环境下即可。

发表评论