前言
谷歌插件是一种可在谷歌浏览器中添加的小型程序,它可以为用户提供各种功能和服务。其中一些最受欢迎的插件包括广告拦截器、密码管理器、翻译工具、屏幕截图工具和社交媒体管理器等。
作者在chrome应用市场中发现一款特别好的插件“Monica”,能够提高开发人员的工作效率,在chrome应用市场上拥有600,000+用户,Monica是ChatGPT在网页上的应用,换句话说,Monica就是靠着ChatGPT API的强大功能才厉害。此篇文章就让作者手把手教你从0到1开发谷歌浏览器插件吧!
1、 初始化项目
1.1 使用create-react-app 搭建新项目
npx create-react-app chrome-plugin
chrome-plugin是创建的项目名称。
1.2 精简项目
├─ node_modules
├─ public
| ├─ favicon.ico
| ├─ index.html
| ├─ mainfest.json
├─ src-
| ├─ App.css
| ├─ App.js
├─ .gitignore
├─ package.json
├─ README.md
└─ yarn.lock
1.3 引入ant Design
1.3.1 安装
yarn add antd
1.3.2 引入组件
引入你所需要的组件如Button
import { Button } from 'antd';
1.3.3引入组件样式
在公共的文件引入,如App.js
import 'antd/dist/antd.css'
1.3.4 使用antd的Button组件
<Button type="primary">Primary Button</Button>
1.4 依赖包版本
Node.js 16.13.2
create-react-app 5.0.0
react 17.0.2
react-router-dom 6.2.1
antd 4.18.4
react-markdown 8.0.7
event-source-polyfill 1.0.31
marked 5.0.2
react-shadow 20.0.0
2、 Chrome插件开发基础
2.1 基础知识
2.2 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外,还可以发起跨域请求。
2.3 谷歌插件的目录结构如下:
- manifest.json:插件的清单文件,包含插件的基本信息、权限、入口等信息。
- _locales/ :存放插件的多语言翻译文件。
- icons/ :存放插件的图标文件,包括插件在浏览器工具栏中的图标、在扩展管理页面中的图标等。
- scripts/ :存放插件的脚本文件,包括后台脚本、内容脚本等。
- styles/ :存放插件的样式文件。
- pages/ :存放插件的页面文件,包括选项页面、弹出页面等。
- lib/ :存放插件的第三方库文件。
- tests/ :存放插件的测试文件。
- README.md:插件的说明文档。
2.4 manifest.json的参数列表
- name:扩展程序的名称
- version:扩展程序的版本号
- description:扩展程序的描述信息
- manifest_version:manifest 文件的版本号,当前版本为 2
- icons:扩展程序的图标
- browser_action:浏览器图标点击时弹出的浏览器操作界面
- page_action:浏览器图标点击时弹出的页面操作界面
- background:扩展程序后台脚本
- content_scripts:扩展程序注入到网页中的脚本
- permissions:扩展程序需要的权限
- web_accessible_resources:扩展程序可以访问的网页资源
- options_page:扩展程序的选项页面
- options_ui:扩展程序的选项界面
- default_locale:扩展程序的默认语言
- chrome_settings_overrides:覆盖 Chrome 浏览器的设置
- minimum_chrome_version:扩展程序所需的最低 Chrome 浏览器版本号
{
"name": "plugin",
"version": "1.0",
"description": "谷歌插件,基于chatgpt开发。",
"manifest_version": 3,
"background": {
"service_worker": "static/js/background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["static/js/content.js"],
"run_at": "document_end"
}
],
"permissions": ["storage", "declarativeContent","tabs", "activeTab", "scripting", "commands"],
"host_permissions": [],
"web_accessible_resources": [
{
"resources": ["images/*.png"],
"matches": ["<all_urls>"]
},
{
"resources": ["static/css/*.css"],
"matches": ["<all_urls>"]
},
{
"resources": ["*.html"],
"matches": ["<all_urls>"]
}
],
"action": {
"default_icon": {
"16": "images/icon16.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
},
"default_title": "React CRX MV3"
},
"icons": {
"16": "images/icon16.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
},
"commands": {
"_execute_action": {
"suggested_key": { "default": "Ctrl+B", "mac": "Command+B" }
}
}
}
2.5 谷歌插件permissions的参数说明
- activeTab:可以访问当前激活的选项卡的信息,包括URL、标题和图标等。常用于执行与当前选项卡相关的操作。
- storage:可以访问插件本地存储空间,可以用来存储和读取插件的数据,如设置项、用户偏好等。
- tabs:可以访问所有选项卡的信息,包括URL、标题、图标、ID等。常用于操作选项卡,如打开、关闭、切换等。
- webRequest:可以拦截、修改和阻止网络请求,常用于实现广告屏蔽、安全防护等功能。
- webNavigation:可以监控浏览器的导航事件,如页面跳转、前进后退等,常用于实现特定页面的自动化操作。
- contextMenus:可以添加右键菜单,常用于提供快捷操作和扩展功能。
- notifications:可以弹出桌面通知,常用于提醒用户、显示状态等。
- cookies:可以访问浏览器的Cookie,常用于实现自动登录等功能。
- identity:可以实现OAuth2认证,常用于与第三方服务进行交互。
- unlimitedStorage:可以拥有无限制的存储空间,常用于需要存储大量数据的插件。
- bookmarks:可以访问和操作浏览器的书签,常用于实现书签管理等功能。
- history:可以访问和操作浏览器的历史记录,常用于实现历史记录管理等功能。
3 项目架构设计
3.1 工程目录结构
项目的基本结构如下,主要是在src目录下创建background、content、popup文件夹,在public目录下面创建manifest.json文件。
├─ /config <--配置目录(由eject生成) `
├─ /public <--popup入口页面`
| ├─ /images <--图片目录`
| | ├─ icon.png <--插件图标`
| ├─ favicon.ico <--这个没有也行,用不到`
| ├─ index.html <--popup入口页面`
| ├─ insert.js <--插入到目标页面执行的js(非必须,视业务需求而定)`
| ├─ manifest.json <--插件的配置文件`
├─ /scripts <--项目构建运行脚本(由eject生成)`
├─ /src <--开发目录`
| ├─ /api <--API公用目录`
| | ├─ index.js`
| ├─ /background <--background script开发目录`
| | ├─ index.js`
| ├─ /common <--公用资源目录`
| | ├─ /js <--公用js目录`
| | └─ /stylus <--公用样式目录(本demo使用stylus)`
| ├─ /content <--content script开发目录`
| | ├─ /components <--content 组件目录`
| | ├─ /images <--content 图片目录`
| | ├─ content.styl <--content 样式`
| | └─ index.js <--content script主文件`
| ├─ /popup <--popup开发目录`
| | ├─ /pages <--popup 页面目录`
| | ├─ /components <--popup 组件目录`
| | ├─ index.js <--popup 主文件`
| ├─ index.js <--项目主文件,也是popup入口文件`
├─ pakeage.json`
3.2 暴露webpack配置
项目要根据Chrome Extension格式打包出对应的文件。因此打出来的包要包含popup、background script、content script,所以需要对webpack进行配置。
在create-react-app创建项目时,webpack配置原本是隐藏的需要执行以下命令
npm run eject
执行完上述命令后,会在项目根目录下生成一个 config 文件夹,其中的 webpack.config.js文件就是webpack的配置文件。需要注意的是,一旦执行了 eject命令,就无法回退到原来的状态,所以在修改之前需要慎重考虑。
3.3 webpack.config.js配置整改
需要修改entry和output,如下:
entry: {
main: isEnvDevelopment && !shouldUseReactRefresh
? [webpackDevClientEntry,paths.appIndexJs] : paths.appIndexJs,
content: './src/content/index.js', // 谷歌插件需要用到的centent.js
background: './src/background/index.js' // 谷歌插件需要用到的background.js
},
output: {
path: paths.appBuild, // 打包后的文件夹名称
pathinfo: isEnvDevelopment,
filename: isEnvProduction
? 'static/js/[name].js'
: isEnvDevelopment && 'static/js/[name].bundle.js',
chunkFilename: isEnvProduction
? 'static/js/[name].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
assetModuleFilename: 'static/media/[name].[hash][ext]',
publicPath: paths.publicUrlOrPath,
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
},
打包完之后,会在static/js目录下面创建centent.js和background.js文件。如图:
4 content script开发
Content script是一种可以注入到网页中的JavaScript脚本。它可以与页面上的元素进行交互。我们可以通过Content script注入chatgpt的聊天框。如图:
4 向浏览器页面注入react应用。
4.1 创建react挂载点
在content文件夹下面创建index.js文件,并且在文件中创建挂载点。如图:
import ReactDOM from "react-dom";
// 创建id为sino-container的div
const app = document.createElement("div");
app.id = "my-container";
function Content() {
return <div>hello world</div>;
}
// 将刚创建的div插入body最后
document.body.appendChild(app);
// 将ReactDOM插入刚创建的div
ReactDOM.render(<Content />, app);
Content组件就是我们需要开发的模块
4.2 开发chatgpt应用
在content文件夹下面创建components组件库
4.3 使用Shadow DOM开发
Content script是注入到网页中的JavaScript脚本,样式必须要全局注入到页面中,这样会导致页面的样式污染。为了解决这个问题,我们采用了shadow DOM。
Shadow DOM 是一种 Web API,用于将 DOM 树的一部分隐藏起来,使其不受外部 CSS 样式的影响,并且可以防止外部 JavaScript 访问其内部。可以创建独立的、封闭的组件,使其可以更好地维护和重用。在开发过程中,可以使用 Shadow DOM 创建自定义元素,然后将其插入到页面中,从而实现更高效、可靠的页面构建。
在react中用到react-shadow,安装下:
npm install react-shadow@20.0.0
在代码中使用
import ReactDOM from "react-dom";
import root from "react-shadow";
// 创建id为sino-container的div
const app = document.createElement("div");
app.id = "my-container";
function Content() {
return <root.div>hello world</root.div>;
}
// 将刚创建的div插入body最后
document.body.appendChild(app);
// 将ReactDOM插入刚创建的div
ReactDOM.render(<Content />, app);
效果如图:
4.3.1 向shadow DOM中注入Style
使用chrome.runtime.getURL(xxx)获取文件资源地址,再注入到标签中。
let style;
try {
fetch(chrome.runtime.getURL("static/css/content.css")) // 打包后的样式文件地址
.then((response) => response.text())
.then((fileContent) => {
style = fileContent;
});
} catch (error) {}
function Content() {
return (
<root.div> <style type="text/css">{styleData}</style>
hello world
</root.div>
);
}