开发chrome扩展插件
# 引言
在前端开发过程中,一般分为三个环境:开发环境、测试环境和生产环境。这三个环境对于前端而言,不过就是请求的 API 接口不同罢了。如果是vue3
项目,可以通过 import.meta.env.MODE
来区分环境,可是站在后端兄弟角度,就必须修改配置文件的地址才能调试。如果要在本地调试,必须 clone 前端代码部署才行,这极为不方便。为此,我开发了一个 Chrome 插件用于解决这个问题,与项目的配置文件配合使用,可以提高后端同事的调试效率。
# 为什么采用插件
一般情况下,前端项目的配置文件会单独拧出来,方法网站根目录下。如果项目部署需要部署在不同的环境或者平台中,只需修改 config.js 的地址即可。我们可以将其挂载到window
上,使之成为一个全局变量。通过浏览器的控制台console
修改,这个没问题,但是如果页面刷新,修改的内容由于是保存在内存中,会被清空。
为此,修改的内容需要持久化储存。没错,数据可以储存在storage
中,页面刷新后,可以从storage
中读取应用。但是数据在控制台修改毕竟很繁琐,因此还需要一个可视化的页面去修改数据,将数据存储到storage
中。如果开发一个前端页面去做这事,就有点得不偿失,而选择开发插件是一种更优的选择。
# 插件的介绍与开发
谷歌的插件网上有非常多的文档,本文只讲述为项目定制插件的核心实现。 其效果如下:
# 插件的功能
插件提供的功能非常简单。用户安装插件后,可以动态修改 接口地址,项目自动刷新,启用新的接口地址访问后台;在修改接口地址后,也可以恢复默认配置,系统会启用默认地址。
插件要主要解决的问题就是将输入的地址存储到网页的storage
中,网页会去读取storage
的值,并刷新页面,应用新值。
其核心工作与实现如下:
# popup.html
界面
popup.html
提供数据的入口以及交互操作界面,其中还会引入popup.js
<div class="container">
<div class="formItem">
<h3>MMS自定义API地址:</h3>
<img id="reset" title="重置" />
</div>
<div>
<p>BASE_URL:</p>
<div class="formItem">
<input type="text" id="baseURL" placeholder="http://" />
<button id="btn">保存</button>
</div>
</div>
</div>
<script src="../js/popup.js"></script>
2
3
4
5
6
7
8
9
10
11
12
13
14
# popup.js
popup.js
的作用包括设置弹窗内容、响应用户交互、处理事件以及与后台脚本进行数据交换。由于popup.js
无法与网页进行通信,因此需要借助于后台脚本。
响应用户交互,其实现如下,通过chrome.runtime.sendMessage
向background.js
发送消息
/** 监听保存按钮事件 */
doms.btn.addEventListener("click", () => {
const val = doms.input.value;
if (val) {
if (isValidURL(val)) {
//判断输入是否是合法的url
chrome.runtime.sendMessage(
{ action: "UPDATE_GLOBAL_VAR", value: val },
(response) => {
if (response.status === "success") {
console.log("🚀设置BASE_URL成功");
window.close();
} else {
alert("🚀设置BASE_URL失败");
}
}
);
} else {
doms.input.value = preValue;
alert("接口地址不合法");
}
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# background.js
background.js
主要用于处理插件的后台逻辑和管理扩展的生命周期,此次开发中background.js
主要做了两件事:
- 1.安装成功后初始化
storage.local
的变量 - 2.监听
popup.js
发送的消息以及接收到消息后,给popup.js
一个反馈,并且将数据通过chrome.tab.sendMessage
发送给content_script.js
其实现如下:
// background.js
console.log("🚀 加载background.js成功");
// background.js
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.local.set({ globalVar: null });
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "UPDATE_GLOBAL_VAR") {
chrome.storage.local.set({ globalVar: message.value });
sendResponse({ status: "success" });
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {
type: "UPDATE_CONTENT",
data: message.value,
});
});
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# content_script.js
插件中的content_script.js
就像一个中转站一样,用于脚本和网页之间沟通的桥梁,监听二者的消息再转发。
content_script.js
用chrome.runtime.onMessage
监听background.js
的消息,再通过window.postMessage
转发数据给网页
其实现如下:
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
console.log("Message received in content script:", message);
if (message.type === "UPDATE_CONTENT") {
console.log("Data from background:", message.data);
window.postMessage({
type: _CONST_.FROM_CONTENT_SCRIPT,
data: message.data,
});
}
});
2
3
4
5
6
7
8
9
10
此外,由于插件还做到了初始配置的回显,所以在content_script.js
中还通过window.addEventListener
监听了网页的消息,如下
window.addEventListener("message", function (event) {
if (
event.source === window &&
event.data.type &&
event.data.type == _CONST_.FROM_CONTENT_WEBPAGE
) {
console.log("🚀 ~ WebPage`s Message received in content script:", event);
// 定义你想要获取的数据键
const keys = ["globalVar"];
chrome.storage.local.get(keys, function (result) {
console.log("🚀 ~ result:", result);
// 确保获取的数据存在
if (chrome.runtime.lastError) {
console.error("Error retrieving data:", chrome.runtime.lastError);
return;
}
const webPageValue = event.data.data;
//存储最初始的值,用于重置
if (result.globalVar == null) {
chrome.storage.local.set({ originalValue: webPageValue });
}
// 处理获取的数据
if (result.globalVar != webPageValue) {
chrome.storage.local.set({ globalVar: webPageValue });
}
});
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
content_script.js
接收到网页消息后,将数据存储在chrome.storage.local
中。
# config.js
config.js
不属于插件的内容,存在于前端项目中,但是config.js
相当于是数据的最终接收方和响应对象。
其实现如下:
window.addEventListener("message", function (event) {
if (
event.source === window &&
event.data.type &&
event.data.type == _CONST_.FROM_CONTENT_SCRIPT
) {
window.localStorage.setItem(
_CONST_.CONFIG_PLUGIN,
JSON.stringify({ BASE_URL: event.data.data })
);
window.location.reload();
}
});
let CONFIG_PLUGIN_STORAGE = window.localStorage.getItem(_CONST_.CONFIG_PLUGIN);
CONFIG_PLUGIN_STORAGE = CONFIG_PLUGIN_STORAGE
? JSON.parse(CONFIG_PLUGIN_STORAGE)
: {};
window.config = {
BASE_URL: "http://192.168.145.74:18430",
...CONFIG_PLUGIN_STORAGE,
};
window.postMessage({
type: _CONST_.FROM_CONTENT_WEBPAGE,
data: window.config.BASE_URL,
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
config.js
的作用增强了,除了提供接口地址。它还承担了重要的作用。主要功能如下:
- 监听
content_script.js
脚本发送的消息,然后将新数据存储到localStorage
中去,最后刷新页面 - 读取
localStorage
中数据,组装成环境地址接口 - 向
content_script.js
发送消息
# 总结
插件的开发一定要符合 chrome 插件开发规范,脚本间的通信要一 一对应。通过crx
格式安装的扩展插件的前提是插件要打包发布在 Chrome 的应用商店中,一次性收费25$
。
github 地址 (opens new window),如果对您有帮助,请给一个 star。