浏览器网络插件
- 🌐 浏览器网络代理插件
- 0️⃣ 前言
- 😎 快速体验
- 🔍 浏览器兼容性
- 🚀 使用方法
- 🔨 简单的实现逻辑
🌐 浏览器网络代理插件
0️⃣ 前言
Firefox
浏览器网络设置功能我觉得还是蛮实用的,但是, Chrome
和 Edge
浏览器却没有类似的简便插件或内建选项。我发现浏览器的网络配置接口实际上是存在的, 看着功能实现不难, 简单的做了一个手动配置网络的插件 🔧🌐.
😎 快速体验
👉 项目源码: Browser_Proxy_Plugin
🎉 欢迎一起交流!💬 (⭐️ start 和 🍴 fork,感谢大家的支持!🙏)
🔍 浏览器兼容性
目前已在以下浏览器中测试,功能完全正常:
- 🟢 Chrome 131
- 🟢 Edge 131
其他现代浏览器(如 Firefox 等)理论上也支持,但没有去测试。
🚀 使用方法
-
打开 Chrome 设置 ⚙️
- 点击 Chrome 窗口右上角的
⋮
(三个点)。 - 依次选择 更多工具 > 扩展程序。
- 点击 Chrome 窗口右上角的
-
启用开发者模式 🧑💻
- 在扩展程序页面右上角,找到 开发者模式 开关,点击将其打开。
-
加载解压后的文件夹 📁
- 点击页面左上角的 加载已解压的扩展程序 按钮。
- 在弹出的文件选择窗口中,选择刚刚解压的插件文件夹。
-
确认加载成功 ✅
- 如果一切正常,插件会立即出现在扩展程序列表中。
- 你可以在 Chrome 右上角的扩展图标中看到它。
🔨 简单的实现逻辑
- 用
HTML
语言写popup.html
也就是这个弹出的界面的样式
我尝试引入
VUE3
和ElementPlus
框架 发现不是那么容易 并且容易出错, 后面调查发现用Bootstrap
是不错的 有机会可以试试.
- 在
module/popup.js
里面获取元素的id
然后做事件的监听和业务的实现, 代码如下
document.addEventListener("DOMContentLoaded", function () {// 默认的bypass列表const defaultBypassList = ["127.0.0.1/8","192.168.1.0/24","::1","localhost",".net.nz",];// 这个插件默认的浏览器存储的数据格式let initialSettings = {bypassList: [],httpPort: "8080",httpProxy: "example.com",httpsPort: "",httpsProxy: "",proxyEnabled: false,socksHost: "",socksPort: "",useForHttps: false,};/** ================== Step 1 ================== */// 得到插件的全部 HTML Elementconst noProxyToggle = document.getElementById("no-proxy-checkbox");const manualProxyToggle = document.getElementById("manual-proxy-checkbox");const proxyConfiguration = document.getElementById("proxy-configuration");const proxyPanel = document.getElementById("proxy-panel");const httpProxyInput = document.getElementById("http-proxy");const httpPortInput = document.getElementById("http-port");const httpsProxyInput = document.getElementById("https-proxy");const httpsPortInput = document.getElementById("https-port");const socksHostInput = document.getElementById("socks-host");const socksPortInput = document.getElementById("socks-port");const bypassListInput = document.getElementById("bypass-list");const useForHttpsCheckbox = document.getElementById("use-for-https");const applyButton = document.getElementById("apply-button");const cancelButton = document.getElementById("cancel-button");/** ================== Step 2 ================== */// 给插件的HTML Element注入监听事件 观察变化// 2.1 不使用代理和使用手动代理的互斥行为noProxyToggle.addEventListener("change", () => {if (noProxyToggle.checked) manualProxyToggle.checked = false;else manualProxyToggle.checked = true;// 主动触发一次使用手动代理的行为促使它去修改样式manualProxyToggle.dispatchEvent(new Event("change"));});manualProxyToggle.addEventListener("change", () => {if (manualProxyToggle.checked) {proxyConfiguration.classList.remove("disabled");proxyPanel.classList.remove("not-allowed");noProxyToggle.checked = false;} else {proxyConfiguration.classList.add("disabled");proxyPanel.classList.add("not-allowed");noProxyToggle.checked = true;}});// 2.2 HTTP 和 HTTPS 如果是共享状态的话需要同步更新数据httpProxyInput.addEventListener("input", () => {if (useForHttpsCheckbox.checked)httpsProxyInput.value = httpProxyInput.value;});httpPortInput.addEventListener("input", () => {if (useForHttpsCheckbox.checked) httpsPortInput.value = httpPortInput.value;});useForHttpsCheckbox.addEventListener("change", () => {if (useForHttpsCheckbox.checked) {httpsProxyInput.classList.add("disabled", "not-allowed");httpsPortInput.classList.add("disabled", "not-allowed");httpsProxyInput.value = httpProxyInput.value;httpsPortInput.value = httpPortInput.value;} else {httpsProxyInput.classList.remove("disabled", "not-allowed");httpsPortInput.classList.remove("disabled", "not-allowed");}});// 2.3 从 HTML Element 上拿出状态写入存储 并且需要立即生效设置applyButton.addEventListener("click", () => {// 获取用户输入并将其分割为数组,同时移除多余的空格const bypassList = bypassListInput.value.split(";").map((url) => url.trim()).filter(Boolean);// 将默认的bypassList加入用户输入的bypassListconst finalBypassList = [...new Set([...defaultBypassList, ...bypassList])];const proxyConfig = {mode: "fixed_servers",rules: {singleProxy: {scheme: "http",host: httpProxyInput.value || "example.com",port: parseInt(httpPortInput.value) || 8080,},// 使用合并后的 bypassListbypassList: finalBypassList,},};// 保存设置到 Chrome 存储中chrome.storage.sync.set({proxyEnabled: manualProxyToggle.checked,httpProxy: httpProxyInput.value,httpPort: httpPortInput.value,httpsProxy: useForHttpsCheckbox.checked? httpProxyInput.value: httpsProxyInput.value,httpsPort: useForHttpsCheckbox.checked? httpPortInput.value: httpsPortInput.value,socksHost: socksHostInput.value,socksPort: socksPortInput.value,// 保存合并后的 bypassListbypassList: finalBypassList,useForHttps: useForHttpsCheckbox.checked,// 保存代理配置proxySettings: proxyConfig,},// 立即应用设置function () {if (manualProxyToggle.checked) {chrome.proxy.settings.set({value: proxyConfig,scope: "regular",},function () {});} else {chrome.proxy.settings.clear({ scope: "regular" }, function () {});}});window.close();});// 2.4 取消设置需要回退回存储中记录的状态cancelButton.addEventListener("click", () => {// 恢复到初始设置if (initialSettings.proxyEnabled)manualProxyToggle.checked = initialSettings.proxyEnabled;if (initialSettings.httpProxy)httpProxyInput.value = initialSettings.httpProxy;if (initialSettings.httpPort)httpPortInput.value = initialSettings.httpPort;if (initialSettings.httpsProxy)httpsProxyInput.value = initialSettings.httpsProxy;if (initialSettings.httpsPort)httpsPortInput.value = initialSettings.httpsPort;if (initialSettings.socksHost)socksHostInput.value = initialSettings.socksHost;if (initialSettings.socksPort)socksPortInput.value = initialSettings.socksPort;const userBypassList = initialSettings.bypassList? initialSettings.bypassList: [];const combinedBypassList = [...new Set([...defaultBypassList, ...userBypassList]),];bypassListInput.value = combinedBypassList.join(", ");if (initialSettings.useForHttps !== undefined)useForHttpsCheckbox.checked = initialSettings.useForHttps;// 关闭 popupwindow.close();});/** ================== Step 3 ================== */// 入口函数 插件初始化的操作chrome.storage.sync.get(["proxyEnabled","httpProxy","httpPort","httpsProxy","httpsPort","socksHost","socksPort","bypassList","useForHttps",],function (result) {initialSettings = result;// 互斥的两个开关选项if (result.proxyEnabled) manualProxyToggle.checked = true;manualProxyToggle.dispatchEvent(new Event("change"));if (result.httpProxy) httpProxyInput.value = result.httpProxy;if (result.httpPort) httpPortInput.value = result.httpPort;if (result.httpsProxy) httpsProxyInput.value = result.httpsProxy;if (result.httpsPort) httpsPortInput.value = result.httpsPort;if (result.socksHost) socksHostInput.value = result.socksHost;if (result.socksPort) socksPortInput.value = result.socksPort;// 合并不显示默认去掉的bypassListconst userBypassList = result.bypassList ? result.bypassList : [];const combinedBypassList = userBypassList.filter((item) => !defaultBypassList.includes(item));bypassListInput.value = combinedBypassList.join("; ");if (result.useForHttps) useForHttpsCheckbox.checked = result.useForHttps;useForHttpsCheckbox.dispatchEvent(new Event("change"));});
});
大概的框图可以这么表示, 一看就明白