前言
最近在搞一个 chrome 插件,主要开发的是 content script 往站点插入一些 dom 的功能。开发过此类功能的同学可能会清楚,如果你用纯 JS 来做这件事情的话是比较麻烦的,当然你也可以选择 jquery 。
我作为一个 React+Antd 的重度患者,肯定是希望以自己最为舒适的方式去做开发。所以就想着自己去搭建一个简单的脚手架,检验一下自己的工程化能力。
PS:这里的浏览器插件指的是 Chrome 插件,下文就不再做解释。
插件简述
先来看看一个浏览器插件原生的工程目录下会包含什么东西:
manifest.json
Chrome 扩展的配置文件,用来描述扩展的基本信息和配置。以下是一个实例:
{
"manifest_version": 3,
"name": "My Chrome Extension",
"version": "1.0",
"description": "A simple Chrome extension.",
"permissions": ["storage", "activeTab", "scripting"],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
}
manifest_version指定清单文件的版本,当前版本为3。name扩展的名称。version扩展的版本号。description扩展的描述信息。permissions扩展所需的权限,例如访问存储、当前标签页等。background配置后台脚本,在manifest_version 3中,使用service_worker指定后台脚本文件。action配置扩展的操作,包括默认的弹出页面和图标。default_popup:指定用户点击扩展图标时显示的HTML文件。default_icon:指定扩展图标的不同尺寸。
icons配置扩展的图标,提供16x16、48x48和128x128像素的图标。content_scripts配置内容脚本。matches:指定内容脚本将注入哪些页面,这里使用<all_urls>表示所有页面。js:指定要注入的内容脚本文件。
content.js
在网页的上下文中运行,能够与网页的DOM和JavaScript交互。可以访问和操作页面上的元素、修改样式、响应用户事件等。
alert('这是contentjs')
background.js
Chrome 扩展的后台持续运行的脚本。它不直接与网页交互,而是负责处理全局的任务和逻辑。处理长时间运行的任务,如监听浏览器事件、管理扩展状态、执行定时任务等。
chrome.runtime.onInstalled.addListener(() => {
console.log('Extension installed');
});
popup
在用户点击扩展图标时显示的HTML页面。它通常用作扩展的用户界面。主要用作用户与扩展交互的界面,提供按钮、表单、信息显示等。
<!DOCTYPE html>
<html>
<head>
<title>My Extension</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Hello, Chrome Extension!</h1>
<button id="action-button">Click me</button>
<script src="popup.js"></script>
</body>
</html>
document.getElementById("action-button").addEventListener("click", () => {
alert(1);
});
Content Script
OK,那我们现在第一步就先来实现:用 React+Antd 来写内容脚本。
首先把该安装的依赖先装一遍:
然后把图标、 manifest.json 这种配置的东西放到 public 目录下,后续打包通过一个 copy 插件拷贝到产物文件夹中:
然后新建一个 config 文件夹,用来放 webpack 配置:
主要的配置都在 common 中,其他两个是根据不同的环境做一些不同的策略而已。
这里主要贴一下 webpack.common.js 的配置:
const path = require("path");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
entry: {
content: path.resolve(__dirname, "../src/content/index.js"),
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "../dist"),
clean: true,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: "babel-loader",
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
{
loader: "less-loader",
options: {
lessOptions: {
javascriptEnabled: true,
},
},
},
],
},
],
},
resolve: {
extensions: [".js", ".jsx"],
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: path.resolve(__dirname, "../dist"),
},
],
}),
],
};
先看 entry ,我们现在主要关注内容脚本,所以有一个 content 入口,入口文件对应 src/content/index.js 。
rules 中就是一些 loader ,这里就不展开, plugins 中主要是一个拷贝插件。
这里再顺便贴一下其他两个配置文件:
下面主要来看 src/content/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
const div = document.createElement("div");
document.body.appendChild(div);
ReactDOM.createRoot(div).render(<App />);
首先引入一个 App 组件,这就是一个 React 组件,然后通过 ReactDOM ,把它渲染到 dom 上。注意 createElement 产生的div标签,我们的 React 组件最后会作用到这个 div 上。
所以最终渲染出来的 dom 节点,你想挂在页面的哪个元素下,你就把这个 div 追加到哪个元素下,这里我直接放到了 body 下。
最后看一下 App.jsx ,就是一段平平无奇的 React 代码了
import { Button } from "antd"
import React from "react"
const App = () => {
return (
<div>
<Button onClick={()=>{
alert('🐔')
}} type="primary">插件插入的按钮</Button>
</div>
)
}
export default App
然后在 package.json 中加入下面两条命令:
无论是开发环境打包还是生产打包,都会有一个 dist 目录,安装插件时,直接安装这个 dist 目录即可。
最后看看效果:
Background
有了上面的基础之后,我们再来打包一个 background.js 就已经轻车熟路了。
首先 manifest.json 中加入配置:
然后 webpack 打包中加一个入口:
尝试修改一下 background.js :
import { isEmpty } from "lodash";
chrome.runtime.onInstalled.addListener(() => {
console.log("这里是background", isEmpty([1, 2, 3]));
});
发现打包也是没问题的。
Popup
最后再来打包一个 popup 页面,先来把 manifest.json 的配置加上:
打包这个 popup 页面你可以理解为跟你平时打包一个 React 的 SPA 应用一样。
首先是一个 html 模版文件,然后是一个 index.js 入口:
最后是一个 App 组件:
webpack 的配置需要注意:
先加一个入口:
然后看到了一个熟悉的插件:
filename 就是输出的文件,这个需要对应 manifest.json 的配置; chunks 就是这个插件需要帮你自动引入哪个打包后的 js ,由于我们上面入口配置的是 popup ,所以这里写 popup 。
最后大功告成:
最后
对我来说,这是一次好玩的工程化实战体验。重温了一次 webpack+react 的实战配置,如果你用的是 Vue ,那其实也大差不差。你要搞清楚的是, webpack 的打包入口在哪里,打出来的产物你希望是什么,就可以了。
希望对你有帮助,如果觉得有意思的话,点点关注点点赞吧~