Chrome扩展开发-曼大Blackboard增强插件
UoM Blackboard Enhancement是一个致力于提升曼大Blackboard使用体验和学生用户生产力的Chrome扩展(Chrome-Extension),又称Chrome插件。其目的是为了解决个人Blackboard使用过程中的遇到的一些痛点,增加便捷的功能,如丰富的首页信息提示和链接入口,内置播放器增强等等。扩展本身功能完全采用JavaScript
编写,没有使用任何第三方JS库,确保运行的稳定和高效。支持Chrome/Edge浏览器。
Chrome商店插件首页 👉 UoM Blackboard Enhancement – Chrome 网上应用店 👉 GitHub开源仓库
Chrome扩展开发本身与前端开发类似,使用HTML、JS、CSS完成各项需求,唯一需要额外学习的是Chrome提供的API的使用方法,熟悉嵌入流程以实现对所针对网页的二次渲染和数据通信及存储等需求。本文以UomBbEn为例简单介绍Chrome扩展开发的主要流程和注意事项。(开发和学习过程参考自:【干货】Chrome插件(扩展)开发全攻略)
manifest.json
这是一个Chrome扩展中最重要的配置文件,该json
文件需位于项目的根目录下,Chrome会自动根据文件内的配置项判断扩展的名称、图标、资源路径、所需权限等等。本次开发中涉及的配置项和对应描述请参考以下manifest.json
示例文件。更多配置字段请查阅Chrome开发者文档(目前已更新至V3版本,本文采用的仍是V2版本的结构和配置)。
{ "manifest_version": 2, // manifest版本 "name": "UoM Blackboard Enhancement", //扩展名称 "version": "0.1.2", // 扩展版本 "description": "Improve your experience of Blackboard of the University of Manchester.", // 扩展描述 "author": "RyanXin", // 开发者 "homepage_url": "https://www.ryanxin.cn/", // 首页url,可任意设置,别浪费 "omnibox": { "keyword" : "bben" }, // 地址栏命令关键词,在浏览器地址栏输入对应关键词后可允许用户输入指令 "icons": { "128": "assets/icon-128.png", "256": "assets/icon-256.png" }, // 插件图标,可指定多个不同分辨率的图标,值为项目文件夹中的相对路径 "page_action": { // 浏览器扩展栏图标相关配置,指定page_action后仅扩展指定有效页面激活时图标才会点亮,否则可替换为browser_action "default_icon": "assets/icon-128.png", // 默认图标 "default_title": "UoM Blackboard Enhancement", // 扩展标题 "default_popup": "html/popup.html" // 单击扩展图标后的小弹窗页面 }, "content_scripts": [ // 指定需要激活扩展并注入js的网页和相关配置 { "matches": ["https://online.manchester.ac.uk/*"], // 需要操作的网页url "js": ["js/home.js", "js/common.js"], // 需要执行的js文件 "css": ["css/home-portlet.css", "css/home-courses.css", "css/home-liveSessions.css"], // 需要渲染的css文件 "run_at": "document_end" // js文件的执行时间,document_end为一经网页内容加载完毕后,其他可选值有document_start和<!-- wp:enlighter/codeblock {"language":"json"} --> <pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">{ "manifest_version": 2, // manifest版本 "name": "UoM Blackboard Enhancement", //扩展名称 "version": "0.1.2", // 扩展版本 "description": "Improve your experience of Blackboard of the University of Manchester.", // 扩展描述 "author": "RyanXin", // 开发者 "homepage_url": "https://www.ryanxin.cn/", // 首页url,可任意设置,别浪费 "omnibox": { "keyword" : "bben" }, // 地址栏命令关键词,在浏览器地址栏输入对应关键词后可允许用户输入指令 "icons": { "128": "assets/icon-128.png", "256": "assets/icon-256.png" }, // 插件图标,可指定多个不同分辨率的图标,值为项目文件夹中的相对路径 "page_action": { // 浏览器扩展栏图标相关配置,指定page_action后仅扩展指定有效页面激活时图标才会点亮,否则可替换为browser_action "default_icon": "assets/icon-128.png", // 默认图标 "default_title": "UoM Blackboard Enhancement", // 扩展标题 "default_popup": "html/popup.html" // 单击扩展图标后的小弹窗页面 }, "content_scripts": [ // 指定需要激活扩展并注入js的网页和相关配置 { "matches": ["https://online.manchester.ac.uk/*"], // 需要操作的网页url "js": ["js/home.js", "js/common.js"], // 需要执行的js文件 "css": ["css/home-portlet.css", "css/home-courses.css", "css/home-liveSessions.css"], // 需要渲染的css文件 "run_at": "document_end" // js文件的执行时间,document_end为一经网页dom内容加载完毕后,其他可选值有document_start和document_idle(默认) }, { "matches": ["https://video.manchester.ac.uk/*"], "js": ["js/player.js"], "css": ["css/player.css"], "run_at": "document_end", "all_frames": true // 指定是否为内联框架(如iframe)中的网页激活扩展 } ], "permissions": ["storage"], // 扩展所需权限声明(不声明将无法使用相应API) "web_accessible_resources": ["js/home-inject.js"], // 可由外部环境通过url直接访问的文件,一般用于动态js注入,下面会讲 "background": { "scripts": ["js/background.js"] }, // 需要在浏览器后台执行的脚本 "options_page": "html/options.html", // 扩展配置页面html "options_ui": { // 新版配置页写法 "page": "html/options.html", "chrome_style": true } }
manifest正确设置完毕后,打开Chrome扩展界面,启用开发者模式,点击”加载已解压的扩展程序“,选择项目根目录,即可将扩展导入到本地浏览器中。
JS注入
Chrome扩展根据配置通过对特定页面注入JS/CSS等文件改变网页的行为和外观,一定程度上属于对页面的二次渲染,这里主要关注JS的注入和执行。
JS的注入有两种主要形式,第一种是在页面加载的特定时间自动执行content_scripts
中指定的js文件(我们称之为扩展的js文件),该文件可以正常访问和操作所有的dom元素,但和主页面的js分属两个独立的上下文环境,因此两边的js无法共享变量、函数等信息,即无法直接进行通信。这就导致一个很大的问题,从扩展内部中的js无法直接为页面中的dom元素设定事件的回调函数,因为从主页面上下文中无法访问到扩展js中的函数。此时,便有必要引入另一种js注入方式:动态注入。
动态注入的方式也很简单,利用扩展js可以操作dom的特性,为dom添加一个script
元素标签,设置所需注入js的url,令页面自行加载新增的js脚本。所有事件回调函数(如按钮点击)都应定义在动态注入的js当中,从而被当作普通页面中的js正常执行。
// inject cuntom js var temp = document.createElement('script'); temp.setAttribute('type', 'text/javascript'); temp.src = chrome.extension.getURL('js/home-inject.js'); // get the link like:chrome-extension://xxxxxx/js/home-inject.js document.head.appendChild(temp);
需要注意的是,需要动态注入的js文件必须在manifest.json
的web_accessible_resources
字段中显式声明,只有这样才能使该文件资源可被外部访问。我们可以使用Chrome提供的APIchrome.extension.getURL('js/home-inject.js')
来获取传入资源的外部url,从而将其动态注入到dom当中。
JS数据通信
JS的注入机制带来一个直接的问题,由于所属扩展js和动态注入js之间天然隔绝的上下文环境,双方无法直接进行任何形式的数据交换,包括全局变量、参数传递等。因此,我们采用另一种方法,通过全局消息传递的方式自定义通信协议来完成数据传递。一般而言,此类需求存在于网页js向扩展js发送数据以调用所需的Chrome扩展API。
postMessage
方法可以在当前网页全局范围内发送消息。
window.postMessage({"command": "showCourse", "data": courseEle.querySelector("a").innerText}, '*');
在扩展js中注册消息事件监听函数,接收并处理收到的消息。
window.addEventListener("message", (e) => { commandHandler(e.data.command, e.data.data); }, false);
数据云存储
Chrome扩展可以使用chrome.storage
API储存和读取用户数据(需要提前申请storage
权限)。该API主要提供两个重要的接口,chrome.storage.local
和chrome.storage.sync
,前者将扩展数据保存于用户本地,后者可实现自动同步扩展数据至用户的Chrome账户。这两种接口所包含的方法相同,首先是get
方法,接受两个参数,第一个参数需传入一个字符串列表,声明将要读取的数据项key的名称,第二个参数传入一个回调函数,接收数据对象并对其做相应处理。set
方法接收一个对象字典,将对象中的键值对进行存储,存储的方式类似于web前端中的localStorage
和sessionStorage
。
以UomBbEn中的课程隐藏配置为例,首先get
获取存储的已隐藏课程数据,判断新的数据是否存在其中,若不存在则更新并set
新数据项。
// add a course name into "disabledCourses" chrome.storage.sync.get(["disabledCourses"], (items) => { let value = JSON.parse(items.disabledCourses); if (value.indexOf(data) === -1) { value.push(data); chrome.storage.sync.set({ "disabledCourses": JSON.stringify(value) }); } });
更新
Chrome扩展更新的方式十分简单,只需更新manifest.json
中version
字段的版本号的值即可。待扩展正式发布后,Chrome会自动检查更新,若有新版本的扩展会自动下载更新,无需开发者与用户执行其他任何操作。
特别注意在本地开发过程中,若需要使更新后的扩展项目生效,需要进入Chrome扩展界面,单击”更新“按钮。
发布
访问Chrome应用商店开发者信息中心,使用Google账号注册登录,首次注册登录需要缴纳5美刀的入驻费用。之后便可上传软件包,撰写详细的项目说明和宣传内容,再等待官方审核后发布。注意审核时项目权限的申请尽量遵循非必要不添加的原则,这样一是可以大大提高审核通过率,二是会极大的缩短审核的所需时间。