最新又重新看了一遍cross-requset的代码,因为毕竟平台做了二次开发,cross-request 在接口运行上起到了至关重要的作用,所以还是需要了解它的作用,也方便后续的排查问题以及解决。
重新花时间缕了下cross-request的整个流程 大体如下:
我们需要解决几个疑惑
如果对chrome插件有一定了解的话一定知道, content-script 与实际的页面只是共享dom内容,而不能够共享js的变量以及方法的。所以这里就有一个比较麻烦的问题了,真正的页面点击运行的时候,如何才能够将具体的请求的数据给到content-script呢? 所以就需要用到inject-script了。 注入的脚本能够做到共享js变量的方式来解决这个事情。
因为我们第一个问题说过了,content-script 与实际的页面只是共享dom内容,所以传递数据就是通过修改dom的数据,来进行数据的传递了。所以上图的流程里面id为y-request的div就是起着这样子一个作用。
这里就涉及到了chrome 浏览器安全的限制了。在chrome 72的版本以后就限制了不允许content-script进行跨域的请求。而我们的接口运行肯定是需要进行跨域的操作的。所以只能够将所有的信息再次传递给到background由它来完成我们的接口执行,最后再把执行的结果传递回来。
从上面的流程图我们会发现一个问题,index.js 在收到接口请求发给content-script后就会不停的轮询去读取dom的变化来确定是否已经完成了接口请求,而response.js(content-script)同样, 它更是启动后就在循环的读取dom里面一个status的状态值来判断是否需要获取接口数据进行请求。
这种方式的实现还是有点麻烦了。其实应该可以通过通知,订阅类似于这种模式来解决这个问题。所以我们下来可以考虑改造下cross-request
所以这里我们需要先普及下一个内容 关于
element.dispatchEvent()
对于标准浏览器,其提供了可供元素触发自定义事件的方法:element.dispatchEvent().。
不过,在使用该方法之前,我们还需要做其他两件事,即创建和初始化。
document.createEvent()
event.initEvent()
element.dispatchEvent()
举个例子:
var dom = document.querySelector('#id')
document.addEventListener('alert', function (event) {
console.log(event)
}, false);
// 创建
var evt = document.createEvent("HTMLEvents");
// 初始化
evt.initEvent("alert", false, false);
// 触发, 即弹出文字
dom.dispatchEvent(evt);
1. createEvent()
createEvent()方法返回新创建的Event对象,支持一个参数,表示事件类型,具体见下表:
2. initEvent()
initEvent()
方法用于初始化通过DocumentEvent接口创建的Event的值。
支持三个参数:initEvent(eventName, canBubble, preventDefault)
分别表示:
3. dispatchEvent()
dispatchEvent()
就是触发执行了,dom.dispatchEvent(eventObject)
参数eventObject
表示事件对象,是createEvent()
方法返回的创建的Event
对象。
所以以下就是具体的代码修改的内容
index.js
var customEvent = document.createEvent('Event');
customEvent.initEvent('myCustomEvent', true, true);
function fireCustomEvent(data) {
hiddenDiv = document.getElementById(container);
hiddenDiv.innerText = data
hiddenDiv.dispatchEvent(customEvent);
}
function run(req) {
if (!req) return;
if (typeof req === 'string') req = { url: req }
var newId = getid();
data = {
id: newId,
res: null,
req: req
}
data = encode(data);
yRequestMap[newId] = {
id: newId,
status: INITSTATUS,
success: function (res, header, data) {
if (typeof req.success === 'function') {
req.success(res, header, data);
}
},
error: function (error, header, data) {
if (typeof req.error === 'function') {
req.error(error, header, data)
}
}
}
fireCustomEvent(data);
}
yRequestDom.addEventListener('reponseEvent', function () {
var text = yRequestDom.innerText;
if (text) {
var data = decode(yRequestDom.innerText);
var id = data.id;
var res = data.res;
if (res.status === 200) {
yRequestMap[id].success(res.body, res.header, data);
} else {
yRequestMap[id].error(res.statusText, res.header, data);
}
} else {
}
});
win.crossRequest = run;
if (typeof define == 'function' && define.amd) {
define('crossRequest', [], function () {
return run;
});
}
resopnse.js
var customEvent = document.createEvent('Event');
customEvent.initEvent('reponseEvent', true, true);
function responseCallback(res, dom, data) {
var id = data.id;
var headers = handleHeader(res.headers);
data.runTime = new Date().getTime() - data.runTime;
data.res = {
id: id,
status: res.status,
statusText: res.statusText,
header: headers,
body: res.body
}
dom.innerText = encode(data);
dom.dispatchEvent(customEvent);
}
function sendAjaxByBack(id, req, successFn, errorFn) {
successFns[id] = successFn;
errorFns[id] = errorFn;
connect.postMessage({
id: id,
req: req
});
}
connect.onMessage.addListener(function (msg) {
var id = msg.id;
var res = msg.res;
res.status === 200 ?
successFns[id](res) :
errorFns[id](res);
delete successFns[id];
delete errorFns[id];
});
//因注入 index.js ,需要等到 indexScript 初始化完成后执行
var findDom = setInterval(function () {
try {
yRequestDom = document.getElementById(container);
if (yRequestDom) {
clearInterval(findDom)
yRequestDom.addEventListener('myCustomEvent', function () {
var data = yRequestDom.innerText;
console.log('收到自定义事件消息:', decode(data));
data = decode(data);
var req = data.req;
req.url = req.url || '';
data.runTime = new Date().getTime();
sendAjaxByBack(data.id, req, function (res) {
responseCallback(res, yRequestDom, data);
}, function (err) {
responseCallback(err, yRequestDom, data);
})
});
}
} catch (e) {
clearInterval(findDom)
console.error(e)
}
}, 100)
以上就是修改的内容,只是把定时任务给去除 替换成了事件监听的方式来完成这个工作了。
具体代码见: cross-request