开发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,再深入的就没有具体分析了



目录
相关文章
|
21天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
135 9
|
1月前
|
Web App开发 Windows
win 快捷键大全,虚拟窗口、桌面等操作细节,可以使界面整洁,分类工作;Chrome快捷键,都是一些开发的骚操作
这篇文章提供了Windows操作系统和Chrome浏览器的快捷键大全,以及Xshell的快捷键操作,旨在帮助用户提高工作效率和界面管理。
119 2
|
1月前
|
Web App开发 JSON JavaScript
vue学习:chrome 中 vuetools 开发插件 的下载、安装
这篇文章介绍了如何在Chrome浏览器中下载、安装并测试Vue.js开发插件——vue-devtools。
274 0
vue学习:chrome 中 vuetools 开发插件 的下载、安装
|
3月前
|
Web App开发 存储 前端开发
《Chrome谷歌插件Top10》开发最好用的谷歌插件
本文介绍了多个实用的浏览器插件及其安装方法。包括CSDN浏览器助手,提供高效开发工具;FeHelper,前端必备工具,支持格式化、压缩等功能;uBlock Origin,有效屏蔽广告和弹窗;PageLiner,网页标尺工具,便于前端设计;Fatkun,批量下载图片;Smallpdf,文件转换工具;Octotree,GitHub代码树插件;Awesome Screenshot,截图与录屏工具;ColorZilla,颜色拾取器;Dark Reader,暗黑模式阅读插件。安装方式有通过Chrome商店搜索或下载crx插件本地安装。
67 11
|
3月前
|
Web App开发 JSON 前端开发
30个Chrome 灵魂插件!
30个Chrome 灵魂插件!
|
3月前
|
Web App开发 JSON 安全
【跨域难题终结者】:一键解锁Chrome浏览器神秘设置,彻底告别开发阶段的跨域烦恼!
【8月更文挑战第20天】跨域是前端开发常遇难题,尤其在前后端分离项目中。浏览器因安全考量会阻止不同源间的请求。本文对比CORS、JSONP、代理服务器等解法,并介绍开发阶段通过调整Chrome设置来临时禁用跨域限制的方法,提供启动Chrome及使用`fetch`API示例,适合快速测试。但请注意这不适用于生产环境,存在一定安全风险。
855 1
|
3月前
|
Web App开发 前端开发 JavaScript
手摸手教你,从0到1开发一个Chrome浏览器插件
开发 Chrome 插件既有趣又具成就感。本教程将引导你从零开始,逐步创建一个简单的 Chrome 插件。首先了解 Chrome 插件是可增强浏览器功能的小程序。以一个基础示例开始,你将学习如何设置开发环境,包括安装 Chrome 和准备文本编辑器,并掌握 HTML、CSS 和 JavaScript 的基础知识。接着,我们将构建插件的基本结构,涉及 `manifest.json` 配置文件、`background.js` 后台脚本、`popup.html` 用户界面以及 `style.css` 样式表。
310 8
|
3月前
|
Web App开发
Chrome 护眼模式 - 黑暗模式 - 夜眼(Night Eye) 插件
Chrome 护眼模式 - 黑暗模式 - 夜眼(Night Eye) 插件
142 0
Chrome 护眼模式 - 黑暗模式 - 夜眼(Night Eye) 插件
|
3月前
|
Web App开发 人工智能 iOS开发
灵办AI助手Chrome插件全面评测:PC Web端的智能办公利器
《灵办AI助手:Mac OS下的高效办公利器》 灵办AI助手是一款专为提升工作效率而设计的浏览器插件,适用于Chrome、Edge等主流浏览器,在Mac OS系统中表现尤其出众。本文将深入评测其核心功能,包括网页翻译、AI对话、AI阅读及代码辅助等,展示如何在实际工作中运用这些功能来提升效率。此外,文中还提供了详细的安装与设置指南,帮助读者轻松上手这款办公神器。无论你是学生、职场人还是开发者,灵办AI助手都能成为你提高生产力的理想选择。
120 0
|
2月前
|
Web App开发 数据采集 存储
WebDriver与Chrome DevTools Protocol:如何在浏览器自动化中提升效率
本文探讨了如何利用Chrome DevTools Protocol (CDP) 与 Selenium WebDriver 提升浏览器自动化效率,结合代理IP技术高效采集微博数据。通过CDP,开发者可直接操作浏览器底层功能,如网络拦截、性能分析等,增强控制精度。示例代码展示了如何设置代理IP、cookie及user-agent来模拟真实用户行为,提高数据抓取成功率与稳定性。适用于需要频繁抓取互联网数据的应用场景。
452 3
WebDriver与Chrome DevTools Protocol:如何在浏览器自动化中提升效率

热门文章

最新文章

下一篇
无影云桌面