愿你坚持不懈,努力进步,进阶成自己理想的人

—— 2017.09, 写给3年后的自己

Chrome Extension 开发总结(三):开发技巧

一、动态注入或执行JS

backgroundpopup中是不能 直接 访问DOM的,但是可以通过chrome.tabs.executeScript间接访问,如下:

  • 首先配置manifest.json,授予页面权限:
{
    // ...
    "permissions": [
        "tabs",
        "http://*/*",
        "https://*/*"
    ]
}
  • 然后就可以通过chrome.tabs.executeScript这个API来执行代码了,但是需要注意的是:即使是这种方式,原始页面的JS也不能够被访问:
// 动态执行JS代码
chrome.tabs.executeScript(tabId, {
    code: `document.body.style.backgroundColor = 'red'`
})
// 动态执行JS文件
chrome.tabs.executeScript(tabId, { file: 'some-script.js' })


二、动态注入CSS

动态注入CSS,在manifest.json上的配置与动态注入JS一致,不同的是,使用的是chrome.tabs.insertCSS这个API,示例代码如下:

chrome.tabs.insertCSS(tabId, { code: '...' })
// 或者动态注入CSS文件
chrome.tab.insertCSS(tabId, { file: 'some-style.css' })


三、获取当前窗口ID

示例代码如下:

chrome.windows.getCurrent((currentWindow) => {
    console.log(currentWindow.id)
})


四、获取当前标签页ID

在使用chrome API过程中,经常会遇到需要使用tabId的情形,那么获取当前标签页ID的方法有:

  • 第一种方法:通过chrome.tabs.query这个API进行tab的筛选
function getCurrentTabId(callback) {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        if (callback) {
            callback(tabs.length ? tabs[0].id : null)
        }
    })
}
  • 第二种方法:
function getCurrentTabId(callback) {
    chrome.windows.getCurrent((currentWindow) => {
        chrome.tabs.query({ active: true, windowId: currentWindow.id }, (tabs) => {
            if (callback) {
                callback(tabs.length ? tabs[0].id : null)
            }
        })
    })
}


五、本地存储

Chrome extension中,其本地存储提供了chrome.storage这一API,有别于原生的localStorage的是:

  • chrome.storage是针对插件全局的,即使是在background中保存的数据,也能够在content script中获得
  • chrome.storage.sync可以跟随当前浏览器登录用户,会自动同步数据,从而用户在当前电脑的设置会同步到其他电脑中。在未登录或者未联网的情况下,则先保存到本地,联网登录后再进行同步

要使用chrome.storage,首先需要在manifest.jsonpermissions中添加storage这一值。然后其设置值和取值的方法如下:

chrome.storage.sync.set({
    prop1: 'A',
    prop2: 'B'
})
chrome.storage.sync.get(['prop1'], (item) => console.log(item)) // 输出:{ prop1: 'A' }


六、拦截网络请求

通过webRequest这系列的API,我们可以对任何的HTTP请求进行修改和定制。使用它之前,同样需要在manifest.jsonpermissions中申请权限,即添加webRequest这一值。以下是使用webRequest的一个例子:

// manifest.json
{
    "permissions": [
        "webRequest",
        "webRequestBlocking",
        // ...
    ]
}

background.js中:

let shouldShowImage
chrome.storage.sync.get({ shouldShowImage: true }, (items) => {
    shouldShowImage = items.shouldShowImage
})
chrome.webRequest.onBeforeRequest.addListener((details) => {
    if (!shouldShowImage && details.type === 'image') {
        return {
            cancel: true
        }
    }
    if (details.type === 'media') {
        chrome.notifications.create(null, {
            type: 'basic',
            iconUrl: 'img/icon.png',
            title: '检测到音频',
            message: `音视频地址:${details.url}`
        })
    }
}, { urls: ['<all_urls>'] }, ['blocking'])

这个例子中,对webRequest生命周期里的onBeforeRequest阶段进行了处理,这个生命周期钩子会在任何的TCP连接建立之前触发,可以取消或者重定向请求。因此,对于image类型的请求,我们视开关情况放过或者阻止请求,对于media的请求,则发送一条通知


七、国际化

ChromeExtension提供了原生支持国际化的能力,使用方式为:

  • 在插件根目录新建一个名为_locales的文件夹,
  • _locales下面新建一些语言文件夹,如:enzh_CNzh_TW
  • 每个语言文件夹下,需要包含一个messages.json文件
  • manifest.json中,设置默认的语言,如"default_locale": "zh_CN"

示例:

// manifest.json
{
    // ...
    "default_locale": "zh_CN"
}

// _locale/en/messages.json
{
    "pluginDesc": {
        "message": "description"
    },
    "name": {
        "message": "a chrome extension"
    }
}

// _locale/zh_CN/messages.json
{
    "pluginDesc": {
        "message": "描述信息"
    },
    "name": {
        "message": "一个 chrome 扩展"
    }
}

在定义完成后,则可以通过如下方式引用:

  • manifest.jsonCSS文件中:__MSG_[key]__的形式,如__MSG_pluginDesc__,如:
// manifest.json
{
    "pluginDesc": "__MSG_pluginDesc__"
}


八、常用API总结

以下列出了在开发ChromeExtension过程中所常用的一些API:

  • chrome.tabs 与浏览器的标签页系统交互,可以用来创建、修改和重整理浏览器的标签页
  • chrome.runtime 可以检索background页、返回manifest清单文件的详细信息、监听APP或者ChromeExtension生命周期中事件并作出响应,还可以使用这个API来将一个相对路径转化为带有ChromeExtension路径前缀的完整URL
  • chrome.webRequest 可以观察和分析(网络)流量,并且可以拦截、阻塞和修改正在进行中的请求
  • chrome.windows 和浏览器的窗口系统交互,可以用来创建、修改和重整理浏览器的窗口
  • chrome.storage 可以用来存储、检索和跟踪用户数据的变更
  • chrome.contextMenus 可以用来向Chrome的右键菜单中加入菜单项,还可以选择对让何种类型的对象(比如图片、超链接或者页面)应用额外的菜单项
  • chrome.devtools 一系列的用来操作开发者面板的API
  • chrome.extension 拥有可以在任意extension类型脚本中使用的工具方法,还包含了支持extension与它的content script之间,或者extensionextension之间交换信息的工具方法等。


参考资源