首页 / 浏览器扩展开发入门教程 / options 选项页开发

浏览器扩展开发入门教程

options 选项页开发

本教程共 56 篇 · 第 27 篇 · 更新于 2026-08-13 · 约 7 分钟阅读

options选项页options_uichrome.storage设置持久化openOptionsPage

本节目标:学完能做一个让用户改设置的选项页,并把设置存下来、在别处读出来。

27-1 选项页是做什么的

选项页是给用户调偏好的地方:开关某个功能、选择主题、填一个默认参数。

它在扩展管理界面里打开,适合放比 popup 更复杂、更完整的表单。

popup 适合一步操作,选项页适合一堆配置。两者分工很清晰,不要混着用。

Note

选项页和 popup 是两套独立页面。选项页一般从“扩展选项”入口进,不是点图标弹出的。

新手常犯的错误,是把所有选项塞进 popup。popup 空间小、关掉就丢,不适合复杂表单。

一个经验判断:如果一个设置需要解释、需要多个控件组合,它就属于选项页;如果只是点一下切换,popup 里放个开关就够了。

Tip

也可以两个都用:popup 放最常用的开关,选项页放完整的高级设置,两者通过同一份存储保持同步。

选项页还有一个好处:用户从浏览器“管理扩展”页面就能进入,不需要先点开 popup。它是扩展面向用户的一张“正式设置脸面”。

27-2 声明选项页:options_ui

MV3 推荐用 options_ui 来声明选项页,它在扩展管理页里内嵌显示。

{
  "options_ui": {
    "page": "options/options.html",
    "open_in_tab": false
  }
}

open_in_tab 为 false 时,选项页内嵌在扩展管理界面中,体验更统一。

如果设为 true,点击后会在一个普通标签页里打开,像一张独立网页。

除了 options_ui,清单里还有个字段 options_page 也能指向选项页。新扩展建议直接用 options_ui。

{
  "options_page": "options/options.html"
}
Tip

内嵌(open_in_tab:false)的选项页在某些浏览器样式上更受限,但用户操作更顺手。按你的需求选。

如果你要用到 options_page,需要注意它和 options_ui 不要同时声明指向不同页面,否则容易引起混乱。统一用 options_ui 是当下最省心的选择。

27-3 选项页的 HTML 与表单

选项页就是一张普通 HTML,里面放表单元素就好。

<form id="settings">
  <label>
    <input type="checkbox" id="enable"> 启用功能
  </label>
  <label>
    主题
    <select id="theme">
      <option value="light">浅色</option>
      <option value="dark">深色</option>
    </select>
  </label>
  <button type="submit">保存</button>
</form>

表单元素用原生 input、select、textarea 即可,不依赖任何框架。

你也可以用 range、radio、color 等控件,逻辑和普通网页完全一致。

Note

选项页默认不继承浏览器样式。想要统一的观感,可以自己写一份简洁的 CSS。

选项页同样受 MV3 的内容安全策略约束,所以脚本也必须外链,不能用内联事件。写法和 popup 一样:<script src="options.js"></script>

27-4 用 chrome.storage 保存设置

用户改了设置,第一反应可能是 localStorage。在扩展里别这么做。

localStorage 在扩展里不好使:服务工作者(Service Worker)里根本没有它,内容脚本拿到的又是宿主网页的存储,而且它随“清除浏览数据”一起消失。

正确选择是 chrome.storage,它在 popup、选项页、后台之间都通用。

chrome.storage.local.set({ enable: true, theme: "dark" });

读取同样简单,支持回调或 Promise:

const res = await chrome.storage.local.get(["enable", "theme"]);
console.log(res.enable, res.theme);

local 存在本地,sync 会随账号同步。看你需要选一个命名空间。

两者都是异步的,返回的是 Promise,所以推荐用 await 来写,读起来更顺。

Note

sync 有容量和写入频率限制,适合存少量设置;大体积数据请放 local,别占 sync 配额。

存储里不仅能放字符串和数字,对象、数组也照存不误。所以你可以把一组相关设置打包成一个对象,一次读写。

Tip

如果设置要让多设备同步,优先用 chrome.storage.sync;只存本机状态就用 local。

举个例子,把设置归成一个对象:

await chrome.storage.local.set({
  settings: { enable: true, theme: "dark", pageSize: 20 }
});

读取时一次性取回整个 settings,代码会更整洁,也方便整体回写。

27-5 回写设置到存储

选项页最常见的动作就是“保存”。提交表单时把值写进存储。

const form = document.getElementById("settings");

form.addEventListener("submit", (e) => {
  e.preventDefault();
  chrome.storage.local.set({
    enable: document.getElementById("enable").checked,
    theme: document.getElementById("theme").value
  });
});

写进去之后,别的上下文就能读到最新值,做到真正的“一处设置、处处生效”。

Tip

不一定非要“保存”按钮。监听 change 事件实时写入,体验往往更顺。

实时写入的好处是用户关掉页面也不会丢设置,不必等他点保存。

document.getElementById("theme").addEventListener("change", (e) => {
  chrome.storage.local.set({ theme: e.target.value });
});

这种“改动即保存”的模式,省掉了保存按钮,也避免了用户改了却忘点保存的尴尬。

27-6 打开时读取并回填

用户第二次打开选项页,得把上次的值填回表单,不然又是一片空白。

chrome.storage.local.get(["enable", "theme"], (res) => {
  document.getElementById("enable").checked = !!res.enable;
  document.getElementById("theme").value = res.theme || "light";
});

其他上下文(如内容脚本)想跟上变化,可监听存储变化事件。

chrome.storage.onChanged.addListener((changes, area) => {
  if (area !== "local") return;
  if (changes.theme) {
    applyTheme(changes.theme.newValue);
  }
});

onChanged 在任意上下文都能监听,是让设置“即时生效”的关键。

Note

onChanged 回调的 changes 里,每个键都带 oldValue 和 newValue,方便你做增量处理。

回填要在 DOM 加载完成后进行。如果你的选项是模块化脚本,记得等 DOMContentLoaded 之后再读存储,否则 getElementById 会拿不到元素。

27-7 程序化打开选项页

有时你想在 popup 里放一个“设置”按钮,点一下直接跳到选项页。

用 chrome.runtime.openOptionsPage 就行,不需要用户手动去找入口。

document.getElementById("openOptions").addEventListener("click", () => {
  chrome.runtime.openOptionsPage();
});

这个方法在支持 options_ui 的环境都能正确打开对应页面。

Note

openOptionsPage 不需要任何额外权限,只要你在清单里声明了 options_ui 或 options_page。

它返回的是一个 Promise,所以你也可以 await 它,在打开完成后再做点什么,比如写一条日志。

27-8 调试选项页

调试选项页和调试普通网页一样:在扩展管理页点“扩展选项”打开它。

打开后右键“检查”,就能用 DevTools 看控制台、网络、存储内容。

想确认存没存上,可以在控制台直接读 chrome.storage.local.get(null)。

Note

改了清单里的 options_ui 路径后,记得在扩展管理页点“重新加载”,否则新页面不会生效。

如果你用的是 open_in_tab:false 的内嵌模式,调试面板有时需要先在选项页内右键“检查”才能打开。内嵌页和标签页模式在调试入口上略有差别,别找错地方。

27-9 小结

回头看,选项页的套路就三步:用 options_ui 把页面挂上,用 chrome.storage 存取值,用 openOptionsPage 兜底打开。

它和 popup 最大的区别是“重”:能放完整表单、能跨上下文共享、能长期保存。

Tip

设置项一多,建议把它们归到一个对象里再整体存取,比如 settings: {enable, theme},读取更省事。

真正容易卡住新手的,往往不是 API 本身,而是忘了在 onChanged 里让改动即时生效。

把这一章和存储、消息两章连起来看,你会发现扩展的“配置闭环”已经跑通了。

27-10 常见误区

第一个误区,是以为选项页和 popup 共享内存变量。它们是两个独立页面,谁也看不到谁的变量,唯一可靠的桥梁是 chrome.storage。

第二个误区,是改了设置却不回填。用户重开页面看到空白表单,会以为保存失败。打开时务必读取存储、写回控件。

第三个误区,是只存不监听。如果你希望内容脚本立刻响应新主题,光写存储不够,还得在内容脚本里也挂上 onChanged。

Note

想验证整条链路是否通,最笨也最有效的办法:存完去控制台读 chrome.storage.local.get(null),确认值真的写进去了。