开发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。

相关推荐
罗_三金4 分钟前
微信小程序读写NFC标签(实现NFC标签快速拉起小程序)实战
前端·javascript·微信小程序·小程序
大大。1 小时前
el-input 只能输入数字和一个小数点,或者只能输入正整数
前端·javascript·vue.js
盼盼盼1 小时前
如何避免在使用 Context API 时出现状态管理的常见问题?
前端·javascript·react.js
Amd7942 小时前
Nuxt Kit 中的布局管理
前端·web开发·nuxt.js·布局管理·代码示例·addlayout·页面结构
超雄代码狂2 小时前
JavaScript web API完结篇---多案例
开发语言·前端·javascript
程序员小羊!2 小时前
切换淘宝最新镜像源npm详细讲解
前端·npm·node.js
小白小白从不日白2 小时前
react 动画_样式处理
前端·react.js
SaxoZhao3 小时前
Vue 中阻止点击事件穿透
前端·javascript·vue.js
1234Wu3 小时前
高德地图2.0 绘制、编辑多边形覆盖物(电子围栏)
前端·vue
用你的胜利博我一笑吧3 小时前
vue3+ts+supermap iclient3d for cesium功能集合
前端·javascript·vue.js·3d·cesium·supermap