Ionic在使用过程中如果使用ThemeableBrowser或者InAppBrowser进行数据交互时,两个插件都存在一定的问题。 ThemeableBrowser fork自InAppBrowser,ThemeableBrowser提供了全新的主题匹配,但是由于插件长时间未更新,在数据交互时存在一定的问题,但是我们可以根据自己的实际情况选择使用哪个插件
实际在数据交互中,主要通过以下API来实现:
executeScript (注入JS代码到新的窗口)
on(loadstart、loadstop监听)
show (打开窗口)
close (关闭窗口)
准备工作 ——准备供浏览器插件访问的网页 新建并发布一个网页,供浏览器插件访问。这里我简单用node搭建一个网页(由上往下分别是创建目录、跳过询问来配置package.json、安装express):
1 2 3 mkdir test-web && cd test-web npm init -y npm i express --save
新建index.js文件,并填入以下内容:
1 2 3 4 5 6 7 8 const express = require('express') const path = require('path') const app = express() app.use(express.static(__dirname)) app.listen(8089, () => { console.log(`App listening at port 8089`) })
这样就部署了个静态网页服务器,我们再创建一个html页面和一个js文件: index.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <h1>获得的参数为:</h1> <button id="send-data-btn">向APP传递参数</button> <script src="test.js"></script> </body> </html>
test.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var fromAppData; //获得的参数 var sendDataBtn = document.getElementById('send-data-btn') var getAppData = function (data) { fromAppData = data; alert(JSON.stringify(fromAppData)); } var sendParamsToApp = function () { var sendData = 'sendData' location.href = "https://www.baidu.com/?params=" + sendData } sendDataBtn.onclick = function () { sendParamsToApp(); } var getDataFromWeb = function () { var url = window.location.search; var theRequest = new Object(); if (url.indexOf("?") != -1) { var str = url.substr(1); strs = str.split("&"); for (var i = 0; i < strs.length; i++) { theRequest[strs[i].split("=")[0]] = decodeURI(strs[i].split("=")[1]); } } return theRequest; }
执行命令启动:
在浏览器访问一下是否能正常运行:localhost:8089,实际真机测试时换成IP访问:192.168.2.130:8089(根据自己的IP启动)
准备APP 安装插件后,实际代码如下home.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 dataFromWeb: any; constructor( public navCtrl: NavController, private iab: InAppBrowser) { } sendDataToWeb() { const browser = this.iab.create('http://192.168.1.8:8089', '_blank'); const data = 'hello!'; //发送的参数 const code: string = "getAppData(" + data + ")"; browser.on('loadstop').subscribe(event => { //发送参数 browser.executeScript({ code: code }) .then(params => { console.log(params); }); //获得返回的参数 if (event.url.match("www.baidu.com")) { const code = "getDataFromWeb()" browser.executeScript({ code: code }) .then(params => { console.log(params[0]) this.dataFromWeb = params[0].sendData; browser.close(); }) } }) }
home.html:
1 2 3 4 5 6 7 <ion-content padding> <button ion-button (click)="sendDataToWeb()">发送参数</button> <p *ngIf="dataFromWeb">从Web收到的参数是:{{dataFromWeb}}</p> </ion-content>
测试并返回数据
简短的说明下实现原理,首先是向Web环境传递参数时,为什么选择了loadstop监听,实际web项目会存在一定的加载时间,如果选择loadstart,那么通过APP内的executeScript()方法注入执行的getAppData()方法会在一定情况下无法执行,这是由于loadstart触发的时机是打开一个新的地址就会触发,某些情况下,网页并未初始化完全即 getAppData这个方法并没有声明,就去执行,所以会报错
1 2 3 4 5 6 var fromAppData; //获得的参数 var getAppData = function (data) { fromAppData = data; alert(JSON.stringify(fromAppData)); }
返回参数时的原理,我们通过web点击跳转一个新的地址,然后通过?拼接的方式将参数拼接在地址栏内即:
1 2 3 4 5 6 7 8 var sendParamsToApp = function () { var sendData = 'sendData' location.href = "https://www.baidu.com/?params=" + sendData } sendDataBtn.onclick = function () { sendParamsToApp(); }
然后通过loadstop监听地址栏变化当有我们的地址出现时,从地址栏拿取参数并且关闭browser,即:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //web var getDataFromWeb = function () { var url = window.location.search; var theRequest = new Object(); if (url.indexOf("?") != -1) { var str = url.substr(1); strs = str.split("&"); for (var i = 0; i < strs.length; i++) { theRequest[strs[i].split("=")[0]] = decodeURI(strs[i].split("=")[1]); } } return theRequest; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 //app browser.on('loadstop').subscribe(event => { ... //获得返回的参数 if (event.url.match("www.baidu.com")) { const code = "getDataFromWeb()" browser.executeScript({ code: code }) .then(params => { console.log(params[0]) this.dataFromWeb = params[0].sendData; browser.close(); }) } })
总结 参考用例 https://www.jianshu.com/p/b63d120d1e01
例子中的向APP返回参数的方法使用了window.open的方法然后调用方法获得参数,实际中存在两个问题;
1.iOS会阻止window.open的方法; 改为location.href可解决
2.打开一个新的域名时,新的域名下没有getDataFromWeb这个方法,在某些情况下会报错无法找到这个方法, 如果改为在当前域名下添加#的方法来获取URL的改变来触发loadstop,在iOS下会触发loadstop方法,但是在Android下不会触发,所以这里只能打开一个新的域名并且通过?参数拼接的方法获取参数
3.ThemeableBrowser插件由于长时间未更新,在iOS12下当打开web时loadstop监听会被iOS系统屏蔽掉,无法监听,所以无法用于返回参数
4.所以总体来说,如果只是单纯的向web传递参数,可以考虑使用ThemeableBrowser来实现,如果需要web向APP内返回参数,只能只用InAppBrowser来解决 5.采用上述方法,存在一个安全问题就是返回的参数是通过地址栏拼接获得,实际项目中可以通过和服务器同时约定一个加密码来用于验证数据的真伪即可解决.