动态注入或执行JS
虽然在background和popup中无法直接访问页面DOM,但是可以通过chrome.tabs.executeScript来执行脚本,从而实现访问web页面的DOM(注意,这种方式也不能直接访问页面JS)。
示例manifest.json配置:
复制代码
{ "name": "动态JS注入演示", ... "permissions": [ "tabs", "http://*/*", "https://*/*" ], ...}
JS:
// 动态执行JS代码chrome.tabs.executeScript(tabId, {code: 'document.body.style.backgroundColor="red"'});// 动态执行JS文件chrome.tabs.executeScript(tabId, {file: 'some-script.js'});
动态注入CSS
示例manifest.json配置:
{ "name": "动态CSS注入演示", ... "permissions": [ "tabs", "http://*/*", "https://*/*" ], ...}
JS代码:
复制代码
// 动态执行CSS代码,TODO,这里有待验证chrome.tabs.insertCSS(tabId, {code: 'xxx'});// 动态执行CSS文件chrome.tabs.insertCSS(tabId, {file: 'some-style.css'});
获取当前窗口ID
`chrome.windows.getCurrent(function(currentWindow)`
`{`
`console.log('当前窗口ID:' + currentWindow.id);`
`});`
## 获取当前标签页ID
一般有2种方法:
// 获取当前选项卡IDfunction getCurrentTabId(callback){ chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { if(callback) callback(tabs.length ? tabs[0].id: null); });}
获取当前选项卡id的另一种方法,大部分时候都类似,只有少部分时候会不一样(例如当窗口最小化时)
// 获取当前选项卡IDfunction getCurrentTabId2(){ chrome.windows.getCurrent(function(currentWindow) { chrome.tabs.query({active: true, windowId: currentWindow.id}, function(tabs) { if(callback) callback(tabs.length ? tabs[0].id: null); }); });}
## 本地存储
本地存储建议用`chrome.storage`而不是普通的`localStorage`,区别有好几点,个人认为最重要的2点区别是:
- `chrome.storage`是针对插件全局的,即使你在`background`中保存的数据,在`content-script`也能获取到;
- `chrome.storage.sync`可以跟随当前登录用户自动同步,这台电脑修改的设置会自动同步到其它电脑,很方便,如果没有登录或者未联网则先保存到本地,等登录了再同步至网络;
需要声明`storage`权限,有`chrome.storage.sync`和`chrome.storage.local`2种方式可供选择,使用示例如下:
// 读取数据,第一个参数是指定要读取的key以及设置默认值chrome.storage.sync.get({color: 'red', age: 18}, function(items) { console.log(items.color, items.age);});// 保存数据chrome.storage.sync.set({color: 'blue'}, function() { console.log('保存成功!');});
## webRequest
通过[webRequest](https://developer.chrome.com/extensions/webRequest)系列API可以对HTTP请求进行任性地修改、定制,下面是`webRequest`的几个生命周期:

这里通过`beforeRequest`来简单演示一下它的冰山一角:
//manifest.json{ // 权限申请 "permissions": [ "webRequest", // web请求 "webRequestBlocking", // 阻塞式web请求 "storage", // 插件本地存储 "http:///", // 可以通过executeScript或者insertCSS访问的网站 "https:///" // 可以通过executeScript或者insertCSS访问的网站 ],}// background.js// 是否显示图片var showImage;chrome.storage.sync.get({showImage: true}, function(items) { showImage = items.showImage;});// web请求监听,最后一个参数表示阻塞式,需单独声明权限:webRequestBlockingchrome.webRequest.onBeforeRequest.addListener(details => { // cancel 表示取消本次请求 if(!showImage && details.type == 'image') return {cancel: true}; // 简单的音视频检测 // 大部分网站视频的type并不是media,且视频做了防下载处理,所以这里仅仅是为了演示效果,无实际意义 if(details.type == 'media') { chrome.notifications.create(null, { type: 'basic', iconUrl: 'img/icon.png', title: '检测到音视频', message: '音视频地址:' + details.url, }); }}, {urls: ["<all_urls>"]}, ["blocking"]);
几个可能经常用到的事件使用示例:
// 每次请求前触发,可以拿到 requestBody 数据,同时可以对本次请求作出干预修改chrome.webRequest.onBeforeRequest.addListener(details => { console.log('onBeforeRequest', details);}, {urls: ['<all_urls>']}, ['blocking', 'extraHeaders', 'requestBody']);// 发送header之前触发,可以拿到请求headers,也可以添加、修改、删除headers// 但使用有一定限制,一些特殊头部可能拿不到或者存在特殊情况,详见官网文档chrome.webRequest.onBeforeSendHeaders.addListener(details => { console.log('onBeforeSendHeaders', details);}, {urls: ['<all_urls>']}, ['blocking', 'extraHeaders', 'requestHeaders']);// 开始响应触发,可以拿到服务端返回的headerschrome.webRequest.onResponseStarted.addListener(details => { console.log('onResponseStarted', details);}, {urls: ['<all_urls>']}, ['extraHeaders', 'responseHeaders']);// 请求完成触发,能拿到的数据和onResponseStarted一样,注意无法拿到responseBodychrome.webRequest.onCompleted.addListener(details => { console.log('onCompleted', details);}, {urls: ['<all_urls>']}, ['extraHeaders', 'responseHeaders']);
上面示例中提到,使用`webRequest`API是无法拿到`responseBody`的,想要拿到的话只能采取一些变通方法,例如:
1. 重写`XmlHttpRequest`和`fetch`,增加自定义拦截事件,缺点是实现方式可能有点脏,重写不好的话可能影响正常页面;
1. `devtools`的`chrome.devtools.network.onRequestFinished`API可以拿到返回的body,缺点是必须打开开发者面板;
1. 使用`chrome.debugger.sendCommand`发送`Network.getResponseBody`命令来获取body内容,缺点也很明显,会有一个恼人的提示:

上述几种方法的实现方式这个链接基本上都有,可以参考下:<https://stackoverflow.com/questions/18534771/chrome-extension-how-to-get-http-response-body>
## 国际化
插件根目录新建一个名为`_locales`的文件夹,再在下面新建一些语言的文件夹,如`en`、`zh_CN`、`zh_TW`,然后再在每个文件夹放入一个`messages.json`,同时必须在清单文件中设置`default_locale`。
`_locales\en\messages.json`内容:
{ "pluginDesc": {"message": "A simple chrome extension demo"}, "helloWorld": {"message": "Hello World!"}}
`_locales\zh_CN\messages.json`内容:
{ "pluginDesc": {"message": "一个简单的Chrome插件demo"}, "helloWorld": {"message": "你好啊,世界!"}}
在`manifest.json`和`CSS`文件中通过`__MSG_messagename__`引入,如:
{ "description": "MSG_pluginDesc", // 默认语言 "default_locale": "zh_CN",}
JS中则直接`chrome.i18n.getMessage("helloWorld")`。
测试时,通过给chrome建立一个不同的快捷方式`chrome.exe --lang=en`来切换语言,如:

英文效果:

中文效果:
