十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Chrome插件开发全攻略:从manifest到上架避坑指南

Chrome插件开发全攻略:从manifest到上架避坑指南 简介面向希望掌握Chrome扩展开发或提升表单自动化效率的开发者该资源以Worktile任务描述表单自动填写为应用场景演示了基于manifest.json、内容脚本、后台脚本与选项页面的完整扩展实现。压缩包共22个文件以JavaScript逻辑脚本、HTML界面、JSON配置及图片样式资源为主整体仅185KB结构紧凑适合初学者快速剖析。已有1878人学习下载。资源内提供popup、options、content-script、background等典型模块其中内容脚本负责直接操作页面DOM后台脚本处理常驻事件与定时任务包含DOM元素选取与赋值、EventTarget.dispatchEvent模拟输入、localStorage数据存储以及MutationObserver监听页面变化等实用写法同时通过manifest权限配置和OAuth授权示例展示了与Worktile交互的完整思路。对需要编写浏览器自动化工具或改善重复性网页操作的人来说这份可运行的插件源码是理解扩展开发流程、调试技巧和发布配置的实用参考样本。 我从一个实际能用的例子出发把这几年折腾 Chrome 插件踩过的坑一并写出来。无论你是刚接触插件开发还是已经写过一两个小工具这篇文章都能让你少走不少弯路。1. 从零写一个 Chrome 插件到底难不难先说结论如果只做一个“能跑起来”的插件难度比你想的低得多。Chrome 插件的核心就是一个 HTML 页面加一段 JavaScript再加上一个描述文件 manifest.json。你没有看错不需要任何框架不需要构建工具甚至不需要服务器本地建个文件夹就能开始。很多人在网上搜“chrome浏览器插件例子”一上来就去找复杂的开源项目结果被各种打包、编译、权限配置吓退了。实际上日常用的那些小工具型插件比如一键复制当前页面标题、自动替换网页里的某些词汇、给页面加个自定义样式都是纯前端就能搞定的。我当年写的第一个插件就是一个在任意网页上选中文字后弹窗显示字数的工具总共也就五个文件从零到能在商店上架花了一个晚上。需要注意的是Chrome 插件现在分两个版本Manifest V2简称 MV2和 Manifest V3简称 MV3。从 2024 年开始Chrome 已经逐步强制使用 MV3新提交的插件不再接受 MV2 版本。很多人下载老项目发现装不上多半就是版本不兼容。MV3 最大的变化是后台脚本从常驻的 background page 改成了 service worker并且不允许使用远程代码所有脚本必须打包在插件内部。这对安全来说是好事但对写惯了传统后台脚本的人来说一开始会有点不习惯。所以我的建议是直接学 MV3别在旧项目上浪费时间。接下来我会用 MV3 的规范带着你走一遍完整流程。2. 核心文件拆解manifest.json、popup、content script2.1 manifest.json 是插件的“身份证”任何一个插件都必须有一个 manifest.json 文件放在根目录。这是 Chrome 识别这个文件夹是不是插件的唯一依据。你可以把它理解成插件的配置中心插件叫什么名字、需要哪些权限、入口文件在哪全在这里声明。下面这个是最简配置{ manifest_version: 3, name: 文字提取助手, version: 1.0.0, description: 提取当前页面选中的文字并复制, action: { default_popup: popup.html, default_icon: icon.png }, permissions: [activeTab, clipboardWrite], content_scripts: [ { matches: [all_urls], js: [content.js], run_at: document_end } ] }逐个解释一下。manifest_version固定为 3不用多说。action定义的是点击工具栏图标后弹出的窗口也就是 popup。default_popup指向一个 HTML 文件当用户点击插件图标时Chrome 会把这个 HTML 当作一个小弹窗展示出来。permissions是权限声明这个非常重要插件能干什么、不能干什么全靠它约束。clipboardWrite是允许写入剪贴板activeTab是在用户主动点击插件时授予当前标签页的临时访问权限。还有一个关键是content_scripts。这里的content.js会注入到所有匹配的网页中执行matches字段声明哪些网址会注入all_urls表示全部网址。run_at指明注入时机document_end是 DOM 加载完成后注入这个时机对大多数场景来说都够用。2.2 popup、content script、background 三者怎么分工很多新手第一次接触插件开发会被这几个概念绕晕。我用一句话总结popup 是插件的“脸面”content script 是插件的“手”background 是插件的“大脑”。popup 是一个普通的 HTML 页面但生命周期极短用户点击图标才会创建点击其他地方就销毁了。所以别在 popup 里写太重的逻辑它只负责显示界面和接收用户操作。content script 运行在网页的上下文中但它拥有独立的 JavaScript 环境不会和页面本身的脚本互相污染。它能操作 DOM、读取页面内容这是插件能“看到”网页的关键。background 是一个 service worker在后台运行适合处理需要常驻或跨页面的事件比如监听浏览器事件、管理多个标签页。但在 MV3 中service worker 会在几秒内休眠所以不能依赖它存储长期状态。三者之间不能直接互相调用变量需要通过消息通信机制传递数据。消息通信有两种方式chrome.runtime.sendMessage用于一次性请求chrome.runtime.connect用于建立长连接。平时大多数场景用第一种就够了。3. 实操写一个能真正用起来的“网页文字提取”插件3.1 需求梳理和目录结构设计直接给需求用户在一个网页里选中一段文字点击右键菜单里的“提取选中文字”弹窗显示提取结果并自动复制到剪贴板。这个需求覆盖了 content script、background、消息通信、右键菜单、剪贴板操作是一个很典型的学习案例。目录结构如下text-extractor/ ├── manifest.json ├── background.js ├── content.js ├── popup.html ├── popup.js └── icon.png其中 icon.png 可以先用任意一张 128x128 的图片代替后面再做正式图标。3.2 右键菜单和后台逻辑实现右键菜单需要在 background.js 里创建因为只有 background 才能调用chrome.contextMenus接口。代码非常简单chrome.runtime.onInstalled.addListener(() { chrome.contextMenus.create({ id: extract-selected-text, title: 提取选中文字, contexts: [selection] }); }); chrome.contextMenus.onClicked.addListener((info, tab) { if (info.menuItemId extract-selected-text) { chrome.tabs.sendMessage(tab.id, { action: getSelectedText }, (response) { if (response response.text) { chrome.scripting.executeScript({ target: { tabId: tab.id }, func: (text) { navigator.clipboard.writeText(text); }, args: [response.text] }); } }); } });这里需要注意contexts: [selection]表示菜单只在用户选中文字时才出现避免在空白区域右键时弹出无意义的选项。chrome.tabs.sendMessage是向指定标签页的 content script 发送消息tab.id是用户当时点击右键的那个标签页。还有一个细节我在 MV3 里用了chrome.scripting.executeScript来执行复制操作而不是直接在 background 里复制。这是因为复制剪贴板需要用户手势或页面上下文权限直接在 service worker 里调用navigator.clipboard往往会失败。这是我在踩过坑之后总结出的正确姿势。3.3 content script 提取选中文字对应地content.js 里需要监听消息并返回选中内容chrome.runtime.onMessage.addListener((message, sender, sendResponse) { if (message.action getSelectedText) { const selectedText window.getSelection().toString().trim(); sendResponse({ text: selectedText }); } return true; });window.getSelection().toString()是获取网页选中文本的标准方式。trim()去掉首尾空格避免提取到一堆无意义的换行。sendResponse是同步返回结果的关键最后一行return true表示这个监听器是异步执行的保持消息通道不关闭。这里有个小细节content script 里的window.getSelection()获取的是当前页面的选择区域如果页面里有 iframe只能获取顶层窗口的选中内容。想要获取 iframe 里的内容需要单独处理比如用document.querySelectorAll(iframe)遍历然后获取每个 iframe 的contentDocument。不过这个需求比较少见一般做爬虫类的插件才会用到。3.4 popup 展示和历史记录popup 页面作为展示层获取到 content script 传来的数据后显示在界面上。这里我加上一个简单的历史记录方便重复查看。popup.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 style body { width: 300px; padding: 12px; font-family: system-ui; } textarea { width: 100%; height: 200px; box-sizing: border-box; } /style /head body h3提取结果/h3 textarea idresult readonly/textarea button idcopy-btn复制/button button idclear-btn清空/button script srcpopup.js/script /body /htmlpopup.jsconst history JSON.parse(localStorage.getItem(extract_history) || []); const resultArea document.getElementById(result); if (history.length 0) { resultArea.value history[history.length - 1].text; } else { resultArea.value 暂无历史记录; } document.getElementById(copy-btn).addEventListener(click, () { navigator.clipboard.writeText(resultArea.value).then(() { alert(已复制); }); }); document.getElementById(clear-btn).addEventListener(click, () { localStorage.removeItem(extract_history); resultArea.value ; });这里用了localStorage来存储历史记录。注意popup 的localStorage是独立的和网页的localStorage不共享这对于插件来说是安全的。但有一个问题popup 每次打开都会重新加载所以我要在加载时读取历史并回显。如果你想做更完善的历史记录可以把数据存在chrome.storage.local中它比localStorage容量更大且能在 popup、content script、background 之间共享。这个场景下用localStorage已经足够了但如果插件功能再复杂一点建议尽早迁移到chrome.storage。4. 插件调试和常见报错排查4.1 如何正确加载未打包的插件写完代码打开 Chrome在地址栏输入chrome://extensions/打开开发者模式点击“加载已解压的扩展程序”选择你的项目文件夹。如果文件没有语法错误插件图标就会出现在工具栏上。这一步看起来简单但有几个点经常出问题。文件夹不能有中文路径某些环境下 Chrome 会报错。manifest.json 必须是纯 JSON 格式不能有注释。MV3 不支持 JSON 注释我之前见过有人用 VSCode 写了注释导致加载失败。icon 文件不能缺失。如果 manifest 里声明了 icon 但文件不存在会加载失败。所以最简单的方法是先不配 icon等整体功能跑通了再加上。4.2 “清单文件缺失或不可读”系列问题这是新手遇到最多的报错。常见原因有三个manifest.json 里某个字段拼写错误。比如manifest_version写成了manifestVersioncontent_scripts写成了contentScripts。某个字段的值类型不对。比如把permissions写成了字符串而不是数组。声明了不存在的文件。比如content_scripts里写了js: [content.js]但该文件不在指定路径。Chrome 的报错信息其实已经足够详细只是很多人不仔细看。建议遇到加载失败时先看报错信息是哪一行再去对照 manifest 规范检查。千万别在没看报错的情况下反复尝试那是浪费时间。4.3 跨域问题no Access-Control-Allow-Origin header很多插件功能涉及向外部 API 发请求比如翻译插件、汇率插件。在 content script 里直接发fetch请求时经常遇到no Access-Control-Allow-Origin header is present on the requested resource的报错。这个问题的根源是浏览器同源策略。content script 的 fetch 请求携带了页面的 origin目标服务器没有返回允许跨域的响应头请求就被拦截了。解决办法是把请求从 content script 搬到 background service worker 里发。service worker 的请求不经过页面 context不受页面同源策略限制只要你在 manifest 中声明了目标域名的host_permissions就能正常请求。依赖这个原理我所在的团队做过一个自动划词翻译插件前端在 content script 捕获选中事件把文本发送给 background 里的 service worker由它调用翻译 API再把结果回传给 popup。这样完美绕开了跨域报错而且后台请求的日志也更清晰调试起来比在页面上下文里看请求省心得多。4.4 Chrome 提示“网站未使用安全连接”或“文件可能已被篡改”这个报错通常发生在下载第三方插件如.crx文件时Chrome 出于安全策略阻止了安装。很多老教程提到“把 .crx 文件拖进扩展程序页”即可安装现在这套基本行不通了。遇到这种情况不建议通过关闭安全选项来处理更合理的做法是找插件的开源或官方渠道直接加载“已解压的扩展程序”。如果你只是自己开发测试用 4.1 节的方法加载文件夹就行完全不需要 .crx 文件。若一定要打包分发建议通过 Chrome Web Store 开发者后台创建项目并上传 zip 包商店会帮你加密签名用户直接从商店安装既安全又方便。5. 从例子到产品隐藏的细节与进阶路线5.1 权限最小化原则千万别滥用设计插件时权限声明要克制。有些插件一上来就申请all_urls和webRequest权限结果用户一看到权限提醒就放弃安装了。Chrome 商店审核也特别看重这一点权限和功能不匹配会被拒。以我们的文字提取插件为例其实完全不需要host_permissions因为 content script 就是通过matches声明的。activeTab权限加上用户主动点击这个行为已经足够覆盖需求。很多功能可以做成按需注入而不是无条件注入到所有页面这样既省资源又安全。5.2 消息通信的实用进阶长连接 vs 一次性消息前面提到sendMessage是一次性消息。如果你的插件需要频繁交互比如实时把页面数据推送给 popup或者多个页面共享状态一次性消息就会显得很笨重。这时可以用chrome.runtime.connect建立长连接。举个例子我们给页面加了一个可视化编辑面板用户在页面上拖拽元素希望 popup 里的统计面板实时更新数字。如果每次拖拽都发一条sendMessage会快速建立大量连接性能很差。用长连接就顺滑很多页面和 popup 建立一条通道数据不停地在通道里流直到用户关闭弹窗。需要注意长连接不是免费的午餐。MV3 后台 service worker 休眠后会断开所有连接所以长连接不能应用于跨长时间的场景。更稳妥的做法是让页面端维护重连逻辑监听断线后自动重发连接请求。5.3 从本地项目到商店上架一次真实的发布流程本地能跑起来只是第一步真要发布到 Chrome Web Store 还要走一遍流程。个人开发者需要先注册开发者账号一次性支付 5 美元注册费。然后进入开发者控制台创建新条目上传 zip 压缩包注意是 zip不是 .crx填写简介、隐私政策、商店图标截图提交审核。审核周期通常几个小时到几天不等不需要太过担心只要权限合规、内容不误导用户一般都能过审。发布后如果更新版本记得把版本号在 manifest.json 里递增否则上传会被拒绝。这条规则很多人第一次发布时会踩折腾到半夜还以为是哪里配置错了其实就是没改版本号。5.4 我踩过的三个最隐蔽的坑最后分享三个花了我最多时间排查的问题希望能帮你避开。第一个是 service worker 休眠问题。MV3 的 background 不是常驻的大概 30 秒没有事件就会休眠休眠后部分状态会丢失。早期我做过一个定时提醒类插件把定时器写在 service worker 里结果用户反馈“完全没提醒”。后来查证才发现是 service worker 休眠后定时器全没了。解决办法是改用chrome.alarmsAPI它由浏览器调度不会因为 service worker 休眠而失效。第二个是 popup 里引用外部 CDN 脚本被拒绝。MV3 默认开启content_security_policy禁止执行远程代码所有 JS 文件必须打包在插件内。我一开始在 popup.html 里通过script srchttps://cdn.example.com/lib.js引用了第三方库直接白屏控制台还报错。解决办法是把库文件下载到本地改成相对路径引用。第三个是window.getSelection()在输入框里选不中内容。如果想提取用户在input或textarea里选中的文本window.getSelection()拿不到只能用document.activeElement.value.substring(selectionStart, selectionEnd)这类方式。当时我在做表单辅助插件时遇到这个坑一头雾水了好几天。这些经验和坑很多是官方文档不会告诉你的只能在实践中摸出来。希望这篇“chrome浏览器插件例子”的拆解能让你少走弯路快速做出第一个能用的插件。如果你卡在某个具体环节建议先打开 Chrome 的开发者工具看 “Service Workers” 和 “Console” 面板的输出大部分问题在日志里都有线索。插件开发不是高不可攀的技术把它当成一个普通的网页项目来写自然就顺畅了。本文还有配套的精品资源点击获取
返回列表