Jinuss's blog Jinuss's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

前端可视化
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

    • Git使用手册
    • craco-less使用问题
    • npm常用命令
    • 绘制多彩的三角形
    • 开发chrome扩展插件
      • npm的作用域介绍
      • Django开发基础介绍
      • Django开发前端篇
      • Django Web开发接口定义
      • npm packageJson属性详解
      • vue3中使用live2D
      • Worker加载外部文件
      • vite配置打包分类文件
      • 微前端基础知识入门篇一
      • yaml语言教程
      • 微前端基础知识入门篇(二)
    • GitHub技巧

    • SVG

    • 技术
    • 技术文档
    东流
    2024-09-04
    目录

    开发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>
    
    1
    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("接口地址不合法");
        }
      }
    });
    
    1
    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,
          });
        });
      }
    });
    
    1
    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,
        });
      }
    });
    
    1
    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 });
          }
        });
      }
    });
    
    1
    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,
    });
    
    1
    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。

    编辑 (opens new window)
    上次更新: 2024/12/03, 06:54:15
    绘制多彩的三角形
    npm的作用域介绍

    ← 绘制多彩的三角形 npm的作用域介绍→

    最近更新
    01
    GeoJSON
    05-08
    02
    Circle
    04-15
    03
    CircleMarker
    04-15
    更多文章>
    Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式