开发chrome插件auto reload原理分析

简介: 开发chrome插件auto reload原理分析

之前开发过一款chrome插件,当时使用了一个vue-cli-plugin-browser-extension,配合vue开发chrome插件非常的丝滑,尤其是可以auto reload这块非常的感兴趣。

今天有时间就仔细研究了下

vue-cli-plugin-browser-extension

先说一下使用,在vue.config.js中配置如下:

pluginOptions: {
  browserExtension: {
    components: {
      background: true,
      contentScripts: true,
    },
    componentOptions: {
      contentScripts: {
        entries: {
          content: "src/content.ts",
          inject: "src/inject/index.ts",
        },
      },
      background: {
        entry: "src/background.ts",
      }
    }
  }
}
复制代码

readme.md中写的也非常的详细。

具体是怎么实现的呢?

查看package.json的声明,发现入口在index.js

{
    "main":"index.js"
}
复制代码

非常熟悉的入口函数,直到后来看了vue-cli的源码,才知道这里面的api, options到底是怎么回事,有兴趣的可以阅读下@vue/cli-serve的源码。

因为对auto reload非常感兴趣,所以只关注这块的逻辑

// index.js
const ExtensionReloader = require('webpack-extension-reloader')
module.exports = (api, options) => {
    api.chainWebpack();
    // 重点在这里,使用了一个plugin
    webpackConfig.plugin('extension-reloader').use(ExtensionReloader, ...);
}
复制代码

再次查看下package.jsondevDependenci,还真的有依赖这个

"dependencies": {
  "@vue/cli-shared-utils": "^3.0.0-rc.3",
  "copy-webpack-plugin": "^5.1.2",
  "imports-loader": "^0.8.0",
  "webextension-polyfill": "^0.4.0",
  "webpack": "^4.16.0",
  "webpack-extension-reloader": "^1.1.0",
  "zip-webpack-plugin": "^3.0.0"
}
复制代码

那很明显auto reload就是使用了webpack-extension-reloader插件。

webpack-extension-reloader

还是首先看下package.json的main

{
    "main": "dist/webpack-extension-reloader.js",
    "repository": {
      "type": "git",
      "url": "git://github.com/rubenspgcavalcante/webpack-extension-reloader.git"
    }
}
复制代码

发现npm install后,只有dist代码,当然我是拒绝看这种build之后的代码,一般来说都是有GitHub地址的。

我们clone下来直接查看GitHub的源代码。

那这个main入口代码是如何生成的呢?发现有个webpack.config.js,里面有这个配置

module.exports={
    entry: test({ tests: "./specs/index.ts" }) || {
      [packName]: "./src/index.ts",
      [`${packName}-cli`]: "./client/index.ts"
    },
    output: {
      publicPath: ".",
      path: path.resolve(__dirname, "./dist"),
      filename: "[name].js",
      libraryTarget: "umd"
    }, 
}
复制代码

源码指向了src/index.ts

import ExtensionReloaderImpl from "./ExtensionReloader";
export = ExtensionReloaderImpl;
复制代码

又指向了ExtensionReloader,因为是个webpack插件,所以直接看apply函数即可(不懂的亲自写个webpack plugin就明白了)

import { middlewareInjector } from "./middleware";
export default class ExtensionReloaderImpl extends AbstractPluginReloader{
    apply(compiler:Compiler){
        this._registerPlugin(compiler);
    }
    _registerPlugin(){
        this._injector = middlewareInjector(parsedEntries, { port, reloadPage });
    }
}
复制代码

这里的重点又指向了src/middleware

import _middlewareInjector from "./middleware-injector";
export const middlewareInjector = _middlewareInjector;
复制代码

再看src/middleware/middleware-injector

import middleWareSourceBuilder from "./middleware-source-builder";
const middlewareInjector: MiddlewareInjector = (
  { background, contentScript, extensionPage },
  { port, reloadPage },
) => {
  const source: Source = middleWareSourceBuilder({ port, reloadPage });
  // ...
};
export default middlewareInjector;
复制代码

再看middleware-source-builder

import { template } from "lodash";
// 这里就是模板的源文件,关于这个写法,得参考webpack
import rawSource from "raw-loader!./wer-middleware.raw"; 
import polyfillSource from "raw-loader!webextension-polyfill";
import { RawSource, Source } from "webpack-sources";
import {
  RECONNECT_INTERVAL,
  SOCKET_ERR_CODE_REF,
} from "../constants/middleware-config.constants";
import * as signals from "../utils/signals";
export default function middleWareSourceBuilder({
  port,
  reloadPage,
}: IMiddlewareTemplateParams): Source {
  const tmpl = template(rawSource);
  return new RawSource(
    // 这里都是一些参数
    tmpl({
      WSHost: `ws://localhost:${port}`,
      config: JSON.stringify({ RECONNECT_INTERVAL, SOCKET_ERR_CODE_REF }),
      polyfillSource: `"||${polyfillSource}"`,
      reloadPage: `${reloadPage}`,
      signals: JSON.stringify(signals),
    }),
  );
}
复制代码

这里有个小发现,就是lodash原来也有个template方法,和ejs非常像。

再看wer-middleware.raw

/* -------------------------------------------------- */
/*      Start of Webpack Hot Extension Middleware     */
/* ================================================== */
/*  This will be converted into a lodash templ., any  */
/*  external argument must be provided using it       */
/* -------------------------------------------------- */
(function(window) {
  function contentScriptWorker() {
    runtime.sendMessage({ type: SIGN_CONNECT }).then(msg => console.info(msg));
    runtime.onMessage.addListener(({ type, payload }: IAction) => {
      switch (type) {
        case SIGN_RELOAD:
          logger("Detected Changes. Reloading ...");
          // 重点:当收到消息后,调用了reload函数,至此原理就非常清楚了
          reloadPage && window.location.reload();
          break;
        case SIGN_LOG:
          console.info(payload);
          break;
      }
    });
  }
  function backgroundWorker(socket: WebSocket) {
      // 通过socket和background建立链接
    socket.addEventListener("message", ({ data }: MessageEvent) => {
      const { type, payload } = JSON.parse(data);
      if (type === SIGN_CHANGE && (!payload || !payload.onlyPageChanged)) {
          // 重点:当background收到重载消息是,会重新加载这个chrome extension
          // http://www.kkh86.com/it/chrome-extension-doc/extensions/runtime.html#method-reload
          runtime.reload();
        });
      }
    });
    socket.addEventListener("close", ({ code }: CloseEvent) => {
      const intId = setInterval(() => {
        const ws = new WebSocket(wsHost);
        ws.addEventListener("open", () => {
          clearInterval(intId);
          runtime.reload();
        });
      }, RECONNECT_INTERVAL);
    });
  }
  // ======================== Called only on extension pages that are not the background ============================= //
  function extensionPageWorker() {
    runtime.sendMessage({ type: SIGN_CONNECT }).then(msg => console.info(msg));
    runtime.onMessage.addListener(({ type, payload }: IAction) => {
      switch (type) {
        case SIGN_CHANGE:
          reloadPage && window.location.reload();
          break;
      }
    });
  }
  // ======================= Bootstraps the middleware =========================== //
  // 这里应该是多环境复用这份代码
  runtime.reload
    ? extension.getBackgroundPage() === window ? backgroundWorker(new WebSocket(wsHost)) : extensionPageWorker()
    : contentScriptWorker();
})(window);
复制代码

再看下package.json中"ws": "^7.2.0",果然安装了ws,用来创建一个websocket服务器, 在src/hot-reload/HotReloaderServer.ts中就有创建这个server的逻辑。 当webpack重新build并生成文件后,就会触发

this._compiler.hooks.afterEmit.tap
复制代码

具体的实现细节还是非常多的,原理大致也就明白了,就是最终会触发server去通知调用reload接口,实现auto reload,再深入的就没有具体分析了



目录
相关文章
|
1月前
|
Web App开发 存储 开发者
Chrome 插件上架发布全流程指南
浏览器插件开发完以后,要发布到 Chrome Web Store上,也是需要颇费一番周折的,本文就从注册账号开始,一直到最后发布上架的全流程进行指导,希望帮助你提供一些经验,避免踩坑,耗时耗力。
249 8
|
3月前
|
Web App开发 JavaScript 前端开发
Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念
Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念,包括事件驱动、单线程模型和模块系统;探讨其安装配置、核心模块使用、实战应用如搭建 Web 服务器、文件操作及实时通信;分析项目结构与开发流程,讨论其优势与挑战,并通过案例展示 Node.js 在实际项目中的应用,旨在帮助开发者更好地掌握这一强大工具。
75 1
|
3月前
|
Web App开发 人工智能 自然语言处理
WebChat:开源的网页内容增强问答 AI 助手,基于 Chrome 扩展的最佳实践开发,支持自定义 API 和本地大模型
WebChat 是一个基于 Chrome 扩展开发的 AI 助手,能够帮助用户理解和分析当前网页的内容,支持自定义 API 和本地大模型。
306 0
|
3月前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
521 9
|
4月前
|
Web App开发 JSON JavaScript
vue学习:chrome 中 vuetools 开发插件 的下载、安装
这篇文章介绍了如何在Chrome浏览器中下载、安装并测试Vue.js开发插件——vue-devtools。
731 0
vue学习:chrome 中 vuetools 开发插件 的下载、安装
|
4月前
|
Web App开发 Windows
win 快捷键大全,虚拟窗口、桌面等操作细节,可以使界面整洁,分类工作;Chrome快捷键,都是一些开发的骚操作
这篇文章提供了Windows操作系统和Chrome浏览器的快捷键大全,以及Xshell的快捷键操作,旨在帮助用户提高工作效率和界面管理。
258 2
|
6月前
|
Web App开发 存储 前端开发
《Chrome谷歌插件Top10》开发最好用的谷歌插件
本文介绍了多个实用的浏览器插件及其安装方法。包括CSDN浏览器助手,提供高效开发工具;FeHelper,前端必备工具,支持格式化、压缩等功能;uBlock Origin,有效屏蔽广告和弹窗;PageLiner,网页标尺工具,便于前端设计;Fatkun,批量下载图片;Smallpdf,文件转换工具;Octotree,GitHub代码树插件;Awesome Screenshot,截图与录屏工具;ColorZilla,颜色拾取器;Dark Reader,暗黑模式阅读插件。安装方式有通过Chrome商店搜索或下载crx插件本地安装。
128 11
|
6月前
|
Web App开发 JSON 前端开发
30个Chrome 灵魂插件!
30个Chrome 灵魂插件!
81 4
|
30天前
|
Web App开发 人工智能 JSON
AutoMouser:AI Chrome扩展程序,实时跟踪用户的浏览器操作,自动生成自动化操作脚本
AutoMouser是一款Chrome扩展程序,能够实时跟踪用户交互行为,并基于OpenAI的GPT模型自动生成Selenium测试代码,简化自动化测试流程。
139 17
AutoMouser:AI Chrome扩展程序,实时跟踪用户的浏览器操作,自动生成自动化操作脚本
|
1月前
|
Web App开发 安全 前端开发
一个接口4个步骤轻松搞定最新版Chrome、Edge、Firefox浏览器集成ActiveX控件
目前的浏览器市场,谷歌浏览器占据了半壁江山,因此,谷歌也是最有话语权的,2015年开始取消支持 NPAPI 插件,2022 年10月停止支持 PPAPI 插件;而曾经老大哥IE浏览器也已停止服务,退出历史舞台,导致大量曾经安全、便捷的ActiveX控件无法使用。为了解决这个难题,本人特研发出allWebPlugin中间件,重新让所有ActiveX控件能在谷歌、火狐等浏览器使用。