开发chrome扩展插件修改地址

引言

在前端开发过程中,一般分为三个环境:开发环境、测试环境和生产环境。这三个环境对于前端而言,不过就是请求的 API 接口不同罢了。如果是vue3项目,可以通过 import.meta.env.MODE来区分环境,可是站在后端兄弟角度,就必须修改配置文件的地址才能调试。如果要在本地调试,必须 clone 前端代码部署才行,这极为不方便。为此,我开发了一个 Chrome 插件用于解决这个问题,与项目的配置文件配合使用,可以提高后端同事的调试效率。

为什么采用插件

一般情况下,前端项目的配置文件会单独拧出来,方法网站根目录下。如果项目部署需要部署在不同的环境或者平台中,只需修改 config.js 的地址即可。我们可以将其挂载到window上,使之成为一个全局变量。通过浏览器的控制台console修改,这个没问题,但是如果页面刷新,修改的内容由于是保存在内存中,会被清空。

为此,修改的内容需要持久化储存。没错,数据可以储存在storage中,页面刷新后,可以从storage中读取应用。但是数据在控制台修改毕竟很繁琐,因此还需要一个可视化的页面去修改数据,将数据存储到storage中。如果开发一个前端页面去做这事,就有点得不偿失,而选择开发插件是一种更优的选择。

插件的介绍与开发

谷歌的插件网上有非常多的文档,本文只讲述为项目定制插件的核心实现。

其效果如下:

插件的功能

插件提供的功能非常简单。用户安装插件后,可以动态修改 接口地址,项目自动刷新,启用新的接口地址访问后台;在修改接口地址后,也可以恢复默认配置,系统会启用默认地址。

插件要主要解决的问题就是将输入的地址存储到网页的storage中,网页会去读取storage的值,并刷新页面,应用新值。

其核心工作与实现如下:

popup.html 界面

popup.html提供数据的入口以及交互操作界面,其中还会引入popup.js

html 复制代码
<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>
popup.js

popup.js的作用包括设置弹窗内容、响应用户交互、处理事件以及与后台脚本进行数据交换。由于popup.js无法与网页进行通信,因此需要借助于后台脚本。

响应用户交互,其实现如下,通过chrome.runtime.sendMessagebackground.js发送消息

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("接口地址不合法");
    }
  }
});
background.js

background.js主要用于处理插件的后台逻辑和管理扩展的生命周期,此次开发中background.js主要做了两件事:

  • 1.安装成功后初始化storage.local的变量
  • 2.监听popup.js发送的消息以及接收到消息后,给popup.js一个反馈,并且将数据通过chrome.tab.sendMessage发送给content_script.js

其实现如下:

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,
      });
    });
  }
});
content_script.js

插件中的content_script.js就像一个中转站一样,用于脚本和网页之间沟通的桥梁,监听二者的消息再转发。

content_script.jschrome.runtime.onMessage监听background.js的消息,再通过window.postMessage转发数据给网页

其实现如下:

js 复制代码
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,
    });
  }
});

此外,由于插件还做到了初始配置的回显,所以在content_script.js中还通过window.addEventListener监听了网页的消息,如下

js 复制代码
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 });
      }
    });
  }
});

content_script.js接收到网页消息后,将数据存储在chrome.storage.local中。

config.js

config.js不属于插件的内容,存在于前端项目中,但是config.js相当于是数据的最终接收方和响应对象。

其实现如下:

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,
});

config.js的作用增强了,除了提供接口地址。它还承担了重要的作用。主要功能如下:

  • 监听content_script.js脚本发送的消息,然后将新数据存储到localStorage中去,最后刷新页面
  • 读取localStorage中数据,组装成环境地址接口
  • content_script.js发送消息

总结

插件的开发一定要符合 chrome 插件开发规范,脚本间的通信要一 一对应。通过crx格式安装的扩展插件的前提是插件要打包发布在 Chrome 的应用商店中,一次性收费25$

github地址,如果本文或插件对您有帮助,请给一个star。

相关推荐
小小小小宇13 分钟前
LLM 长期记忆构建
前端
lichenyang45325 分钟前
从 Express 老项目到 NestJS + Docker:一次车辆管理系统的渐进式重构
前端
Momo__2 小时前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
程序员小富2 小时前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇2 小时前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇2 小时前
React中的forwardRef
前端·react.js·面试
槑有老呆2 小时前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马2 小时前
Verilog开发常见问题汇总解析
前端
子兮曰2 小时前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端