简介axure-chrome-extension-V0.6.3 是一款面向 UI/UX 设计师与原型制作人员的 Chrome 浏览器扩展用于在本地直接预览 Axure 生成的 HTML 原型免去上传服务器或切换环境的繁琐步骤适合需要频繁检查设计细节、调试交互逻辑的初中级设计从业者。资源包共 7 个文件以 3 个 png 图标资源、2 个 js 脚本、1 个 json 配置清单和 1 个 html 页面为主压缩包整体约 25KB体积轻巧安装后即可在浏览器中启用本地预览能力。目前已有 606 人学习下载说明该插件在原型预览场景中具有一定实用价值。通过其中的脚本与清单文件读者可了解扩展的加载机制与图标配置方式快速完成安装并投入实际设计流程减少软件切换成本提升原型验证与团队协作效率。1. 从「axure-chrome-extension-V0.6.3 下载」说起一个原型标注插件到底解决什么问题如果你在做产品原型尤其是用 Axure 输出 HTML 之后大概率遇到过这个场景开发同学打开你发的原型链接看到的只是一堆静态页面尺寸、间距、颜色值、交互说明全靠嘴说或者另开文档。axure-chrome-extension 这类浏览器扩展要干的事就是在 Axure 生成的 HTML 原型上叠加一层「标注层」——鼠标悬停显示元素尺寸、点击复制色值、自动生成 CSS 片段。V0.6.3 这个版本号说明它已经迭代过几轮不是玩具脚本。这篇文章面向三类人一是想给自己团队做一套原型标注工具的前端二是拿到扩展但不知道怎么装、怎么配、怎么改的 PM 或设计三是想基于现成扩展二次开发、接入自己设计规范的工程师。我会把「下载之后怎么跑起来、参数怎么调、哪些地方容易翻车」讲清楚代码和配置都能直接抄。需要说明的是这类扩展的核心逻辑并不复杂难的是对 Axure 输出结构的适配和边界处理这也是后面几章的重点。2. 拆解 axure-chrome-extension 的运行机制它凭什么能读到原型里的元素2.1 扩展的三个组成部分与消息流转一个标准的 Chrome 扩展由 manifest、content script、backgroundService Worker和 popup/options 页面组成。axure-chrome-extension 这类工具通常把「读取 DOM、计算样式」放在 content script 里因为只有它能直接访问页面 DOM把「跨标签页状态、快捷键注册、存储」放在 background把「开关、参数配置」放在 popup。消息流转是这样的popup 里点「开启标注」→ 发消息给 background → background 用chrome.tabs.sendMessage转发给当前标签页的 content script → content script 在页面上注入标注层。反过来content script 检测到用户点击某个元素把元素信息回传给 popup 展示。理解这条链路后面排查「点了没反应」就有方向了。// content-script.js 核心监听来自 popup/background 的指令 chrome.runtime.onMessage.addListener((msg, sender, sendResponse) { if (msg.type TOGGLE_INSPECT) { // 开启或关闭标注模式 inspectEnabled msg.enabled; if (inspectEnabled) { document.addEventListener(mousemove, handleHover, true); document.addEventListener(click, handleClick, true); } else { document.removeEventListener(mousemove, handleHover, true); document.removeEventListener(click, handleClick, true); removeOverlay(); // 清理已注入的标注层 } sendResponse({ ok: true }); } return true; // 保持消息通道开放异步响应必须 });这段代码的关键点有三个capture: true让监听在捕获阶段触发避免被 Axure 自身的交互脚本拦截return true是 Chrome 扩展消息机制的硬性要求漏了它异步sendResponse会失效removeOverlay必须做否则反复开关会叠加多层标注 DOM页面越来越卡。2.2 为什么必须用 getBoundingClientRect 而不是 offsetWidthAxure 输出的 HTML 大量使用绝对定位和 transform 做缩放适配。如果你用offsetWidth读尺寸拿到的是元素未经 transform 的原始值和用户实际看到的尺寸对不上。正确做法是getBoundingClientRect()它返回的是渲染后的实际几何信息。function getElementMetrics(el) { const rect el.getBoundingClientRect(); const style window.getComputedStyle(el); return { width: Math.round(rect.width), height: Math.round(rect.height), // 注意rect 是相对视口的滚动时要加上 scrollX/scrollY 才是文档坐标 top: Math.round(rect.top window.scrollY), left: Math.round(rect.left window.scrollX), fontSize: style.fontSize, color: style.color, background: style.backgroundColor, padding: ${style.paddingTop} ${style.paddingRight} ${style.paddingBottom} ${style.paddingLeft}, margin: ${style.marginTop} ${style.marginRight} ${style.marginBottom} ${style.marginLeft} }; }参数说明rect.width/height是含 border 和 padding 的边框盒尺寸getComputedStyle返回的是最终计算值颜色是rgb()格式如果你要十六进制得自己转。这里有个坑——Axure 里很多元素是嵌套的 div你 hover 到的可能是最外层容器而不是视觉上的那个块所以后面要做「元素筛选」。2.3 元素筛选怎么让 hover 命中用户真正想标注的那个Axure 原型里一个按钮可能由 3 到 5 层 div 叠成。如果直接取event.target用户永远选不到想要的那层。常见做法是设置一个「最小有效面积」阈值并向上回溯找到第一个有背景色或边框的祖先。function pickMeaningfulElement(target) { let el target; let depth 0; while (el el ! document.body depth 6) { const style window.getComputedStyle(el); const hasVisual style.backgroundColor ! rgba(0, 0, 0, 0) || style.borderTopWidth ! 0px || el.tagName IMG || el.tagName BUTTON; const rect el.getBoundingClientRect(); // 面积太小或不可见的跳过 if (hasVisual rect.width 4 rect.height 4) { return el; } el el.parentElement; depth; } return target; // 兜底返回原始目标 }depth 6是防止无限向上回溯到 body面积阈值 4px 是经验值能过滤掉分割线和装饰性元素。这个逻辑不是万能的遇到复杂嵌套还是会有偏差但比直接用 target 好得多。3. 本地跑通 axure-chrome-extension V0.6.3从加载到标注的完整步骤3.1 加载未打包扩展与 manifest 配置要点拿到扩展目录后在浏览器地址栏进入扩展管理页打开「开发者模式」点「加载已解压的扩展程序」选中包含 manifest.json 的根目录。V0.6.3 这类版本通常用 Manifest V3因为 V2 已经被主流浏览器逐步淘汰。{ manifest_version: 3, name: Axure Inspector, version: 0.6.3, permissions: [activeTab, scripting, storage], host_permissions: [file:///*, http://*/*, https://*/*], background: { service_worker: background.js }, action: { default_popup: popup.html }, content_scripts: [ { matches: [file:///*, http://*/*, https://*/*], js: [content-script.js], css: [overlay.css], run_at: document_idle } ] }参数说明host_permissions里的file:///*很关键——Axure 导出的原型经常是本地 HTML 文件直接打开没有这条权限扩展在本地文件上完全不工作这是新手最容易卡住的地方。run_at: document_idle保证 DOM 加载完再注入避免读到空节点。activeTab比tabs权限更小更安全能用就用它。3.2 在 Axure 导出的 HTML 上验证标注是否生效加载扩展后打开一个 Axure 导出的index.html本地文件或本地服务器都行点击扩展图标开启标注开关。正常情况下鼠标移动会出现高亮框点击元素后 popup 里显示尺寸和样式。如果没反应按这个顺序排查第一看扩展图标是否变灰变灰说明 content script 没注入第二在页面按 F12 打开控制台看有没有报错第三在控制台手动执行chrome.runtime.id如果报 undefined 说明当前页面不在扩展的匹配范围内。本地文件场景下还要确认浏览器是否允许扩展访问文件网址——这个开关在扩展详情页里默认可能是关闭的。# 快速起一个本地服务器验证避免 file:// 权限问题 # 在 Axure 导出目录下执行 python3 -m http.server 8080 # 然后浏览器访问 http://localhost:8080/index.html用本地服务器有个额外好处http://localhost天然在 host_permissions 覆盖范围内省去调文件权限的麻烦。而且 Axure 原型里如果有 AJAX 请求file:// 协议下会被 CORS 拦本地服务器能规避。3.3 标注层样式注入与 z-index 冲突处理标注层要浮在原型内容之上但 Axure 自身可能用了很高的 z-index。常见做法是把标注层 z-index 设成 214748364732 位有符号整数最大值并用position: fixed让它不受父级定位影响。/* overlay.css */ .axure-inspector-highlight { position: fixed; pointer-events: none; /* 关键不拦截鼠标事件否则无法继续 hover */ z-index: 2147483647; border: 1px solid #1a73e8; background: rgba(26, 115, 232, 0.08); transition: all 0.05s ease-out; } .axure-inspector-tooltip { position: fixed; z-index: 2147483647; pointer-events: none; background: #202124; color: #fff; font: 12px/1.5 monospace; padding: 6px 10px; border-radius: 4px; max-width: 320px; word-break: break-all; }pointer-events: none是血泪经验——不加这个高亮框会挡住鼠标导致 hover 事件在框和元素之间反复触发出现闪烁。transition时间设短一点50ms太长会感觉标注框「追不上」鼠标。4. 参数调优与二次开发让标注结果贴合你自己的设计规范4.1 尺寸单位换算与 DPR 处理在高分屏上getBoundingClientRect返回的是 CSS 像素但设计稿可能是 2 倍图。如果你要对比设计稿得把 CSS 像素乘以 devicePixelRatio。function formatSize(cssPx) { const dpr window.devicePixelRatio || 1; const designPx Math.round(cssPx * dpr); // 同时展示 CSS 像素和设计像素方便对照 return ${Math.round(cssPx)}px (设计稿 ${designPx}px ${dpr}x); }参数说明devicePixelRatio在普通屏是 1Retina 屏是 2 或 3。注意这个值会随浏览器缩放变化如果用户缩放了页面dpr 也会变所以每次读取都要重新取不能缓存。4.2 自定义色值输出格式与设计 token 映射很多团队有自己的设计 token比如主色不叫#1a73e8而叫--color-primary。可以在扩展的 options 页面里让用户配置一张映射表标注时优先显示 token 名。// 色值到 token 的映射可从 chrome.storage 读取用户配置 const colorTokenMap { rgb(26, 115, 232): --color-primary, rgb(52, 168, 83): --color-success, rgb(234, 67, 53): --color-error }; function resolveColorToken(rgbString) { return colorTokenMap[rgbString] || rgbString; }这里要注意getComputedStyle返回的颜色格式可能是rgb()也可能是rgba()带透明度的要单独处理。映射表建议放在chrome.storage.sync里这样换设备也能同步。4.3 一键复制 CSS 片段的实现与剪贴板权限标注工具的高频操作是「复制样式」。用navigator.clipboard.writeText即可但要注意它需要页面处于聚焦状态且 content script 里调用可能受权限限制。async function copyCssSnippet(metrics) { const snippet [ width: ${metrics.width}px;, height: ${metrics.height}px;, font-size: ${metrics.fontSize};, color: ${metrics.color};, background: ${metrics.background};, padding: ${metrics.padding};, margin: ${metrics.margin}; ].join(\n); try { await navigator.clipboard.writeText(snippet); showToast(已复制); } catch (e) { // 降级方案用临时 textarea execCommand fallbackCopy(snippet); } }navigator.clipboard在非 HTTPS 或非 localhost 环境下不可用所以必须有降级方案。fallbackCopy用创建一个隐藏 textarea、选中、document.execCommand(copy)的老办法虽然 API 已废弃但兼容性最好。5. 避坑指南axure-chrome-extension 使用中最容易翻车的五个点现象一扩展装上后图标是灰的点开提示「无法在此页面使用」。原因当前页面不在host_permissions或content_scripts.matches覆盖范围内最常见的是本地file://文件没开访问权限。 解决进扩展详情页把「允许访问文件网址」打开或者改用本地服务器访问。现象二hover 高亮框疯狂闪烁鼠标一动就跳。原因高亮框没有设pointer-events: none它自己成了鼠标事件的目标导致 hover 在框和底层元素之间来回切换。 解决给所有标注层元素加pointer-events: none这是硬性要求。现象三标注的尺寸和设计稿对不上差了一倍。原因没处理devicePixelRatio把 CSS 像素当成了设计像素。 解决读取尺寸时乘以 dpr并在 UI 上同时展示两个值避免混淆。现象四Axure 原型里的动态面板切换后标注层错位或消失。原因动态面板切换会重建 DOM之前绑定的元素引用失效标注层位置没更新。 解决用MutationObserver监听 DOM 变化变化后重新计算标注层位置或者干脆在每次 hover 时实时计算不缓存。现象五反复开关标注功能后页面越来越卡。原因每次开启都注入新的标注 DOM关闭时没清理干净DOM 节点累积。 解决开启前先调用一次removeOverlay()确保页面上只有一层标注 DOM关闭时彻底移除并解绑事件监听。6. 进阶技巧把标注数据导出成结构化 JSON 供团队复用前面讲的都是「看」但标注的真正价值在于「沉淀」。我一般会加一个「导出当前页面所有标注」的功能把用户标注过的元素信息收集成 JSON直接喂给设计系统或代码生成工具。// 收集当前页面所有已标注元素导出结构化数据 function exportAnnotations() { const annotations []; document.querySelectorAll([data-axure-annotated]).forEach(el { const m getElementMetrics(el); annotations.push({ selector: buildSelector(el), // 生成稳定的 CSS 选择器 tag: el.tagName.toLowerCase(), text: (el.textContent || ).trim().slice(0, 50), metrics: m, token: resolveColorToken(m.background) }); }); const blob new Blob([JSON.stringify(annotations, null, 2)], { type: application/json }); const url URL.createObjectURL(blob); const a document.createElement(a); a.href url; a.download axure-annotations-${Date.now()}.json; a.click(); URL.revokeObjectURL(url); // 及时释放避免内存泄漏 }buildSelector是这里最需要花心思的部分。Axure 生成的 class 名往往是随机字符串不能直接用。稳妥的做法是优先用 id没有 id 就用「标签名 nth-child」组合并验证选择器在当前页面唯一。function buildSelector(el) { if (el.id) return #${el.id}; const path []; while (el el.nodeType 1 el ! document.body) { let selector el.tagName.toLowerCase(); const parent el.parentElement; if (parent) { const siblings Array.from(parent.children) .filter(c c.tagName el.tagName); if (siblings.length 1) { selector :nth-of-type(${siblings.indexOf(el) 1}); } } path.unshift(selector); el parent; } return path.join( ); }导出后的 JSON 可以直接对接两类下游一是设计 token 校验脚本检查原型里有没有用非规范色值二是代码生成模板把标注数据转成组件 props 或样式变量。我自己的习惯是每次评审前跑一次导出把 JSON 丢进版本库这样原型改了什么、尺寸变了多少diff 一目了然比截图对比靠谱得多。最后说一个我踩过的坑早期我把标注数据存在localStorage里结果 Axure 原型自己也在用 localStorage键名冲突导致数据互相覆盖。后来全部改用chrome.storage.local和页面存储彻底隔离再没出过问题。做扩展一定要记住content script 和页面共享同一个存储空间能用扩展自己的存储就别碰页面的。希望帮到你。本文还有配套的精品资源点击获取