chrome 插件开发

教程:

  1. https://segmentfault.com/a/1190000006949838
  2. https://segmentfault.com/a/1190000005071240
  3. https://blog.csdn.net/austin_link?t=1
  4. https://crxdoc-zh.appspot.com/extensions/getstarted (官方文档中文)
  5. https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html#%E6%A0%B8%E5%BF%83%E4%BB%8B%E7%BB%8D,这个写的非常详细认真
  6. http://www.cnblogs.com/champagne/p/, 这个是系列教程

一 注意点

  1. chrome 不允许扩展中的HTML页面直接嵌入 js脚本,所有的脚本必须作为外部src引入。
  2. manifest.json 是一个非常重要的配置文件,常用的配置项必须要了解。
  3. background.js / content-script.js , 这些文件名字不能改了,改了之后调用 chrome 开头的一些 api 就会报 null 的错误。

二 配置 manifest.json

 "browser_action": { 
//插件加载后生成图标
        "default_icon": "cc.gif",
// 鼠标悬浮到图表上显示的文字
        "default_title": "Hello CC", 
// 点击图标展示的 html 页面
        "default_popup": "popup.html" 
}, 
// 运行插件需要的权限
    "permissions": [ 
        //指定插件生效的 Url 模式
        "http://*/", 
        "bookmarks", 
        "tabs", 
        "history" 
    ], 
// 插件运行的后台环境
    "background":{//background script即插件运行的环境
// 2 种方式
        "page":"background.html"
        // "scripts": ["js/jquery-1.9.1.min.js","js/background.js"]//数组.chrome会在扩展启动时自动创建一个包含所有指定脚本的页面
    }, 
// document 插件向页面注入的 js/css 脚本.可以实现广告屏蔽、页面 css 定制
     "content_scripts": [{  
  //满足什么条件的 url 执行该插件
         "matches": ["http://*/*","https://*/*"],   
// 注入js脚本
         "js": ["js/jquery-1.9.1.min.js", "js/js.js"],   
// 注入css
"css": ["css/custom.css"],
//插件执行的时间,"document_start","document_end",
// "document_idle",表示页面空闲时
         "run_at": "document_start",  
    }] 
  • 与 browser_action 对应的还有一个 page_action,browser_action 对所有的页面生效,而 page_action 只针对特定的页面生效,page_action 与 browser_action 只能存在一个。
  • "manifest_version": 2,因为一些乱七八糟的原因,这个值必须是2.
  • popup是点击browser_action或者page_action图标时打开的一个小窗口网页,焦点离开网页就立即关闭,一般用来做一些临时性的交互。需要特别注意的是,由于单击图标打开popup,焦点离开又立即关闭,所以popup页面的生命周期一般很短,需要长时间运行的代码千万不要写在popup里面。
    在权限上,它和background非常类似,它们之间最大的不同是生命周期的不同,popup中可以直接通过chrome.extension.getBackgroundPage()获取background的window对象。

background

background.后台(姑且这么翻译吧),是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面.

background的权限非常高,几乎可以调用所有的Chrome扩展API(除了devtools),而且它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置CORS。

配置中,background可以通过page指定一张网页,也可以通过scripts直接指定一个JS,Chrome会自动为这个JS生成一个默认的网页

三 调试

扩展程序 -> 加载已解压的扩展程序
如果修改了扩展源文件,有的时候更新一下可以生效,有的时候需要把插件删除,重装一下。

四 content_scripts,注入 JS 和 CSS

在 content_scripts 配置的文件 js 文件中,可以直接操作dom,如果写了啥 chrome.tabs 之类的会显示无效 ....

content_scripts和原始页面共享DOM,但是不共享JS,如要访问页面JS(例如某个JS变量),只能通过injected js来实现。content-scripts不能访问绝大部分chrome.xxx.api,除了下面这4种:

chrome.extension(getURL , inIncognitoContext , lastError , onRequest , sendRequest)
chrome.i18n
chrome.runtime(connect , getManifest , getURL , id , onConnect , onMessage , sendMessage)
chrome.storage

其实看到这里不要悲观,这些API绝大部分时候都够用了,非要调用其它API的话,你还可以通过通信来实现让background来帮你调用(关于通信,后文有详细介绍)。

五 五种类型的 JS 对比

chrome 插件的 JS 主要可以分为这 5 类,injected script, content-script, popup js, background.jsdevtools.js

5.1 权限对比

js 种类 可访问的 API DOM访问情况 JS访问情况 直接访问
injected js 和普通的JS无任何差别,不能访问任何扩展API 可以访问 可以访问 不可以
content script 只能访问 extension/ runtime 等部分 API 可以访问 不可以 不可以
popup js 可以访问绝大部分 API,除了 devtools 不可以直接访问 不可以 可以
backgroundjs 可以访问绝大部分 API,除了 devtools 不可以直接访问 不可以 可以
devtools js 只能访问 devtools/ extension/ runtime 等部分 API 可以 可以 不可以

六 消息通信

Chrome 插件中存在 5 种js,那么它们之间如何通信呢?popup 和 background 其实几乎可以视为同一种东西,因为它们可访问的 API 的 一样、通信机制都一样、都可以跨域。

6.1 互相通信概览

injected-script content-script popup-js background-js
injected-script -- window.postMessage -- --
content-script window.postMessage -- chrome.runtime.sendMessage chrome.runtime.connect chrome.runtime.sendMessage chrome.runtime.connect
popup-js -- chrome.tabs.sendMessage chrome.tabs.connect -- chrome.extension.getBackgroundPage
background-js -- chrome.tabs.sendMessage chrome.tabs.connect chrome.extension.getViews --
devtools-js chrome.devtools.inspectedWindow.eval -- chrome.runtime.sendMessage chrome.runtime.sendMessage

6.2 通信详细介绍

6.2.1 popup 和 background

popup 可以直接调用 background 中的 js 方法,也可以直接访问 background 的 DOM:

// background.js
function test(){
  alert('我是 background');
}

// popup.js
let bg = chrome.extension.getBackgroundPage();
// 访问bg 的函数
bg.test();
// 访问 bg 的 DOM
alert(bg.document.body.innerHTML);

background 访问 popup, 需要 popup 已经打开:

let views = chrome.extension.getViews({type:'popup'});
if(views.length > 0){
  console.log(views[0].location.href);
}

6.2.2 popup 和 background 向 content 主动发送消息

backgroud.js 或者 popup.js

function sendMessageToContentScript(message,callback){
  chrome.tabs.query({active:true, currentWindow: true}, function(tabs){
    chrome.tabs.sendMessage(tabs[0].id, message, function(response){
      if(callback){
        callback(response)
    }
})
})
}

// 调用
sendMessageToContentScript({cmd:'test', value:'nihao'},function(response){
  console.log("来自 content 的回复:" + response);
})

content-script.js 接受消息:

chrome.runtime.onMessage.addListener((request,sender,sendResponse){
  if (request.cmd === "test"){
      alert(request.value);
    }
  sendResponse("我收到了你的消息");
})

6.2.3 content-script 主动发消息给后台

content-script.js

chrome.runtime.sendMessage({greeting: '你好,我主动发消息'},(response)=>{
  console.log("收到来自后台的回复"+ response);
})

background.js 或 popup.js

// 监听来自 content-script 的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse)=>{
  console.log(request, sender, sendResponse);
  sendResponse('我是后台' + JSON.stringify(request));
})

参数说明:

  • request 是接受的数据
  • sender 是一个对象,包含了许多关于发出者的信息:
    • tab,关于 tab 的一些信息。
    • frameId
    • id , 猜测是唯一标识符
    • url,发送者的url。
  • sendResponse, 是个回调函数而已。

注意事项:

  • content_scripts向popup主动发消息的前提是popup必须打开!否则需要利用background作中转;
  • 如果background和popup同时监听,那么它们都可以同时收到消息,但是只有一个可以sendResponse,一个先发送了,那么另外一个再发送就无效;

6.2.4 injected script 和 content-script

content-script 和 页面内的脚本(injected-script 自然也属于页面内的脚本)之间唯一共享的东西就是 页面 的 DOM元素。
injected-script:

window.postMessgae({"test":"nihao"},"*")

content script:

window.addEventListener("message",(e)=>{
  console.log(e.data);
})

6.3 长连接

Chrome插件中有2种通信方式,一个是短连接(chrome.tabs.sendMessage和chrome.runtime.sendMessage),一个是长连接(chrome.tabs.connect和chrome.runtime.connect)。

短连接的话就是挤牙膏一样,我发送一下,你收到了再回复一下,如果对方不回复,你只能重新发,而长连接类似WebSocket会一直建立连接,双方可以随时互发消息。
popup.js:

getCurrentTabId((tabId) => {
    var port = chrome.tabs.connect(tabId, {name: 'test-connect'});
    port.postMessage({question: '你是谁啊?'});
    port.onMessage.addListener(function(msg) {
        alert('收到消息:'+msg.answer);
        if(msg.answer && msg.answer.startsWith('我是'))
        {
            port.postMessage({question: '哦,原来是你啊!'});
        }
    });
});

content-script.js:

// 监听长连接
chrome.runtime.onConnect.addListener(function(port) {
    console.log(port);
    if(port.name == 'test-connect') {
        port.onMessage.addListener(function(msg) {
            console.log('收到长连接消息:', msg);
            if(msg.question == '你是谁啊?') port.postMessage({answer: '我是你爸!'});
        });
    }
});

你可能感兴趣的:(chrome 插件开发)