尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Quasar BEX 扩展类型全解析:New Tab、Popup、Options、DevTools 与网页注入实战

Quasar BEX 扩展类型全解析:New Tab、Popup、Options、DevTools 与网页注入实战 前端UI组件跨平台【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址https://gitcode.com/gh_mirrors/qu/quasar点击查看免费下载导读本文围绕 Quasar 官方文档中浏览器扩展BEX的入口类型这一主题展开系统讲解如何使用一套 Quasar 应用quasar/app-vite同时承载新标签页New Tab、弹出窗Popup、选项页Options、开发者工具页DevTools以及注入网页的浮层 UI五种浏览器扩展入口。你将掌握每种入口的 manifest 配置方式、基于路由Hash 模式的页面映射技巧以及通过 IFrame 内容脚本把 Quasar 应用嵌入任意网页并实现双向通信的完整实战方案。在 Quasar 中浏览器扩展模式的入口形态远比普通网页应用灵活同一个 App 的 UI 既可以独立运行在浏览器自带的标签页、弹窗与开发者工具面板中也可以被注入到第三方网页里形成浮层。核心思路是——用 Vue Router 定义路由用src-bex/manifest.json把每个入口指向www/index.html#/route。下文将逐一展开。五种 BEX 入口类型概述浏览器扩展BEX本质上是一类运行在浏览器托管上下文中的应用可以自定义浏览器本身或它展示的页面。Quasar 的 BEX 模式支持以下五种运行形态而一个 Quasar 应用即可覆盖全部五种无需为每种类型单独创建项目New Tab新标签页在浏览器自己的标签页中运行替换浏览器默认的新标签页。Developer Tools开发者工具面板运行在浏览器开发者工具窗口中。Popup弹出窗点击扩展图标后弹出的窗口。Options选项页扩展的设置页面。Web Page 注入以内容脚本 IFrame 的方式运行在被注入的网页上下文里。前四种New Tab、DevTools、Popup、Options本质上是浏览器自己管理页面的入口只需要在 manifest 中把对应字段指向带路由的www/index.html即可第五种则需要额外的注入与通信机制是扩展能力最丰富的场景文档也专门用一节案例演示。关于 BEX 的整体结构可参考 配置 BEX五种类型的官方原始说明见 types-of-bex.md。New Tab替换浏览器的新标签页新标签页类型最简单在 manifest 中将chrome_url_overrides指向www/index.html浏览器就会在用户新建标签页时加载你的 Quasar 应用。{ chrome_url_overrides: { newtab: www/index.html } }注意两点点击扩展图标toolbar 图标与新建标签页是两条不同路径。点击图标走的是 manifest 的actionManifest v3或browser_actionManifest v2配置而不是chrome_url_overrides。如果希望点击图标也打开同一页面需要同时配置action.default_popup或default_title等字段。构建产物中的www/index.html来自 Quasar 应用在/src下的 UIVite 构建输出构建或开发时会被注入到扩展包中详细机制见 bex-config.js 中vite()对build.outDir的处理。Developer tools、Options 与 Popup同一套路由三种入口这三种入口遵循完全相同的模式在 Vue Router 中创建路由再把 manifest 中对应字段指向带 Hash 的路由地址。Hash 模式路由www/index.html#/popup之所以可行是因为扩展页面的 URL 是chrome-extension://extension-id/www/index.html#/...不存在服务端因此不能依赖 history 模式所需的服务器 rewrite 规则——Hash 路由由浏览器本地解析天然适配扩展环境。1. 定义路由在 Quasar 应用的routes.js中为每种入口创建独立页面组件const routes [ { path: /options, component: () import(/pages/OptionsPage.vue) }, { path: /popup, component: () import(/pages/PopupPage.vue) }, { path: /devtools, component: () import(/pages/DevToolsPage.vue) } ]路由采用懒加载每个入口页面只在被打开时才下载对应组件。2. 在 manifest 中引用路由Manifest v3Chrome / Chromium 系{ manifest_version: 3, action: { default_popup: www/index.html#/popup }, options_page: www/index.html#/options, devtools_page: www/index.html#/devtools }Manifest v2旧版兼容{ manifest_version: 2, options_page: www/index.html#/options, browser_action: { default_popup: www/index.html#/popup }, devtools_page: www/index.html#/devtools }字段语义对照入口Manifest v3Manifest v2Popup点击图标弹出action.default_popupbrowser_action.default_popupOptions扩展选项页options_pageoptions_pageDevTools开发者工具面板devtools_pagedevtools_page关于 DevTools 的补充devtools_page本身只是开发者工具面板的入口 HTML真正的面板 UI 通常需要在该页面中通过chrome.devtools.panels.create()创建你可以把www/index.html#/devtools路由对应的页面组件写成负责创建面板的引导逻辑或直接在其中渲染面板内容。工程细节Quasar 的 manifest 支持all/chrome/firefox三个顶层对象构建时按目标浏览器合并all与目标对象深合并。如果你需要为 Chrome 和 Firefox 使用不同的 manifest 版本或不同的 background 机制可以像模板那样分别配置例如 Chrome 用background.service_workerFirefox 用background.scripts参见 模板 manifest。合并逻辑位于 bex-utils.js 的createManifest()且构建时会自动校验manifest_version字段是否存在。Case study把 Quasar 应用注入网页Web Page 注入这是五种类型中真正体现扩展威力的场景通过内容脚本创建一个 IFrame把 Quasar 应用塞进去再注入目标网页让应用看起来就像网页本身的一部分。整体思路内容脚本src-bex/my-content-script.js在目标页面加载时执行创建 IFramesrc指向扩展包内的www/index.html插入到页面顶部。Quasar 应用/src通过 BEX Bridge 向内容脚本发送事件控制 IFrame 高度等行为。内容脚本监听事件并操作 IFrame 与底层页面例如展开抽屉时把 IFrame 拉满全屏关闭时恢复成只露出工具栏的高度。下面按文档中的三个文件逐一实现。第一步内容脚本 —— 创建并注入 IFramesrc-bex/my-content-script.js/** * Importing the file below initializes the content script. * * Warning: * Do not remove the import statement below. It is required for the extension to work. * If you dont need createBridge(), leave it as import #q-app/bex/content. */ import { createBridge } from #q-app/bex/content const bridge createBridge({ debug: false }) /** * When the drawer is toggled set the iFrame height to take the whole page. * Reset when the drawer is closed. */ bridge.on(wb.drawer.toggle, ({ payload }) { if (payload.open) { setIFrameHeight(100%) } else { resetIFrameHeight() } }) const iFrame document.createElement(iframe) const defaultFrameHeight 62px /** * Set the height of our iFrame housing our BEX * param height */ function setIFrameHeight(height) { iFrame.height height } /** * Reset the iFrame to its default height e.g The height of the top bar. */ function resetIFrameHeight() { setIFrameHeight(defaultFrameHeight) } /** * The code below will get everything going. Initialize the iFrame with defaults and add it to the page. * type {string} */ iFrame.id bex-app-iframe iFrame.width 100% resetIFrameHeight() // Assign some styling so it looks seamless Object.assign(iFrame.style, { position: fixed, top: 0, right: 0, bottom: 0, left: 0, border: 0, zIndex: 9999999, // Make sure its on top overflow: visible }) ;(function () { // When the page loads, insert our browser extension app. iFrame.src chrome.runtime.getURL(www/index.html) document.body.prepend(iFrame) })()要点解析import { createBridge } from #q-app/bex/content这行不能删除——它负责初始化内容脚本上下文#q-app/bex/content是 Quasar CLI 提供的别名模块即使不用createBridge()也要保留import #q-app/bex/content。默认 IFrame 高度只有62px刚好容纳 Quasar 工具栏高度因此页面主体仍可与底层网页交互收到wb.drawer.toggle事件且open为真时高度切换为100%让整个抽屉可见。chrome.runtime.getURL(www/index.html)把扩展包内的 UI 页面解析为chrome-extension://协议地址——这正是UI 以www文件夹形式注入扩展包的直接应用。IFrame 使用position: fixed覆盖全屏并设置极高zIndex保证浮层永远在最上层。第二步给页面内容留出工具栏空间src-bex/assets/content.css.target-some-header-class { margin-top: 62px; }给目标页面的头部元素增加62px的上外边距避免 IFrame 中的工具栏遮挡网页原有内容62px 与 IFrame 的默认高度一致二者需要保持同步。第三步Quasar 应用中控制抽屉并通知内容脚本在/src的 Vue 组件中使用q-drawer监听其开关事件通过$q.bexBEX Bridge 的 App 端接口向内容脚本发送事件q-drawer :model-valuedrawerIsOpen update:model-valuedrawerToggled Some Content /q-drawerimport { useQuasar } from quasar import { ref } from vue setup () { const $q useQuasar() const drawerIsOpen ref(true) async function drawerToggled () { const contentPort $q.bex.portList.find(portName portName.startsWith(contentmy-content-script-) ) if (contentPort void 0) { return } await $q.bex.send({ event: wb.drawer.toggle, to: contentPort, payload: { open: drawerIsOpen.value } }) // Only set this once the promise has resolved so we can see the entire slide animation. drawerIsOpen.value !drawerIsOpen.value } return { drawerToggled } }关键机制端口查找内容脚本的 Bridge 端口命名规则为content脚本相对路径-实例号见 bex-bridge.md所以用startsWith(contentmy-content-script-)匹配每个标签页都会有一个独立的内容脚本实例portList中可能同时存在多个匹配项这里取第一个。先发事件再翻转状态drawerIsOpen.value的翻转放在await之后确保 IFrame 先完成拉高动画抽屉内容才完整可见。返回值的语义send()返回 Promise可await等待内容脚本处理完bridge.on支持同步返回或返回 Promise 的异步响应。完成这三步后你的 Quasar 应用就已经跑在网页里了。之后可以在 Quasar 应用中触发任意自定义事件例如highlight.content、save.note等内容脚本监听后直接操作底层页面的 DOM 或调用chrome.*API。多内容脚本与资源清单容易踩的坑[!WARNING] 务必检查src-bex/manifest.json尤其是对my-content-script.js的引用。你可以同时拥有多个内容脚本每新建一个都必须同步在 manifest 中注册同理/src-bex/assets下新增的每个 CSS 文件也要在 manifest 中引用。content_scripts: [ { matches: [ all_urls ], css: [ assets/content.css ], js: [ my-content-script.js ] } ]对应到 Quasar 构建流程createManifest()会从background.service_worker、background.scripts、content_scripts[].js以及quasar.config中的bex.extraScripts四处收集脚本清单逐个用 Rolldown 编译产出.js并写入构建目录参见 bex-utils.js 的extractBexScripts()assets、icons、_locales三个目录会被原样复制到构建产物见copyBexAssets()。漏注册的内容脚本不会出现在产物中这是新手最常见的脚本不生效原因。补充两点工程细节TS 开发者background 与 content 脚本在 manifest 中应写.ts扩展名如my-content-script.tsQuasar CLI 会在编译时自动把 manifest 内的.ts/.tsx改写为.js/.jsxextractBexScripts()中的scriptExtRE正则负责剥离扩展名。浏览器厂商只认.js转换由 CLI 自动完成。权限最小化模板默认使用all_urls匹配示例内容脚本可在任意页面运行但这会扩大扩展的权限并增强安装时的权限警告。若扩展只服务特定站点应将matches、host_permissions与web_accessible_resources收敛到最窄范围例如https://*.example.com/*具体可参考 配置 BEX 中的Least-privilege example。与内容脚本通信的基础BEX Bridge网页注入场景中的事件收发依赖 BEX Bridge——这是 Quasar 为扩展各部件background / content scripts / popup / options / devtools提供的基于 Promise 的通信层。核心规则如下详见 bex-bridge.mdBackground 是通信中枢所有消息都经由 background 脚本中的 Bridge 转发。若想让 app 与 content scripts 之间直接通信必须在 background 脚本中创建 Bridge且只在一个 background 脚本中创建切勿多实例。App 端在/src的 Vue 组件中通过$q.bex直接使用$q.bex.portName固定为app。内容脚本端createBridge({ debug: false })创建实例后建议先挂载初始监听器再调用bridge.connectToBackground()建立连接连接成功后bridge.isConnected为true后续也可调用bridge.disconnectFromBackground()主动断开。端口命名content路径-编号如contentmy-content-script-2345编号为 1–10000 的实例号。监听连接变化订阅内部事件quasar:ports可获得{ portList, added?, removed? }用于感知端口增删。广播与定向遍历bridge.portList可向全部 app/content 端口广播也可用portList.find(p p.startsWith(content))定位任意一个内容脚本。安全提示来自内容脚本的消息本质上是不可信输入内容脚本运行在任意网页上下文处理消息前必须校验事件名、发送方、payload 形状、URL 与标识符只暴露狭窄操作并仅申请扩展真正需要的权限。发送大 payload浏览器对扩展消息有硬性大小限制。当payload为数组时Bridge 会自动按数组元素分块发送此时若想发送真正的数组需把数组包进对象里例如payload: { myArray: [...] }否则每个元素会被当作独立消息效率极低分块时记得给每块留出几字节余量因为消息本身还包裹了其他属性。构建与打包从源码到可安装的 ZIP理解入口类型后顺带掌握构建流程有助于排查问题。BEX 构建由app-vite的 bex-builder.js 驱动流程如下createManifest()读取src-bex/manifest.json按目标浏览器合并allchrome/firefox自动补齐name、short_name、description、version取自 package.json执行bex.extendBexManifestJson钩子最后把改写后的 manifest 写入构建目录。copyBexAssets()复制assets、icons、_locales目录。并行构建UI 部分走 Vite产物进www/目录Firefox 与生产构建均输出到dist/www见 bex-config.js各 BEX 脚本background、content scripts、extraScripts走 Rolldown。若无--skip-pkg参数最后用fflate把dist目录打包为Packaged.app-name.zip可直接加载或上传应用商店。开发模式Chrome下quasar dev -m bex会额外注入 WebSocket token 与开发服务器端口使扩展页面能连上 Vite HMR——这也是为什么在 Chrome 下开发时扩展 UI 可以热更新。小结五种入口一套 AppNew Tab / Popup / Options / DevTools 通过www/index.html#/route与 manifest 字段映射网页注入通过内容脚本 IFrame 实现。Hash 路由是扩展环境的默认选择扩展 URL 无服务端Hash 模式无需 rewrite 规则。内容脚本是网页注入的钥匙创建 Bridge、注入 IFrame、用$q.bex.send()触发事件、用bridge.on()响应事件。多内容脚本记得注册每新增一个脚本或assets下的 CSS都要同步更新src-bex/manifest.json否则不会进入构建产物。权限保持最小化把matches、host_permissions、web_accessible_resources收敛到实际所需范围减少权限警告、降低安全风险。相关阅读BEX 类型官方文档配置 BEXmanifest 结构与 quasar.config 选项BEX Bridge 通信机制内容脚本详解BEX 模式构建源码 与 manifest 处理源码赞分享前端UI组件跨平台【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址https://gitcode.com/gh_mirrors/qu/quasar点击查看免费下载相关推荐扩展页面开发实战Popup、Options与New Tab页面扩展页面开发实战Popup、Options与New Tab页面 本文详细介绍了Chrome扩展中四种核心页面的开发技巧Popup弹窗页面、Options设置前端开发工具Quasar BEX 与 TypeScript从脚手架到类型安全的浏览器扩展消息桥Quasar BEX 与 TypeScript从脚手架到类型安全的浏览器扩展消息桥 本指南讲解在 quasar/app vite 项目中如何使用 Type前端UI组件跨平台Quasar BEX Content Scripts 完整指南注册、桥接通信与网页 DOM 交互实战Quasar BEX Content Scripts 完整指南注册、桥接通信与网页 DOM 交互实战 内容脚本Content Scripts是 Quasa前端UI组件跨平台上一篇Typo.js 项目推荐下一篇TypeResolver类型告警异常解析的及时通知机制创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表