LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

如何自己写一个浏览器插件?

zhenglin
2026年7月6日 9:52 本文热度 146

刷文章看到一句特别好的话,想存下来。复制,切到备忘录,粘贴,再切回来把链接也复制一遍……来回折腾好几下,有时候切回来页面还找不着了。

我后来干脆自己写了个小插件:网页里划词,旁边蹦个按钮,点一下就存好了。今天就把整个过程拆给你,顺便说说 Chrome 插件到底是个啥。


Chrome 插件是什么?

你平时用的 AdBlock、翻译插件、密码管理器,本质上都是 Chrome 扩展(Extension)

可以这么理解:


Chrome  = 手机系统

插件    = 第三方 App

网页    = 某个 App 里的界面

网页里的 JS 只能在当前页面里折腾——改 DOM、发请求,都受同源策略管着。插件不一样,Chrome 单独给它开了一扇后门:可以跨网站注入脚本、读写存储、监听标签页切换,甚至拦截网络请求。

说白了,就是给浏览器本身加功能,不是给某个网站加功能。


和普通网页 JS 比,强在哪?

特点网页 JSChrome 插件
能跑几个网站就当前这个匹配到的都能跑
关页面还在不在没了Background 可以常驻
有没有独立界面只能改页面有自己的 Popup 小窗口
数据存哪localStorage,跟网站绑死chrome.storage,插件自己管

做「划词收藏」这种跨网站的小工具,用插件比油猴脚本正规,也比纯书签靠谱——数据跟着插件走,不跟某个域名走。



四大组件,先认个脸

一个 Chrome 插件通常由四块拼起来。不用全用上,但得知道各自干啥:


manifest.json   → 身份证。名字、版本、权限、入口,全在这

Background      → 后台。监听浏览器事件,插件关着面板也在跑

Content Script  → 页面特工。注入到网页里,能摸 DOM

Popup           → 小面板。点工具栏图标弹出来的那个窗口

它们之间靠消息传递配合,大致是这样:


用户点 Popup 里的按钮

    ↓

Popup 告诉 Background(或者直接找 Content Script)

    ↓

Content Script 在页面上动手

基础版先用三块:manifest + Content Script + Popup。Background 先不急——等加右键收藏的时候再让它上场。


我们要做什么?

一句话:选中文字 → 点收藏 → 打开图标看列表

具体长啥样:


1. 任意网页划词,旁边蹦一个蓝色「收藏」按钮

2. 点一下,文字、来源标题、链接、时间全存进去

3. 点工具栏图标,弹个小面板,列出所有收藏

4. 能点链接跳回原文,也能删

平时看文章,划一句话旁边冒个按钮;点工具栏图标,面板弹出来,里面是之前存的列表。不花哨,但够用。

下面开始写代码。这个插件我放在 chrome-plugins 目录下——打算做一个插件集合,这是第一个,后面还会加。你跟着建个子文件夹 quote-saver 就行。


第一步:建文件夹

chrome-plugins/

└── quote-saver/

    ├── manifest.json

    ├── background.js      # 后面加右键收藏时再建

    ├── content.js

    ├── popup.html

    ├── popup.js

    └── icons/

        └── icon128.png   # 可选,128×128 的 png



icon128.png 找张 128×128 的图放进去就行,没有也不影响功能,manifest 里不写 icon 字段照样跑,工具栏就是个默认的拼图块图标。background.js 是后面加右键收藏时才建的,一开始不用建。


第二步:manifest.json

每个插件都必须有这个文件,Chrome 靠它知道你是谁、要啥权限。

{

  "manifest_version": 3,

  "name": "划词收藏",

  "version": "1.0.0",

  "description": "选中网页文字,一键收藏",

  "permissions": ["storage"],

  "action": {

    "default_popup": "popup.html",

    "default_title": "我的收藏",

    "default_icon": {

      "128": "icons/icon128.png"

    }

  },

  "icons": {

    "128": "icons/icon128.png"

  },

  "content_scripts": [

    {

      "matches": ["<all_urls>"],

      "js": ["content.js"],

      "run_at": "document_idle"

    }

  ]

}

几个字段记一下:

  • manifest_version: 3 —— 现在新建插件都得用 MV3,旧版 MV2 不让上了


  • permissions: ["storage"] —— 只申请存数据的权限,别贪多,装插件的人会在意这个


  • action.default_popup —— 点图标弹出 popup.html


  • action.default_icon / icons —— 工具栏和扩展管理页的图标,不写就用默认的灰色拼图块


  • content_scripts —— 告诉 Chrome 在哪些页面注入 content.jsmatches 里的 <all_urls> 就是字面意思:所有网页都注入。document_idle 是等 DOM 解析完了再跑,不抢页面加载的活儿




第三步:content.js —— 划词和收藏按钮

Content Script 跑在网页里面,能操作 DOM,但跟页面自己的 JS 变量是隔开的——你在 content.js 里定义的变量,页面 JS 访问不到,反过来也一样。正好,我们挂个悬浮按钮,不会跟网站自己的代码搅在一起。

test{ }

监听 mouseup 看有没有选中文字,有的话在选区旁边画个按钮。点一下,文字、标题、链接、时间一起塞进 chrome.storage.local。最多存 100 条——应该够用,真不够再改。


第四步:popup.html —— 收藏列表

点工具栏图标弹出来的小窗口,就是个普通 HTML 页面,样式随便写。

<!DOCTYPE html>

<html lang="zh-CN">

<head>

  <meta charset="UTF-8">

  <style>

    * { box-sizing: border-box; margin: 0; padding: 0; }

    body { width: 360px; max-height: 480px; font-family: system-ui, sans-serif; font-size: 13px; }

    header { padding: 14px 14px; border-bottom: 1px solid #eee; font-weight: 600; }

    #list { overflow-y: auto; max-height: 420px; }

    .item { padding: 10px 14px; border-bottom: 1px solid #f0f0f0; }

    .item:hover { background: #fafafa; }

    .text { color: #222; line-height: 1.5; margin-bottom: 6px;

            display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }

    .meta { color: #888; }

    .meta a { color: #1a73e8; text-decoration: none; }

    .del { float: right; color: #c00; cursor: pointer; border: none; background: none; }

    .empty { padding: 40px; text-align: center; color: #999; }

  </style>

</head>

<body>

  <header>我的收藏</header>

  <div id="list"></div>

  <script src="popup.js"></script>

</body>

</html>


第五步:popup.js —— 读列表、删条目

let saveBtn = null;


document.addEventListener('mouseup', () => {

  const text = window.getSelection().toString().trim();


  if (text.length < 2) {

    removeBtn();

    return;

  }


  showBtn(text);

});


function showBtn(text) {

  removeBtn();


  const range = window.getSelection().getRangeAt(0);

  const rect = range.getBoundingClientRect();


  // 防止按钮跑到屏幕外面

  const btnLeft = Math.min(rect.right + 8, window.innerWidth - 80);

  const btnTop = Math.min(rect.bottom + 8, window.innerHeight - 40);


  saveBtn = document.createElement('button');

  saveBtn.textContent = '收藏';

  saveBtn.style.cssText = `

    position: fixed;

    left: ${btnLeft}px;

    top: ${btnTop}px;

    z-index: 2147483647;

    padding: 4px 10px;

    font-size: 13px;

    border: none;

    border-radius: 4px;

    background: #1a73e8;

    color: #fff;

    cursor: pointer;

    box-shadow: 0 2px 6px rgba(0,0,0,.2);

  `;


  // 不加这行的话,点按钮会先把选中取消掉

  saveBtn.addEventListener('mousedown', (e) => e.preventDefault());


  saveBtn.addEventListener('click', async () => {

    await saveQuote(text);

    removeBtn();

    showToast('已收藏');

  });


  document.body.appendChild(saveBtn);

}


function removeBtn() {

  if (saveBtn) {

    saveBtn.remove();

    saveBtn = null;

  }

}


async function saveQuote(text) {

  const { quotes = [] } = await chrome.storage.local.get('quotes');


  quotes.unshift({

    id: Date.now(),

    text,

    title: document.title,

    url: location.href,

    time: new Date().toLocaleString()

  });


  await chrome.storage.local.set({ quotes: quotes.slice(0, 100) });

}


function showToast(msg) {

  const toast = document.createElement('div');

  toast.textContent = msg;

  toast.style.cssText = `

    position: fixed; top: 20px; right: 20px; z-index: 2147483647;

    background: #333; color: #fff; padding: 8px 16px;

    border-radius: 4px;

  `;

  document.body.appendChild(toast);

  setTimeout(() => toast.remove(), 1500);

}


// 页面滚动了,按钮位置会飘,干脆去掉

document.addEventListener('scroll', removeBtn, true);

 

Content Script 往里写,Popup 往外读,用的是同一个 chrome.storage.local,key 都是 'quotes'。两边对上就行。

对了,这里用的是 local,数据只存在当前电脑上。想换电脑也能看到收藏的话,把 local 改成 sync 就行——但 sync 只有 100KB 配额,收藏多了容易爆,文本量大的话还是 local 稳。



第六步:装进 Chrome

代码写完了,不用发布,本地就能跑:

  1. 地址栏输入 chrome://extensions/,回车(Edge 用 edge://extensions/,Brave 用 brave://extensions/,都一样)

  2. 右上角「开发者模式」打开

  3. 「加载未打包的扩展程序」→ 选 chrome-plugins/quote-saver 文件夹

  4. 工具栏出现图标,搞定


找篇文章试试:划词 → 点「收藏」 → 再点图标看列表。删一条看看列表会不会更新。都正常的话,第一个插件就算做完了。


调试时我踩过的坑

改代码之后记得两处刷新:扩展管理页点刷新按钮,已经打开的网页也要 F5。Content Script 是页面加载时注入的,不刷新网页它不会变。

看 Popup 的 log    → 右键图标,「检查弹出内容」

看 Content Script  → 普通网页 F12,Sources 里找 content.js

有个报错特别常见:

Extension context invalidated

扩展刚 reload 过,老页面里的 Content Script 已经废了,刷新网页就好。我第一次看到还以为是代码写错了,折腾半天。

还有 storage 写了但 Popup 看不到——八成是 key 名没对上,或者 Popup 打开时没调 render()


顺手再加两个功能

基础版跑通了,但用着用着有两个事不太得劲:一是有些网站自己的 JS 会吞掉 mouseup 事件,划词按钮压根弹不出来;二是想换浏览器,收藏的数据没地方导出。



该文章在 2026/7/6 9:52:31 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-2  粤公网安备44030602007207号