Bootstrap

浏览器插件:那些你需会的操作

Chrome扩展程序序列文章:

1、 配置文件(manifest.json)详解
{
  "manifest_version": 2, // manifest文件版本号。Chrome18开始必须为2
  "page_action": {}, // 地址栏右侧图标管理。包含图标及页面设置等
  "theme": {}, // 用于更改整改浏览器的主题
  "app": {}, // 指定扩展需要跳转到的URL
  "background":{}, // 指定扩展进程的background运行环境
  "chrome_url_overrides":{ // 替换页面,将原定显示的页面替换为自定义页面
    "pageToOverride":"", // 页面
    "bookmarks":"", // 书签
    "history":"", // 历史
    "newtab":"" // 新标签页
  }, 
  "content_scripts":[{ // 指定向web页面注入的脚本/css。
    "matches":[], // 匹配的url
    "css":[],  // 注入的css资源路径
    "js":[],   // 注入的js资源路径
    "run_at":""  // 注入的时机。 document_start|end|idle
    "all_frames":1, // 是否运行在所有frame中
    ...
  }], 
  "content_security_policy": "",  // 安全策略
  "homepage_url": "http://xxx", // 扩展的官方主页
  "options_page": "", // 选项页。用于在扩展管理页面跳转到选项设置
  "permissions":[   // 权限
     "bookmarks",   // 启用书签权限
     "contextMenus", // 启用右键菜单权限
     "cookies",     // 启用cookie权限
     "experimental", // 启用chrome的实验性功能API
     "history",     // 启用history权限
     "notifications",  // 启用桌面通知权限
     "tabs",         // 启用标签权限
     "activeTab",    // 启用活动标签权限
     ...
  ],  
  "requirements": {}, // 指定所需要的特殊技术。目前只支持"3D"
  "update_url": "",   // 自动升级
  "plugins":[],  // 扩展。 可调用第三方扩展
  "web_accessible_resources": [] // 指定资源路径,为String数组
  ...
}
2、如何优雅处理前后通信

问题:传统处理通信,当请求众多,background.js中监听的请求处理逻辑大,手动引入注册监听费事。

// CS页面发起请求
chrome.extension.sendRequest({
  ...
},function(res){

})

// background.js监听请求
chrome.extension.onRequest.addListener(
  function(request,sender,sendResponse){
    switch(...){ 
      case ...: // 通过request匹配出请求标识
      ...;      // 引入或编写对应的业务逻辑
      break;
    }  
  }
)

解决方案:工程化,通过webpack的能力,自动注册监听

requestModule/
  index.js    // 获取所有的请求监听模块
  requestModule1.js  // 请求监听模块
  requestModule2.js
regesiterRequest.js  // 注册监听
// requestModule/index.js

// 注册所有的导出的请求监听函数
let requestProvider = {}

//获取一系列完整的依赖关系 参数解释:(目录,是否包含子目录,匹配文件)
const request = require.context('./', false, /^((?!index).)*\.js$/)
request.keys().forEach(path => {
  const module = request(path).default || request(path)
  Object.keys(module).forEach(key => {
    if (module[key] && requestProvider[key]) {
      throw new Error(`${key}该函数名已被占用,请更换函数名`)
    }
    requestProvider[key] = module[key]
  })
})
export default requestProvider

// regesiterRequest.js

import isFunction from 'lodash/isFunction'
import requestProvider from './requestModule/index'
// 注册所有的请求监听
chrome.extension.onRequest.addListener(
  function(request, sender, sendResponse) {
    if (!request.name) {
      throw 'request的name值,是必传参数!'
    }
    if (filterFn.indexOf(request.name) > -1) {
      return
    }
    if (!isFunction(requestProvider[request.name])) {
      console.warn(`requestProvider中不存在 ${request.name} 这个方法!`)
      return
    }
    if(typeof requestProvider[request.name] === 'function'){
      requestProvider[request.name](request, sendResponse)
    }
  }
);
3、拦截HTTP请求

a、声明权限(manifest.json)

{
  "permissions":{
    "webRequest"
  }
}

b、介绍onBeforeSendHeaders的使用:允许添加/修改/删除request headers

// 例子:删除所有的request中User-Agent的Header
chrome.webRequest.onBeforeSendHeaders.addListener(
  function(details) {
    for (var i = 0; i < details.requestHeaders.length; ++i) {
      if (details.requestHeaders[i].name === 'User-Agent') {
        details.requestHeaders.splice(i, 1);
        break;
      }
    }
    return {requestHeaders: details.requestHeaders};
  },
  {urls: [""]},
  ["blocking", "requestHeaders"]
);

c、介绍onBeforeRequest的使用:在TCP连接建立之后和HTTP数据发送之前被调用的事件

// 例子:阻止发往百度的request
chrome.webRequest.onBeforeRequest.addListener(
  function(details) {
    return {cancel: details.url.indexOf("://www.baidu.com/") != -1};
  },
  {urls: [""]},
  ["blocking"]
);

d、API文档

// onBeforeSendHeaders的callback的参数details结构
details = {
    tabId: integer, //如果没有和tab关联则返回-1
    parentFrameId: integer,
    url: string,
    timeStamp: double,
    //0表示request是在main frame里发生的
    frameId: integer,
    requestId: string,
    requestHeaders: HttpHeaders, // optional
    type: enumerated_string, //value in:  ["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other"]
    method: string //标准HTTP方法
};

RequestFilter = {
    tabId: interger, //optional
    //URL的数组,或者是匹配URL的pattern
    urls: array_of_string,
    //可选的值有:"main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other"
    types: array_of_enumerated_string, //optional
    windowId: integer //optional
};

// 设置了blocking关键字的就用这个object来作为block的规则了
BolockingResponse = {
    //为true的话request被cancel,在onBeforeRequest里面用哦
    cancel: boolean, //optional
    //只在onBeforeRequest事件中使用,用来掉包的关键属性!!!
    redirectUrl: string, //option
    //只用在onHeadersReceived事件里,在浏览器返给server时把header给掉包
    responseHeaders: HttpHeaders //optional
    //只在onBeforeSendHeaders事件中使用。是另一个用来掉包的关键属性!!!
    requestHeaders: HttpHeaders //optional
    //只在onAuthRequred事件中使用,当然也是用来掉包的
    authCredentials: object //optional
};
4、 域绝缘下,操作PageScript页的JS对象

往页面注册脚本,操作对象。常用于对编辑器的功能增强需求。

const originInject = function(jsPath, id, cb) {
  if(id && document.getElementById(id)){
	  typeof cb==="function" && cb()
	  return;
  }

  jsPath = jsPath || ''
  var temp = document.createElement('script')
  temp.setAttribute('type', 'text/javascript')
  // 获得的地址类似:chrome-extension://****/js/inject.js
  temp.src = chrome.extension.getURL(jsPath)
  if(id){
	  temp.id = id
  }
  temp.onload = function()
  {
	typeof cb=="function" && cb()
  };
  document.head.appendChild(temp)
}
5、开发支持热更新

webpack-chrome-extension-reloader:扩展程序热更新插件

// webpack.config.js
{
  ...
  plugins:initPlugins.concat([new ChromeReloadPlugin({
  port: config.port,
  reloadPage: true,
  entries: {
    // 背景页   
    background: 'background',
    // 配置ContentScripts的路径
    contentScript: Object.keys(entries).filter(function (item) {
       return item.indexOf('content-scripts') > -1
    })
  }
})])
}

--END--

作者:梁龙先森 WX:newBlob

原创作品,抄袭必究!