Chrome浏览器插件(Chrome Extension,简称CRX)大家已经非常熟悉了。目前的Chrome Extension开发应该按照Manifest V3的规范。按照谷歌官方的通知,2023年6月开始,将不允许发布Manifest V2的Chrome Extension,预计到2024年将全面下架Manifest V2的Chrome Extension。Manifest V2即将退出历史舞台,因此,本系列教程将不再提及Manifest V2,全部为Manifest V3(简称MV3)内容。
我最近一次发表的关于Chrome插件开发教程是在2023年2月6日《2023新春版:React+Antd开发Chrome插件教程(Manifest V3)》,那个版本是基于React官方的Create-React-App(简称CRA)进行架构设计。但是在React官方网站更新后,就没有再提到Create-React-App了,反而推荐使用其他社区的脚手架工具,其中就提到了Vite。而Create-React-App也一直停留在5.0.1版本,从2022年4月13日至今没有更新,看样子也被官方抛弃了。当然Create-React-App仍然具有它的使用价值。
很多前端开发的小伙伴早就转向Vite了,因此本系列教程的2023金秋版应运而生,详细讲解如何基于Vite开发Chrome插件。希望能够帮助各位省去摸索的时间,少走弯路,快速完成项目开发。
先睹为快
先看下目录了解本教程都有哪些内容。强烈建议按照以下章节一步一步边学边做,可以快速掌握整个项目的原理和细节,在以后遇到新问题的时候,可以知道从哪个环节入手。
章节目录
1 初始化项目
• 1.1 使用Vite新建项目
• 1.2 安装并运行项目
• 1.3 精简项目
2 Vite基础配置
• 2.1 配置国内镜像源
• 2.2 支持Sass/Scss/Less/Stylus
• 2.3 设置dev环境的Server端口号
• 2.4 设置dev环境自动打开浏览器
• 2.5 设置路径别名
3 Chrome Extension基础
• 3.1 Manifest V3概述
• 3.2 Manifest V3 主要新特性
• 3.3 Chrome Extension的组成
• 3.4 规划build生成的目录结构
• 3.5 配置manifest.json
4 项目目录结构设计
5 针对Chrome Extension的Vite配置
• 5.1 设置全局配置
• 5.2 设置popup的build配置
• 5.3 设置content script的build配置
• 5.4 设置background script的build配置
• 5.5 通过补充脚本合并三个build
6 设置公用样式及集成Ant Design
• 6.1 关于样式命名规范
• 6.2 设置全局公用样式
• 6.3 集成Ant Design
• 6.4 设置Antd为中文语言
7 Popup开发
• 7.1 引入popup页面
• 7.2 构建popup的Login页面
• 7.3 构建popup的Home页面
• 7.4 构建popup的Account页面
• 7.5 配置popup页面路由
• 7.6 构建Nav导航组件
• 7.7 构建Entry二级路由框架页面
• 7.8 调整popup入口页面,打通全部路由
• 7.9 完善Login页面的登录跳转
• 7.10 设置popup页面尺寸
8 build项目并载入插件
9 background script开发
• 9.1 设置允许运行popup的页面规则
• 9.2 为什么插件图标在禁用页面不变成灰色
10 content script开发
• 10.1 向目标页面注入悬浮球
• 10.2 在content script中使用Antd
11 在开发环境中调试content script
12 API请求
• 12.1 background pages不支持XMLHttpRequest(axios)
• 12.2 使用mock.js和mockjs-fetch模拟请求
• 12.3 封装API及fetch业务逻辑
• 12.4 委托background script完成API请求
• 12.5 实现popup的Login页面API请求
• 12.6 设置开发环境的反向代理请求
• 12.7 实现content script的API请求
• 12.8 关键知识点小结
13 其他说明
• 13.1 permission权限配置
• 13.2 以<script>方式向目标页面插入js
• 13.3 Service Worker调试
• 13.4 popup页面调试
• 13.5 批量升级全部项目npm依赖包
14 项目Git源码
结束语
本Demo主要依赖包版本
Node.js 18.17.1
vite 4.4.9
react 18.2.0
react-dom 18.2.0
react-router-dom 6.15.0
antd 5.8.3
mockjs 1.1.0
mockjs-fetch 2.2.0
less 4.2.0
sass 1.65.1
stylus 0.59.0
为什么没有使用CRXJS
关于Vite开发Chrome Extension,CRXJS这个Vite Plugin在技术社区中被提及较多。
本教程没有选用CRXJS,主要基于以下原因:
- 截至编写本文时,CRXJS官网只更新到了对Vite3的说明,而且CRXJS的2.0版本长期处在Beta阶段,更新频率也不高。
- 项目的核心功能尽可能选用“大官方”的原厂产品,有保障。尽可能在官方架构的基础上自己改造,把命运掌握在自己手上,发现问题自己想办法解决,也能更深入掌握核心技术。
- 依赖的外部产品越多,学习成本和维护成本也越高。
当然,如果你想了解CRXJS,可以自行查阅它的官网,本教程不使用CRXJS。
CRXJS官网
https://crxjs.dev/vite-plugin
1 初始化项目
1.1 使用Vite新建项目
※注:Vite需要Node.js版本14.18+,16+。然而,有些模板需要依赖更高的 Node 版本才能正常运行,当你的包管理器发出警告时,请注意升级你的 Node 版本。
先进入想要创建项目的目录,在这个目录下执行安装命令。
如果使用npm,执行:
npm create vite@latest
如果使用yarn,执行:
yarn create vite
执行后,会要求填写项目名称,这里我填写的是vite-react-crx,可根据情况自定。
Project name: vite-react-crx
然后,会要求选择框架,选择React:
Select a framework:
Vanilla
Vue
❯ React
Preact
Lit
Svelte
Solid
Qwik
Others
最后,选择开发语言,本教程选择JavaScript:
Select a variant:
TypeScript
TypeScript + SWC
> JavaScript
JavaScript + SWC
回答以上“灵魂三问”后,即可完成Vite项目创建。
如果没有安装yarn,可执行以下命令全局安装:
npm install --global yarn
yarn中文网站: yarn.bootcss.com/
1.2 安装并运行项目
进入项目目录,运行命令进行项目依赖包的安装。
cd vite-react-crx
yarn 或者 npm install
稍等片刻,安装完成后,执行以下命令运行项目:
yarn dev 或者 npm run dev
是不是感觉编译的速度非常快,几乎没有感知就完成了。
与Create-React-App不同,Vite默认是不会自动启动浏览器打开项目页面的。
需要手动打开以下地址访问项目:
http://localhost:5173/
Vite默认开启的端口是5173,后续章节会讲解怎么修改端口号。
1.3 精简项目
接下来,删除用不到的文件,最简化项目。
├─ /node_modules
├─ /public
- | └─ vite.svg
├─ /src
- | ├─ /assets
- | | └─ react.svg
- | ├─ App.css
| ├─ App.jsx
- | ├─ index.css
| └─ main.jsx
├─ .eslintrc.cjs
├─ .gitignore
├─ index.html
├─ package.json
├─ README.md
├─ vite.config.js
└─ yarn.lock
现在目录结构如下,清爽许多:
├─ /node_modules
├─ /public
├─ /src
| ├─ App.jsx
| └─ main.jsx
├─ .eslintrc.cjs
├─ .gitignore
├─ index.html
├─ package.json
├─ README.md
├─ vite.config.js
└─ yarn.lock
以上文件删除后,页面会报错。这是因为相应的文件引用已不存在。需要继续修改代码,先让项目正常运行起来。
逐个修改以下文件,最终精简代码依次如下:
src/App.jsx:
function App() {
return <div className="App">Vite-React-CRX</div>
}
export default App
src/main.jsx:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite React CRX</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
在上述index.html代码中,修改了网站图标,因此需要自行准备一个图标文件favicon.ico,存放在/public
目录下。当然,也可以使用svg格式。
├─ /node_modules
├─ /public
+ | └─ favicon.ico
├─ /src
| ├─ App.jsx
| └─ main.jsx
├─ .eslintrc.cjs
├─ .gitignore
├─ index.html
├─ package.json
├─ vite.config.js
└─ yarn.lock
这里你可能会问,为什么在index.html中的<link>
中引入图标的路径是"/favicon.ico",而favicon.ico明明是放在/public目录下,却不是"/public/favicon.ico"呢?
按照Vite官方说明:引入public中的资源永远应该使用根绝对路径,并且,public中的资源不应该被JavaScript文件引用。
public目录Vite官方说明:
https://cn.vitejs.dev/guide/assets.html#the-public-directory
执行yarn dev,运行效果如下:
2 Vite基础配置
2.1 配置国内镜像源
npm和yarn默认是从国外源站拉取依赖包的,为提高下载速度和稳定性,建议配置为国内镜像源。
设置yarn registry国内镜像:
yarn config set registry https://registry.npmmirror.com
设置npm registry国内镜像:
npm config set registry https://registry.npmmirror.com
如果不清楚本地当前yarn或者npm的配置,可以执行以下命令查看:
yarn查看方法:
yarn config list
npm查看方法:
npm config list
※注:本教程主要使用yarn,后续不再复述对应的npm命令。
2.2 支持Sass/Scss/Less/Stylus
Vite本身提供了对.scss/.sass/.less/.styl/.stylus文件的内置支持。无需再安装特定的Vite插件,但必须安装相应的预处理器依赖。
支持Sass/Scss,执行以下命令安装:
yarn add -D sass
支持Less,执行以下命令安装:
yarn add -D less
支持Stylus,执行以下命令安装:
yarn add -D stylus
安装后,就可以直接使用以上对应的CSS预处理语言了,非常方便。
CSS预处理Vite官方说明:
https://cn.vitejs.dev/guide/features.html#css-pre-processors
2.3 设置dev环境的Server端口号
dev server默认端口是5173,如果想修改为其他端口(例如习惯使用Create-React-App的3000端口),可以进行以下设置。
修改vite.config.js:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
+ server: {
+ // 指定dev sever的端口号,默认为5173
+ port: 3000,
+ },
plugins: [react()],
})
※注:与基于webpack的Create-React-App不同,Vite修改项目配置后,不需要重启项目即可生效。
2.4 设置dev环境自动打开浏览器
使用Create-React-App创建的工程,在启动的时候会自动打开浏览器运行当前项目。但是基于Vite创建的工程默认情况并不会自动打开浏览器。如果想要保持Create-React-App的习惯,自动打开浏览器,可进行以下设置。
修改vite.config.js:
// https://vitejs.dev/config/
export default defineConfig({
server: {
// 指定dev sever的端口号
port: 3000,
+ // 自动打开浏览器运行以下页面
+ open: '/',
},
...(略)
})
open的"/“值,表示的是打开"localhost:3000/”。
如果想直接打开其他页面,例如"localhost:3000/#/home",open的值则设置为"/#/home"即可。
※注:open的值修改后,虽然已经生效,但不会直接触发打开浏览器的行为。这个行为只发生在通过命令启动项目的时候,也就是执行yarn dev的时候。
2.5 设置路径别名
为了避免使用相对路径的麻烦,可以设置路径别名。
修改vite.config.js:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
+ import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
server: {
...(略)
},
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, 'src'),
+ },
+ },
...(略)
})
这个配置里使用了path,不要忘记在代码头部要import path from 'path'
哦。
这样在js代码开头的import路径中,直接使用@表示“src根目录”,不用去自己去数有多少个"…/"了。
例如,src/main.jsx:
// 表示该文件当前路径下的App.jsx(相对路径)
import App from './App'
// 表示src/App.jsx,等价于上面的文件地址(绝对路径)
import App from '@/App'
3 Chrome Extension基础
本次教程基于目前最新的Chrome Extension Manifest V3进行。
3.1 Manifest V3概述
Manifest V3(简称MV3) 是2020年11月9日发布的,时隔MV2已经有很多年了。使用MV3的Chrome插件将有更好的隐私、安全和性能,还能使用很多新的Web技术。具体如下:
(1)隐私
新版插件可以在不需要特殊权限的情况下正常运行,当运行到需要某个权限时再请求用户使用许可。
(2)安全
对插件访问外部资源做了限制,禁止引入外部js,但图片、视频等静态外部资源不受影响。
(3)性能
确保插件可以在各种设备良好运行,即使在安装了很多插件的情况下,也能流畅运行。
(4)开发
降低开发门槛,减少开发障碍,更快更好地开发插件。
(5)能力
持续提升插件的能力、丰富功能,充分发挥更大的价值作用。
3.2 Manifest V3 主要新特性
(1)Service Workers取代background pages
使用Service Workers,可以对资源进行缓存,从而实现离线访问。
(2)网络请求调整
新增了一个declarativeNetRequest
API,允许插件修改及阻断网络请求。
(3)远程资源访问限制
禁止访问外部的JavaScript及Wasm文件,但图片、音视频文件不受影响。
(4)Promises使用
可以愉快地使用promise了,包括async/await。
除此之外,还有一些其他的变化。未来,MV3还会引入更多的新特性。
了解更多可参阅官网说明:
https://developer.chrome.com/docs/extensions/mv3/intro/mv3-overview/
3.3 Chrome Extension的组成
主要由以下部分组成:
- manifest.json (插件配置文件)
- popup (点击插件图标弹出的页面)
- content script (插入到目标页面中执行的js)
- background script (在Chrome后台Service Workers中运行的程序)
【manifest.json】
manifest.json必须放在插件项目根目录,里面包含了插件的各种配置信息,其中也包括了popup、content script、background script等文件的存放路径。
【popup】
作为一个独立的弹出页面,有自己的html、css、js,可以按照常规项目来开发。
【content script】
content script是注入到目标页面中执行的js脚本,可以获取目标页面的Dom并进行修改。但是,content script的JavaScript与目标页面是互相隔离的。也就是说,content script与目标页面的JavaScript不会出现互相污染的问题,同时,也不能调用对方的方法。
注意,以上只是js作用域的隔离,通过content script向目标页面加入的DOM是可以应用目标页面的css,从而造成css互相污染。
【background script】
background script 常驻在浏览器后台Service Workers运行,没有实际页面。一般把全局的、需要一直运行的代码放在这里。重要的是,background script的权限非常高,除了可以调用几乎所有Chrome Extension API外,还可以发起跨域请求。
3.4 规划build生成的目录结构
在了解Chrome Extension的基本组成后,需要按照Chrome Extension官方开发文档以及manifest.json的要求,按以下结构build最终的目录。
├─ /assets <--popup的静态资源目录
| ├─ index-xxxx.css <--popup的样式文件
| ├─ index-xxxx.js <--popup script
| └─ xxxx.png <--popup的图片等文件(示例)
├─ /images <--公共图片资源目录
| └─ app.png <--插件的图标文件
├─ background.js <--background script
├─ content.js <--content script
├─ content.css <--content script的css文件
├─ favicon.ico <--这个没有也行,用不到
├─ index.html <--popup入口页面
├─ insert.js <--插入到目标页面执行的js(非必须,视业务需求而定)
└─ manifest.json <--插件的配置文件
接下来就是如何实现build出这样的目录结构。
3.5 配置manifest.json
在开发Chrome Extension之前,要先配置好manifest.json。
新建public/manifest.json(请删除其中的注释代码):
{
"name": "Chrome插件V3",
"version": "1.0",
"description": "基于Vite的chrome插件V3 Demo",
// Chrome Extension 版本号,3表示MV3
"manifest_version": 3,
// background script配置(根目录为最终build生成的插件包目录)
"background": {
"service_worker": "background.js"
},
// content script配置
"content_scripts": [
{
// 应用于哪些页面地址(可以使用正则,<all_urls>表示匹配所有地址)
"matches": ["<all_urls>"],
// 注入到目标页面的css,注意不要污染目标页面的样式
"css": ["content.css"],
// 注入到目标页面js,这个js是在沙盒里运行,与目标页面是隔离的,没有污染问题。
"js": ["content.js"],
// 代码注入的时机,可选document_start、document_end、document_idle(默认)
"run_at": "document_end"
}
],
// 申请chrome extension API权限
"permissions": ["storage","declarativeContent"],
// 插件涉及的外部请求地址,暂未发现影响跨域请求,猜测是用于上架商店时方便审核人员查阅
"host_permissions":[],
// 如果向目标页面插入图片或者js,需要在这里授权插件本地资源(以下仅为示例)。
"web_accessible_resources": [
{
"resources": [ "/images/app.png" ],
"matches": ["<all_urls>"]
},
{
"resources": [ "insert.js" ],
"matches": ["<all_urls>"]
}
],
// popup页面配置
"action": {
// popup页面的路径(根目录为最终build生成的插件包目录)
"default_popup": "index.html",
// 浏览器插件按钮的图标
"default_icon": {
"16": "/images/app.png",
"32": "/images/app.png",
"48": "/images/app.png",
"128": "/images/app.png"
},
// 浏览器插件按钮hover显示的文字
"default_title": "React CRX MV3"
},
// 插件图标,图省事的话,所有尺寸都用一个图也行
"icons": {
"16": "/images/app.png",
"32": "/images/app.png",
"48": "/images/app.png",
"128": "/images/app.png"
}
}
manifest的配置项还有很多,可前往官网查阅。
manifest:https://developer.chrome.com/docs/extensions/mv3/manifest/
manifest_version:https://developer.chrome.com/docs/extensions/mv3/manifest/manifest_version/
content script:https://developer.chrome.com/docs/extensions/mv3/content_scripts/
permissions:https://developer.chrome.com/docs/extensions/mv3/declare_permissions/
4 项目目录结构设计
本文将按照以下目录结构进行开发:
├─ /node_modules
├─ /public
| ├─ /images <--图片目录
| | └─ app.png <--插件图标
| ├─ favicon.ico <--这个没有也行,用不到
| ├─ insert.js <--插入到目标页面执行的js(非必须,视业务需求而定)
| └─ manifest.json <--插件的配置文件
├─ /src
| ├─ /api <-- api目录
| | └─ index.jsx <-- api库
| ├─ /background <--background script开发目录
| | └─ index.jsx <--background script主文件
| ├─ /common <-- 全局公用目录
| | ├─ /fonts <-- 字体文件目录
| | ├─ /images <-- 图片文件目录
| | ├─ /js <-- 公用js文件目录
| | └─ /styles <-- 公用样式文件目录
| ├─ /content <--content script开发目录
| | ├─ /components <--content 组件目录
| | ├─ /images <--content 图片目录
| | ├─ content.styl <--content 样式
| | └─ index.jsx <--content script主文件
| ├─ /popup <--popup开发目录
| | ├─ /components <--popup 组件目录
| | ├─ /pages <--popup 页面目录
| | ├─ /router <--popup 路由配置目录
| | | └─ index.jsx <--popup 路由配置文件
| | ├─ index.jsx <--popup 主文件
| | └─ popup.styl <--popup 样式文件
| ├─ main.jsx <-- 项目主文件,也是popup入口文件
| └─ mock.jsx <-- mock数据文件
├─ .eslintrc.cjs <-- ESLint配置文件
├─.gitignore
├─ build.js <-- 补充的build脚本文件
├─ globalConfig.js <-- 全局配置文件
├─ index.html <-- popup页面入口
├─ package.json
├─ vite.popup.config.js <-- popup的Vite配置文件
├─ vite.content.config.js <-- content的Vite配置文件
├─ vite.background.config.js <-- background的Vite配置文件
└─ yarn.lock
这种目录结构设计,将background script、content script、popup分别建立独立的目录,并且设置了api、common等公用目录,边界更加清晰,便于维护。
【说明】
- 由于content script是在目标页面上执行,并不是独立的页面,因此不能使用router,也无需pages目录。
- popup是独立的页面,可以按照常规React项目设定相应的目录。
这里需要注意的是,基于Vite脚手架的工程在src目录里并没有使用js文件,而是以jsx文件进行开发。默认情况下,js文件是不能正常加载的,可以通过修改Vite配置来兼容js文件,但不推荐也没必要这么做。除此之外,在src目录之外则可以直接使用js文件。
另外,以上项目结构已经没有src/App.jsx了,现在先不用删除,随着后续章节的讲解再删除。
接下来,就按照上面的目录结构设计开始构建项目。
5 针对Chrome Extension的Vite配置
按照Chrome Extension的规范要求,build出popup项目、content script、background script。
5.1 设置全局配置
在项目根目录下新建globalConfig.js:
// Chrome Extension 最终build目录
export const CRX_OUTDIR = 'build'
// 临时build content script的目录
export const CRX_CONTENT_OUTDIR = '_build_content'
// 临时build background script的目录
export const CRX_BACKGROUND_OUTDIR = '_build_background'
build目录是最终的成品。_build_content目录和_build_background目录只是过程产物,它俩在build结束后,会合并到build目录中,然后自动删除_build_content目录和_build_background目录。
为什么要这么做?
首先,popup项目是一个包括html、css、js等文件的完整小型网站,按照Vite的默认配置即可直接build生成。但是content script、background script是没有html的,build配置不一样,因此需要单独进行build。
其次,content script、background script如果引入了共同的js模块(例如都import了自定义的api库文件),会被Vite将这些共同的js模块抽离成公共js文件。
举个例子,content script中引入了A、B模块,background script中引入了B、C模块。
我们期待打包成两个文件:
- content.js (A+B)
- background.js (B+C)
但实际上会打包成三个文件:
- content.js (A)
- background.js ©
- B.js (B)
而CRX的content script和background script不能再import或require其他文件。所以,只能将content script和background script分别通过不同的Vite配置文件进行打包,并生成在不同的目录下。
5.2 设置popup的build配置
将原vite.config.js更名为vite.popup.config.js。
修改vite.popup.config.js:
❤️❤️❤️------试读结束------❤️❤️❤️
后续精彩章节
• 5.3 设置content script的build配置
• 5.4 设置background script的build配置
• 5.5 通过补充脚本合并三个build
6 设置公用样式及集成Ant Design
• 6.1 关于样式命名规范
• 6.2 设置全局公用样式
• 6.3 集成Ant Design
• 6.4 设置Antd为中文语言
7 Popup开发
• 7.1 引入popup页面
• 7.2 构建popup的Login页面
• 7.3 构建popup的Home页面
• 7.4 构建popup的Account页面
• 7.5 配置popup页面路由
• 7.6 构建Nav导航组件
• 7.7 构建Entry二级路由框架页面
• 7.8 调整popup入口页面,打通全部路由
• 7.9 完善Login页面的登录跳转
• 7.10 设置popup页面尺寸
8 build项目并载入插件
9 background script开发
• 9.1 设置允许运行popup的页面规则
• 9.2 为什么插件图标在禁用页面不变成灰色
10 content script开发
• 10.1 向目标页面注入悬浮球
• 10.2 在content script中使用Antd
11 在开发环境中调试content script
12 API请求
• 12.1 background pages不支持XMLHttpRequest(axios)
• 12.2 使用mock.js和mockjs-fetch模拟请求
• 12.3 封装API及fetch业务逻辑
• 12.4 委托background script完成API请求
• 12.5 实现popup的Login页面API请求
• 12.6 设置开发环境的反向代理请求
• 12.7 实现content script的API请求
• 12.8 关键知识点小结
13 其他说明
• 13.1 permission权限配置
• 13.2 以<script>方式向目标页面插入js
• 13.3 Service Worker调试
• 13.4 popup页面调试
• 13.5 批量升级全部项目npm依赖包
14 项目Git源码
结束语
阅读完整版
📖 完整教程可订阅我的公众号【卧梅又闻花】
《2023金秋版:基于Vite4+React的Chrome插件开发教程》
14 项目Git源码
本项目已上传至Gitee和GitHub,方便各位下载。
Gitee:
https://gitee.com/betaq/vite-react-crx-2023autumn
GitHub:
https://github.com/Yuezi32/vite-react-crx-2023autumn