当前位置: 首页 > news >正文

Chrome插件学习笔记(三)

Chrome插件学习笔记(三)

参考文章:

  • https://blog.csdn.net/guoqiankunmiss/article/details/135847091
  • https://blog.csdn.net/guoqiankunmiss/article/details/135974364

1、项目搭建

在前两篇文章中使用的原生js去操作dom,很费劲并且效果并不是很好,于是决定使用vue3来开发浏览器插件

1.1、创建项目

npm create vite@latest

1.2、依赖安装

# @types/node、@types/ws:用来补充类型信息
# chokidar:用来监听文件变化
# chrome-types:chrome相关的类型提示
# rollup-plugin-copy:用于复制manifest.json
# ws:用于创建WebSocket
npm install -D @types/node @types/ws chokidar chrome-types rollup-plugin-copy ws

1.3、vite.config.ts

import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from "node:path";
import copy from "rollup-plugin-copy";// https://vite.dev/config/
export default defineConfig({server: {port: 5500,},plugins: [vue(),copy({targets: [{src: 'src/manifest.json',dest: path.resolve(__dirname, 'dist')}],hook: 'writeBundle'}),],build: {outDir: path.resolve(__dirname, 'dist'),rollupOptions: {input: {side_panel: path.resolve(__dirname, 'src/index.html'),content_script: path.resolve(__dirname, 'src/content_script/content_script.ts'),service_worker: path.resolve(__dirname, 'src/service_worker/service_worker.ts'),},output: {assetFileNames: 'assets/[name]-[hash].[ext]',chunkFileNames: 'js/[name]-[hash].js',entryFileNames: (chunkInfo) => {const facadeModuleId = chunkInfo.facadeModuleId!const baseName = path.basename(facadeModuleId, path.extname(facadeModuleId))const saveArr = ['content_script', 'service_worker']return `[name]/${saveArr.includes(baseName) ? baseName : chunkInfo.name}.js`;},name: '[name].js'}}}
})

1.4、manifest.json

{"name": "Assistant","description": "A tool for quick conversations with AI","version": "0.0.1","manifest_version": 3,"permissions": ["sidePanel"],"host_permissions": ["*://*/*"],"action": {"default_title": "Click to switch side panel"},"side_panel": {"default_path": "src/index.html"},"background": {"service_worker": "service_worker/service_worker.js","type": "module"},"content_scripts": [{"js": ["content_script/content_script.js"],"matches": ["*://*/*"],"all_frames": true,"run_at": "document_end","match_about_blank": true}]
}

1.5、vite-env.d.ts

/// <reference types="vite/client" />
/// <reference types="chrome-types/index" />

1.6、service_worker.ts

console.log("service_worker");chrome.sidePanel.setPanelBehavior({openPanelOnActionClick: true})

1.7、content_script.ts

console.log("content_script");

1.8、index.html

index.html需要移动到src目录下

1.9、目录结构

在这里插入图片描述

1.10、测试

在这里插入图片描述

2、热部署

热部署的方式很多,如利用额外的插件(可参考文章https://cm-mng.bilibili.co/agent/#/mlogin),这里使用的是socket通信来实现的热部署

2.1、.env.development

VITE_ENV_MODE=development

2.2、 .env.production

VITE_ENV_MODE=production

2.3、file_watcher.ts

import {WebSocketServer} from "ws";
import chokidar from "chokidar";
import path from "node:path";export function file_watcher(wss: WebSocketServer) {wss.on('connection', (ws) => {console.log('Client connected');// 监听src文件夹下的所有文件,并排出node_modules下所有文件const watcher = chokidar.watch(path.join(__dirname, 'src'), {ignored: [/(^|[\/\\])\../, /node_modules/]});// 只有文件变化才会通知,如果设置成all将会通知add、change、addDir等事件watcher.on('change', (event, path) => {ws.send(JSON.stringify({type: 'reload', event, path}));});ws.on('close', () => {console.log('Client disconnected');watcher.close();});});
}

2.4、vite.config.ts

import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from "node:path";
import copy from "rollup-plugin-copy";
import {WebSocketServer} from "ws";
import {file_watcher} from "./file_watcher.ts";// https://vite.dev/config/
export default defineConfig((configEnv) => {// 这里取的环境变量是.env.xxxxx中的变量,根据加载的环境变量判断是否启动socket服务端,用于通知浏览器插件重新加载let env = loadEnv(configEnv.mode, process.cwd(), '');if (env.VITE_ENV_MODE === 'development') {file_watcher(new WebSocketServer({port: 5501}));}return {server: {port: 5500,},plugins: [vue(),copy({targets: [{src: 'src/manifest.json',dest: path.resolve(__dirname, 'dist')}],hook: 'writeBundle'}),],build: {outDir: path.resolve(__dirname, 'dist'),rollupOptions: {input: {side_panel: path.resolve(__dirname, 'src/index.html'),content_script: path.resolve(__dirname, 'src/content_script/content_script.ts'),service_worker: path.resolve(__dirname, 'src/service_worker/service_worker.ts'),},output: {assetFileNames: 'assets/[name]-[hash].[ext]',chunkFileNames: 'js/[name]-[hash].js',entryFileNames: (chunkInfo) => {const facadeModuleId = chunkInfo.facadeModuleId!const baseName = path.basename(facadeModuleId, path.extname(facadeModuleId))const saveArr = ['content_script', 'service_worker']return `[name]/${saveArr.includes(baseName) ? baseName : chunkInfo.name}.js`;},name: '[name].js'}}}}
})

2.5、hot_reload.ts

let timeout: numberexport function hot_reload(socket: WebSocket) {if (timeout) {clearTimeout(timeout);}socket.onopen = () => {console.log('Connected to dev server')}socket.onmessage = (event) => {const message = JSON.parse(event.data)if (message.type === 'reload') {setTimeout(() => {chrome.runtime.reload()}, 1000)}}socket.onclose = (event) => {console.log(`Socket closed: ${event.reason}`);// 断开连接后5秒后尝试再次连接timeout = setTimeout(() => {hot_reload(new WebSocket(`ws://localhost:5501/`))}, 5000);};socket.onerror = (error) => {console.error('Socket error:', error);};
}

2.6、service_worker.ts

import {hot_reload} from "../hot_reload.ts";console.log("service_worker");chrome.sidePanel.setPanelBehavior({openPanelOnActionClick: true})// 如果是开发环境才尝试链接socket服务端
if (import.meta.env.VITE_ENV_MODE === 'development') {console.log('开发环境');hot_reload(new WebSocket(`ws://localhost:5501/`))
} else {console.log('生产环境');
}

2.7、package.json

注意这里的scripts需要修改下,开发的时候使用命令npm run watch,修改代码后插件会自动重新加载

{"name": "assistant","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite","watch": "vite build --watch --mode development","build": "vue-tsc -b && vite build","preview": "vite preview"},"dependencies": {"@types/node": "^24.1.0","@types/ws": "^8.18.1","chokidar": "^4.0.3","chrome-types": "^0.1.363","rollup-plugin-copy": "^3.5.0","vue": "^3.5.17","ws": "^8.18.3"},"devDependencies": {"@vitejs/plugin-vue": "^6.0.0","@vue/tsconfig": "^0.7.0","typescript": "~5.8.3","vite": "^7.0.4","vue-tsc": "^2.2.12"}
}
http://www.dtcms.com/a/298592.html

相关文章:

  • 【7 周速成通关】单片机从理论到实操速学路径(附知识图谱)
  • kettle 8.2 ETL项目【三、加载数据】
  • MEMS 定向短节在振动环境下精度有保障吗?
  • 中国航天集团实习第一周总结
  • 利用Trae将原型图转换为可执行的html文件,感受AI编程的魅力
  • 企业微信H5应用OAuth2登录,企业微信授权登录
  • RocketMQ 做成服务启动
  • FastGPT:重新定义企业专属知识库的灵活部署
  • Linux进程信号——信号产生
  • 【LeetCode 热题 100】22. 括号生成——(解法一)选左括号还是选有括号
  • Linux嵌入式工程师学习路线
  • Linux系统基本配置以及认识文件作用
  • 执行测试时测试数据准备困难如何处理?
  • 汪小菲食通达公司成立新零售公司,布局餐饮零售新赛道
  • 【算法】动态规划 · 上篇
  • Python day24
  • CRM 系统:实现商机全流程管理的关键
  • Qt XML 与 JSON 数据处理方法
  • 文搜图/图搜图
  • OSPF 协议(多区域)
  • Inception网络架构:深度学习视觉模型的里程碑
  • 去甲基化药物联合DLI治疗AML1-ETO阳性
  • 图书推荐-由浅入深的大模型构建《从零构建大模型》
  • 【C++避坑指南】vector迭代器失效的八大场景与解决方案
  • 管理系统模板
  • 应用程序无法正常启动(0xc000007b)怎么办 解决方法详解
  • Matlab学习笔记:结构基础
  • 数仓规范体系的构建
  • SVM多分类实战:从理论到应用的完整指南
  • Linux的磁盘存储管理实操——(下二)——逻辑卷管理LVM的扩容、缩容