开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
问题背景
最近公司业务上需要做一个针对第三方特定网页的列表每条数据后面添加一个功能按钮
实现思路
针对这个业务背景下,我的实现方案是拦截第三方网页的列表请求,获取其响应正文,使用jquery在其每条数据后面append我们的功能按钮。
目前实现的chrome插件是基于"manifest_version":2版本,"manifest_version":3针对请求拦截的实现方式也是一样,此次由于其他业务需要,所以采用了2版本。
此次chrome插件主要涉及文件:
1.content-scripts 内容脚本,向页面注入脚本(可以是js,也可以是css),与原页面共享DOM,js是分开的。
2.injected.js 此脚本通过content-scripts 内容脚本中执行document.createElement('script')的方式将injected.js注入到页面,等同于原页面的js文件(此次功能的解决方式是通过此脚本实现的)
3.background.js 顾名思义,该脚本属于常驻脚本,生命周期和浏览器相同,跨域请求通过该脚本实现
4.devtools 开发者工具,请求拦截的备选方案就是通过devtools的api实现的
请求拦截方式:
方式1
background.js中chrome.webRequest系列api,需要先在manifest中声明权限
chrome.webRequest.onCompleted 成功处理请求时触发。该方式无法拿到返回的响应正文,常见用法为使用onBeforeRequest在请求发生前进行拦截重定向等操作
方式2
devtools对请求拦截,使用chrome.devtools.network系列api中的onRequestFinished,对请求结束事件进行监听,获取响应正文。其中获取到的content即为请求返回的内容。
此方法最大的弊端就是:需要打开开发者工具,chrome.devtools的api都是属于devtools的,对于用户而言,明显该方式不够友好
function handleRequestFinished(request) {
request.getContent(function(content, mimeType) {
chrome.runtime.sendMessage(chrome.runtime.id, {
fromDevTools: 'request',
tabId: chrome.devtools.inspectedWindow.tabId,
content: content
});
});
}
chrome.devtools.network.onRequestFinished.addListener(handleRequestFinished);
方式3(目前找到的最合适的方式)
通过content-scripts将injected.js注入脚本,在injected.js中对原生的XMLHttpRequest及fetch对象做扩展来实现对请求和响应的捕获。在获取到content内容之后,通window.postMessage将内容发送到content.js中
(function(xhr) {
var XHR = XMLHttpRequest.prototype;
var send = XHR.send ;
XHR.send = function(postData) {
this.addEventListener('load', function() {
var myUrl = this._url ? this._url.toLowerCase() : this._url;
if(myUrl) {
if(this.responseType != 'blob' && this.responseText ) {
try {
var text = this.responseText;
// 发送消息到content.js
window.postMessage({type: "inject_message_type", message:JSON.parse(text)})
console.log ('注入脚本发送获取list: ', JSON.parse(text));
} catch (err) {
}
}
}
});
return send.apply(this, arguments);
};
})(XMLHttpRequest);