开发调试
Chrome 插件没有严格的项目结构要求,只要保证根目录有一个 manifest.json 即可,也不需要专门的 IDE 进行开发。
开发调试时,进入插件管理页面最简洁的方式是在地址栏输入chrome://extensions/
进行访问。其他两种进入插件管理页面的方式如下图所示:
打开右上角开发者模式便可以文件夹的形式直接加载插件,否则只能安装.crx
格式的文件。默认情况下,Chrome 要求插件必须从它的 Chrome 应用商店安装,其它任何网站下载的以及自己打包的都无法直接安装。所以,其实我们可以把 crx 文件解压,然后通过开发者模式直接加载。
开发过程中,代码有任何改动都必须重新加载插件,点击对应插件的重新加载按钮或者刷新当前插件管理页面均可。如果出现错误,将会出现类似下面的界面,更改后,你需要先点击“错误”按钮进入弹窗对错误进行清除。
开发过程中遇到的更多细节可以参考官方Debugging extensions[4]文档。
3 分钟写一个浏览器插件,解决某 SDN 未登录无法复制代码的问题
我们知道,前段时间某 SDN 更新后,未登录时,无法复制文章中的代码,给开发者带来一定不便。今天我们就花 3 分钟时间写一个浏览器插件,突破这种限制。
基本思路其实很简单,通过将当前页面的document.body.contentEditable
值设置为true
来达到可复制的效果。当然,这个我们通过控制台面板或者“自定义书签“的方式也能轻松实现,这里只是让大家体验一下简单插件的开发,避免出手就是”Hello World!“,😄
某 SDN 给代码块儿设置了
user-select: none;
,导致无法进行选中复制,从这里入手亦可。
书签功能的实现可参考前端装逼技巧 108 式(一)—— 打工人[5]。
下面将逐步介绍项目各文件相关信息,您也可以直接点击这里[6]在线查看完整代码。整个项目核心代码不足 30 行,相信您能藉此迅速入门 Chrome 插件开发,激发兴趣和潜能,写出更有用的插件或者应用。
目录结构
目录结构图:
目录解释:
- images
- logo.png:插件图标。
- js
- popup.js:弹窗 popup.html 中使用的 js;
- content_script.js:需要直接注入页面的 js;
- manifest.json:配置文件,插件开发中的必备项;
- popup.html:插件弹窗;
- style.css:弹窗样式文件;
manifest.json 配置文件
{ // 清单文件的版本,必须是2或者3, // 文档见 https://developer.chrome.com/docs/extensions/mv3/manifest/manifest_version/ "manifest_version": 2, // 插件的名称 "name": "Copy SDN", // 插件的版本 "version": "1.0.0", // 插件描述 "description": "不登录依然可以在某SDN页面进行代码复制!", // 指定扩展在Chrome 工具栏中的图标,它定义了扩展图标文件位置(default_icon)、 // 悬浮提示(default_title)和点击扩展图标所显示的页面位置(default_popup) "browser_action": { "default_title": "Hello, 某SDN!", "default_icon": "/images/logo.png", // 浏览器右上角图标设置 "default_popup": "popup.html" }, // https://developer.chrome.com/docs/extensions/mv2/manifest/icons/ // 128x128 的图标;它在安装期间和 Chrome 网上应用店使用 // 48x48 图标,用于扩展程序管理页面 (chrome://extensions) // 16x16 图标用作扩展页面的收藏夹图标 // 这里只写一个其实也是可以的 "icons": { "128": "/images/logo.png" }, // 需要直接注入页面的JS "content_scripts": [ { //"matches": ["http://*/*", "https://*/*"],"<all_urls>" 表示匹配所有地址 "matches": ["https://blog.csdn.net/*"], // 多个JS按顺序注入 "js": ["/js/content_script.js"], // "css": ["css/custom.css"], // 代码注入的时间,可选值: "document_start", "document_end", or "document_idle", // document_idle表示页面空闲时,为默认值 "run_at": "document_start" } ], // 定义了扩展需要向 Chrome 申请的权限,比如通过 XMLHttpRequest 跨域请求数据、访问浏览器选项卡(tabs) // 获取当前活动选项卡(activeTab)、浏览器通知(notifications)、存储(storage)等,可以根据需要添加。 "permissions": ["tabs"] }
popup.html 和 css 制作插件弹窗
popup.html
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="./style.css" /> <title>Copy SDN</title> </head> <body> 允许复制Code:<input type="checkbox" class="switch" id="toggle" /> <!-- 这里引入了popup.js,来给popup弹窗添加一些交互功能 --> <script src="./js/popup.js"></script> </body> </html>
弹窗样式文件style.css
:
body { width: 160px; height: 24px; background-color: lavender; display: flex; justify-content: center; align-items: center; } /* Switch开关样式 */ input[type='checkbox'].switch { outline: none; appearance: none; -webkit-appearance: none; -moz-appearance: none; position: relative; width: 40px; height: 20px; background: #ccc; border-radius: 10px; transition: border-color 0.3s, background-color 0.3s; } input[type='checkbox'].switch::after { content: ''; display: inline-block; width: 1rem; height: 1rem; border-radius: 50%; background: #fff; box-shadow: 0, 0, 2px, #999; transition: 0.4s; top: 2px; position: absolute; left: 2px; } input[type='checkbox'].switch:checked { background: rgb(19, 206, 102); } /* 当input[type=checkbox]被选中时:伪元素显示下面样式 位置发生变化 */ input[type='checkbox'].switch:checked::after { content: ''; position: absolute; left: 55%; top: 2px; }
以上代码将构建出如下插件弹窗 UI:
popup.js 给弹窗添加交互
// 这里的js其实是操作popup.html产生的dom的 document.addEventListener('DOMContentLoaded', function () { // 获取开关按钮的初始值。这里{ type: 'get_editable' }是可以随意定义的,可以传递任何你想传递的信息 sendMessageToContentScript({ type: 'get_editable' }, (response) => { toggle.checked = ['true', true].includes(response) ? 'checked' : null; }); // 切换contentEditable状态 toggle.addEventListener('change', () => { sendMessageToContentScript({ type: 'toggle' }); }); }); // 向content_scripts发送消息的函数 function sendMessageToContentScript(message, callback) { // 这里用到了tabs,所以前面配置文件需要配置"permissions": ["tabs"] chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { chrome.tabs.sendMessage(tabs[0].id, message, (response) => { if (callback) callback(response); }); }); }
content_script.js 向页面注入 JS
// 所谓content-scripts,其实就是Chrome插件中向页面注入脚本的一种形式(虽然名为script,其实还可以包括css), // 借助content-scripts我们可以实现通过配置的方式轻松向指定页面注入JS和CSS(如果需要动态注入,可以参考下文), // 最常见的比如:广告屏蔽、页面CSS定制,等等。 // 接收消息 chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { // 数据处理和返回。是不是有点类似redux中reducer数据处理的感觉 switch (request.type) { case 'get_editable': // 将当前文档是否可编辑的信息返回给popup,控制开关的形态 sendResponse(document.body.contentEditable); break; case 'toggle': // 切换可编辑状态 document.body.contentEditable = ![true, 'true'].includes( document.body.contentEditable ); default: break; } });
打包与发布
在插件管理页左上角有一个“打包扩展程序”的按钮,点击就会出现如下界面,选择要打包的文件夹进行打包即可,你会得到一个.crx
的压缩文件,这个实际上就是你经常安装插件时安装的压缩包文件。
要发布到 Google 应用商店的话,需要先支付 5 美元的注册费成为开发者:Register as a Chrome Web Store Developer[7]。