背景
最近ChatGPT的技术概念很火热,开发了一个node-gptcommit开源项目,主要利用GPT用来自动生成git commit的信息。
但是通过命令行工具来生成preview感觉有点不太友好,因此在想有没有另外一种可能将其变得更加好用,然后想到一个场景:
- 在合mr的时候,以及代码review,很多git commit 文件,需要一一个看
在这个时刻,自动生成commit信息就显得很有用,因此有个想法就是将node-gptcommit
做成chrome插件,然后帮助MR的开发者能够快速了解此次MR的所有内容。
当然仅仅是开发一个chrome插件大家上网随便搜一下就知道怎么开发,因此为了本文更加有干货,需要更加深入的知识,所以本文分为两部分:
- Chrome插件开发,如何快速开发一个插件
- Chrome插件架构设计,了解Chrome插件背后的架构设计,以及前端插件架构体系
Chrome插件开发
Chrome插件是一种浏览器扩展程序,可以增强浏览器的功能和用户体验。Chrome插件可以添加新的工具栏、菜单、快捷键、热键等,还可以修改网页的行为和外观,以及与网页交互,实现各种功能。常见的Chrome插件包括广告拦截器、翻译插件、下载管理器、音乐播放器等。
开发Chrome插件其实很简单,因为Chrome的插件主要描述文件mainfest.json
,还有包含在里面的一些其他文件,具体如下:
- manifest.json文件,用于指定插件的名称、版本号、描述等信息,以及指定插件的各种权限和资源
- popup.html文件,主要用于显示插件的弹出窗口,提供用户界面和交互功能
- content scripts文件,用于向网页注入JavaScript代码,从而实现与网页的交互。这些脚本可以访问网页的DOM和JavaScript对象,并且可以向网页发送消息和接收来自网页的消息。
- background scripts文件,用于在Chrome插件的后台运行JavaScript代码,可以与popup、contentscript等通讯,可以实现发起网络请求、拦截用户访问请求等操作
一个标准的Chrome插件目录结构如下:
my-extension/ ├── _locales/ │ ├── en/ │ │ └── messages.json │ └── zh/ │ └── messages.json ├── css/ │ └── popup.css ├── img/ │ ├── icon128.png │ ├── icon48.png │ └── icon16.png ├── js/ │ ├── background.js │ ├── content.js │ └── popup.js ├── popup.html ├── manifest.json └── README.md
其中,各个文件夹和文件的作用如下:
_locales/
:存放插件的本地化文件,用于支持不同语言版本的插件。css/
:存放插件的CSS样式文件。img/
:存放插件的图标文件。js/
:存放插件的JavaScript文件,包括background scripts、content scripts和popup scripts等。popup.html
:插件的popup页面。manifest.json
:插件的配置文件,用于指定插件的名称、版本号、描述等信息,以及指定插件的各种权限和资源。README.md
:插件的说明文档,用于介绍插件的功能和使用方法等。
manifest.json
manifest.json是Chrome插件的配置文件,用于指定插件的名称、版本号、描述等信息,以及指定插件的各种权限和资源。
下面是一个完整的manifest.json
文件的例子:
{ "manifest_version": 2, "name": "My Chrome Extension", "version": "1.0", "description": "This is a description of my Chrome extension", "icons": { "16": "icon16.png", "48": "icon48.png", "128": "icon128.png" }, "background": { "scripts": ["background.js"] }, "content_scripts": [ { "matches": ["*://*.example.com/*"], "js": ["content.js"] } ], "permissions": [ "tabs", "*://*.example.com/*" ] }
其中:
- manifest_version:指定manifest.json文件的版本号,一般为2
- name:指定插件的名称
- version:指定插件的版本号
- description:指定插件的描述
- icons:指定插件的图标,这里16、 48、128指的是需要展示在不同场景插件的图标
- browser_action:可以配置的popup页面和图标
- permissions:插件需要的权限,例如访问浏览器标签页,以及只允许在
*://.example.com/
域名才有效
还有两个相对复杂配置项:
- content_scripts: content_scripts指定插件需要向
*://.example.com/
注入JavaScript代码,代码位于content.js
文件中 - background:指定插件的
background.js
文件为background scripts
popup.html
就是一个简单html文件,里面需要插件的UI展示,demo如下:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>My Chrome Extension</title> <link rel="stylesheet" href="popup.css"> <script src="popup.js"></script> </head> <body> <h1>Hello, World!</h1> <p>This is my first Chrome extension.</p> <button id="myButton">Click me!</button> </body> </html>
popup.html文件可以包含任何HTML、CSS和JavaScript代码,用于构建插件的用户界面和交互功能。在上面的例子中,popup.html文件包含一个标题、一段文本和一个按钮,按钮的点击事件可以在popup.js文件中进行处理。需要注意的是,popup.html文件的UI通常比较简单,因为其主要作用是提供与用户的交互和反馈。
除了HTML、CSS和JavaScript代码外,popup.html文件还可以包含其他类型的资源文件,例如图片、音频或视频文件等。这些资源文件可以放置在相应的目录下,并在HTML代码中使用相应的路径引用。在实际开发中,可以根据需要对popup.html文件进行扩展和修改,以实现更复杂的用户界面和交互功能。
content_scripts
content_scripts
你可以当成用户页面内的js,Content scripts常用于修改网页的行为和外观,以及与网页交互,实现各种功能。例如,可以通过content scripts实现自动填充表单、隐藏广告、添加自定义菜单等功能。下面是一个简单的content_scripts的例子,使得当用户进入Google搜索页面时,输入框默认输入“Hello World!”:
// manifest.json { "name": "My Content Script", "version": "1.0", "manifest_version": 2, "content_scripts": [ { "matches": ["<https://www.google.com/*>"], "js": ["content.js"] } ] } // content.js document.querySelector('input[name="q"]').value = 'Hello World!';
在这个例子中,我们在manifest.json文件中指定了一个content_scripts,用于在用户进入https://www.google.com/*
网页时自动向Google搜索框中输入“Hello World!”
。具体实现的代码在content.js文件中,使用document.querySelector()函数获取到输入框,并将其value属性设置为“Hello World!”。
需要注意的是,content_scripts
只能访问网页的DOM和JavaScript对象,并不能直接访问插件的资源和API。如果插件需要向网页发送消息或从网页接收消息,可以使用chrome.runtime.sendMessage()
和chrome.runtime.onMessage()
等API实现。
backgroundjs
backgroundjs
是运行在后台的一个js文件,里面可以实现各种监听,如:tab监听、插件初始化、sendmessage、onmessage等事件,下面我们来实现一个简单的发送网络请求:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { if (request.type === 'fetch') { fetch(request.url) .then(response => response.text()) .then(data => sendResponse({ success: true, data })) .catch(error => sendResponse({ success: false, error })); return true; } });
常用的监听事件有:
chrome.runtime.onMessage.addListener()
, 监听插件传递过来的的消息事件chrome.runtime.onInstalled.addListener()
, 监听插件的安装事件chrome.runtime.onStartup.addListener()
, 监听插件的启动事件chrome.tabs.onUpdated.addListener()
, 监听浏览器标签页更新事件
当然对应的事件都需要在manifest.json
中permissions
去配置才能开启。
Chrome插件设计架构
既然弄明白了如何开发一个Chrome插件,那么作为一个合格的程序员,我们不仅要知道是什么,还要知道为什么,这才能让你不断地进步。
我们回头再来思考一下,插件有什么用,以及为什么要有插件?
插件最大的作用就是扩展主应用App的功能,同时支持可插拔特性,就是有没有插件,都不会影响现有主应用功能正常使用。
接下来我们来思考,如何我来开发一个chrome插件系统,我应该如何设计呢?下面是简易Chrome插件系统的架构图:
有了上面的架构,我们可以大概知道实现一个插件系统需要几个功能点:
- 插件管理,需要有插件上传、加载、版本机制等功能
- 暴露能力,需要确定暴露哪些API能力,同时还需要展示插件的UI
- 隔离环境,需要确保插件的隔离性,不影响主应用的运行,这里就需要我们去用iframe的模式去执行
插件系统设计
有了对chrome的插件架构有一定了解后,如果这个时候需要我们需要对某个平台去实现一个插件系统,我们应当如何入手以及设计?
在做插件系统之前,我们需要再明确一下插件的定义:
插件是平台核心功能的一种扩展,是在平台生命周期流程中的不同节点去扩展或调整功能
接下来,我们按照几个步骤去实现插件:
插件系统的定位
我们的插件肯定依附于某个平台或应用上的,因此我们需要先对平台能力做一个定位,就以Chrome为例子,chrome浏览器作为展示网页为核心功能。
那么如果去设计一个插件系统,肯定是围绕着网页展示的扩展功能去做插件系统设计。
插件系统的基础能力
插件系统的基础能力包括以下几点:
- 插件管理,需要插件管理界面,面对开发者:,提供上传、版本管理等功能,面对使用者:需要提供安装、查询、删除等功能
- 插件开发,需要给开发者提供工具、文档、规范、发布流程等方便的信息
插件系统架构设计
针对不同平台,可能需要插件架构是不一样的,如:Chrome,采用的并行机制,不同插件可以在同一时间加载、输出。
前端领域很多有插件实现架构,按照执行顺序可以大概分为如下几种:
插件架构 | 描述 | 适⽤场景 | 框架或应用 |
顺序执行 | 显然就是从头执行到尾,不断对内容做修改 | ⼤多数场景 | babel plugin |
瀑布流 | 上一层输出就是下一层输入,这里就是管道概念 | 管道 | Gulp |
洋葱 | 不仅关注输入,而且关心输出,还可以随时直接输出 | 进出 | Koa |
并发执⾏ | 多个输入,对多个插件输出做一个总结输出 | ⽆序任务 | webpack complier |
(参考自前端早早聊 2023年前端插件设计专场 【洋葱:插件化设计在前端领域的应用】)
同时,从架构上还需要考虑以下几点:
- 插件接口,指的是哪些平台哪些接口Hook,允许插件访问或改变
- 插件管理器,指的平台如何管理插件,包括更新、审核、插件的文件模式等。
- 插件加载器,指的平台如何加载插件,是在加载过程需要如何塞入暴露能力等。
- 插件生命周期,指的是一个插件有哪些生命周期节点,如:安装、加载、运行、卸载、禁用等
插件系统的安全和稳定性
我们不仅要考虑插件功能实现,还需要考虑插件的安全和稳定性,主要从以下几方面入手:
- 插件的安全验证,指的是如何限制非法插件的使用
- 插件的权限控制,指的是如何限制插件的权限
- 插件的资源隔离,指的是如何确保插件其运行环境不可以访问平台其他非暴露资源
总结
经过本文,我们不仅学会了Chrome插件的开发,还对插件系统的设计有一定的认知,我们回顾一下:
- Chrome插件开发,主要组成部分为:manifest.json,popup.html,content_scripts和background.js
- 插件系统设计,主要考虑的点:管理、开发工具和文档、暴露能力、安全与稳定