chrome浏览器的插件相信大家在日常生活中都会用到,应用市场上的各种插件不仅个性化了我们的浏览器UI设置(比如各种tab页主题设置插件),同时也提供了更为强大的辅助工具。

对我们前端来说。用好各种插件也能极大的提升日常工作中的开发效率。那么问题来了。我们如何能够更快更好的使用chrome插件呢?

且听笔者娓娓道来~


在日常工作过程中。笔者接到了一个普通的页面开发的需求;仅需要展示一个表格数据即可,当后端的接口文档发给我时,我对着一个表格大约七八十个的字段陷入了沉思😱(我不仅要调整映射,还要把下划线改成驼峰),本着懒癌症晚期患者的原则,我拿起了chrome插件的学习手册。。。

一、工欲善其事必先利其器

chrome插件基本介绍

  • Manifest (清单文件)
  • Service_worker(后台脚本)
  • UI Elements (页面元素)
  • Content Script (内容脚本)

整体的结构如下所示:
结构图

Manifest

相当于插件的 meta 信息,包含插件的名称、版本号、图标、脚本文件名称等,这个文件是每个插件都必须提供的。

主要的配置项介绍如下所示:

{
"name": "extract table", // 名称
"description": "An introductory tutorial", // 描述
"version": "1.0", // 插件的版本
"manifest_version": 3, // 清单的版本,目前都是使用 V3
// action 字段主要描述点击右上角图标弹出的页面
"action": {
"default_popup": "index.html" // 对应的入口 html 文件(Popup 在后面介绍)
"default_title": "Garfish Module",
"default_icon": {
"16": "favicon.ico",
"48": "favicon.ico",
"128": "favicon.ico"
}
},
// 需要使用一些特殊 API 时需要在 permissions 声明权限,会提示给用户
"permissions": ["storage", "scripting"],
// 哪些域名允许使用插件
"host_permissions": ["<all_urls>"],
// 声明 background service worker 的路径,在后面介绍
"background": {
"service_worker": "background.js"
},
// 声明 content script 的入口文件路径、允许使用的域名以及执行时机
"content_scripts": [{
"js": ["content.js"]
"matches": ["<all_urls>"],
// "document_start" "document_idle" "document_end" 三个值
"run_at": "document_idle",
}

Service_worker(后台脚本)

manifest v2中称之为background script,v3统一成 service_worker.

可以调用全部的 chrome 插件 API,实现跨域请求、网页截屏、弹出 chrome 通知消息等功能。插件激活后会在浏览器后台默默运行。

UI Elements (页面元素)

主要包括点击插件的弹出框(popup)、插件的配置页(optons),插入到控制台的tab页等等。

Content Script (内容脚本)

是插件注入到页面的脚本,但是不会体现在页面 DOM 结构里。在一个单独的沙盒内运行,可以操作页面的DOM、调用有限的插件API等。

页面通信

由于content_scripts是在网页中运行的,而非在扩展的上下文中,因此它们通常需要某种方式与扩展的其余部分进行通信。extensions 和content_scripts之间的通信通过使用消息传递进行。 任何一方都可以侦听从另一端发送的消息,并在同一通道上做出响应。

从content_scripts发送消息的代码如下所示:

(async () => {
const response = await chrome.runtime.sendMessage({greeting: "hello"});
console.log(response);
})();

从【options_page,bakcground,popup】等位置发送到 content_scripts的 代码示例:

(async () => {
const [tab] = await chrome.tabs.query({active: true, lastFocusedWindow: true});
const response = await chrome.tabs.sendMessage(tab.id, {greeting: "hello"});
console.log(response);
})();

通过runtime.onMessage事件侦听器来处理消息

chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ? "from a content script:" + sender.tab.url :"from the extension");
if (request.greeting === "hello")
sendResponse({farewell: "goodbye"});
}
);

除此之外,插件还提供了长连接来实现通信。

调试

修改了devtools页面的代码时,需要先在 扩展程序页面下重新加载插件,然后关闭再打开开发者工具即可,无需刷新页面。

二、牛刀小试

首先创建好我们的项目文件,设置好我们需要的配置

image.png

在popup中实现界面的绘制和按钮事件的绑定

var targetDom = document.getElementById('targetDom');
var columnKey = document.getElementById('key');
var btn = document.getElementById("clickBtn");
var result = document.querySelector('#result');
//发送dom给content去解析
btn.addEventListener('click',async ()=>{
var classname = targetDom.value;
if(classname){
selector = classname.split(' ').map(i=>`.${i}`).join('');
chrome.tabs.query({active:true,currentWindow:true},(tabs)=>{
chrome.tabs.sendMessage(tabs[0].id,{selector,columnKey},res=>{
console.log('response',res);
result.innerText = JSON.stringify(res,null,4);
})
})
}
})

在content_scripts中接收参数并完成dom结构解析,最终把数据返回给popup送显。

这样我们就完成了一个基础版的插件开发。当然,在现代开发模式下,我们肯定不能接受如此简陋的开发模式,那么我们如何进行下一步的优化呢,我们下篇继续~