首页 / 浏览器扩展开发入门教程 / 框架与 TypeScript 开发

浏览器扩展开发入门教程

框架与 TypeScript 开发

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

TypeScriptVueReactSvelte类型提示chrome-types框架集成

本节目标:学完你能在 Vite 扩展里接上 Vue、React 或 Svelte,并用 TypeScript 让 chrome.* API 和组件都带类型提示。

上一章环境搭好了。这一章往上叠两层:一层是 UI 框架,一层是 TypeScript。两者不绑定,你可以只要 TS 不要框架,也可以两者都要。

重点提醒一句:框架只负责「页面长什么样、交互怎么组织」。扩展真正的能力,还是来自 MV3 的 chrome.* API。构建出的扩展照样用 chrome.*,别被框架带偏。

55-1 为什么值得上 TypeScript

裸 JS 写扩展,最大的麻烦是「拼错不知道」。比如把 chrome.storage.local 写成 chrome.storge.local,运行时才报错,调试费劲。

TypeScript 在写的时候就能标红,还能在你敲 . 之后列出 chrome.storage 下有哪些方法。这种提示对扩展开发尤其有用,因为 chrome.* 的命名空间又多又长。

Note

类型提示不改运行结果,它只在开发阶段帮你少犯错。最终打包出来的还是普通 JS。

55-2 以 Vue 为例接框架

Vue 是扩展 UI 里最常见的选择。接入只要两步:装官方插件,写进 Vite 配置。

npm install vue
npm install -D @vitejs/plugin-vue

然后在 vite.config.ts 里把 Vue 插件挂上:

import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
import { crx } from "@crxjs/vite-plugin"
import manifest from "./manifest.config"

export default defineConfig({
  plugins: [crx({ manifest }), vue()],
})

注意 crx 在前、vue() 在后。顺序上 crx 负责扩展打包,框架插件负责把 .vue 文件编译成 JS,两者各管一段,互不冲突。

之后你的弹出页就能用单文件组件了。一个最小 popup:

<template>
  <button @click="count++">点了 {{ count }} 次</button>
</template>

<script setup lang="ts">
import { ref } from "vue"
const count = ref(0)
</script>

<script setup lang="ts"> 同时开了组合式 API 和 TypeScript,是现在 Vue 的主流写法。

框架能帮你把弹出页这种交互界面拆成组件,状态管理也更清晰。但要注意,扩展的「能力边界」由 MV3 决定,框架只是把界面写得更痛快。后台脚本我一般不建议挂框架,保持纯 TS 更轻、服务工作者唤醒也更快。

55-3 React 与 Svelte 同样思路

换框架不换骨架,只换插件。

React 用 @vitejs/plugin-react

npm install react react-dom
npm install -D @vitejs/plugin-react
import react from "@vitejs/plugin-react"
export default defineConfig({
  plugins: [crx({ manifest }), react()],
})

Svelte 用 @sveltejs/vite-plugin-svelte

npm install svelte
npm install -D @sveltejs/vite-plugin-svelte
import { svelte } from "@sveltejs/vite-plugin-svelte"
export default defineConfig({
  plugins: [crx({ manifest }), svelte()],
})

三个框架的接入方式高度雷同,区别只在插件名字和文件后缀。你精通一个,另外两个照葫芦画瓢就能上手。没必要为了扩展专门换框架,沿用你熟悉的那个最省心,也最不容易出意外。

Tip

选哪个框架看你顺手,扩展本身不强依赖某一个。弹出页、选项页是框架的主战场;后台服务工作者里一般不放框架,保持轻量即可。

55-4 TypeScript 配置要点

装好 typescript 后,需要一份 tsconfig.json。扩展场景里有几个容易漏的点。

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "types": ["chrome-types"],
    "lib": ["ESNext", "DOM", "DOM.Iterable"]
  },
  "include": ["src", "manifest.config.ts"]
}

逐条说。strict: true 打开全部严格检查,值得开。moduleResolution: "bundler" 配合 Vite 解析,少踩坑。lib 里要含 DOM,因为弹出页能用 document 这类浏览器对象。

最关键的是 types: ["chrome-types"]。它让 TS 认识 chrome.* 全部 API。

Note

chrome-types 是 Google 官方(GitHub 的 GoogleChrome 组织)维护的 Chrome API 类型包,从 Chromium 源码自动生成、随 Chrome 版本更新;社区方案是 DefinitelyTyped 的 @types/chrome。装它:npm install -D chrome-types。有了它,chrome.storage.local.get 这类调用才有提示。

55-5 在代码里拿到类型提示

配置就位后,写后台脚本就能享受提示了。

// src/background/index.ts
chrome.runtime.onInstalled.addListener(() => {
  console.info("扩展已安装")
})

chrome.storage.local.set({ ready: true })

输入 chrome. 的瞬间,编辑器会列出所有可用命名空间:alarmsstoragetabsscripting……拼错立马标红。这就是类型提示的回报。

内容脚本里也一样:

// src/content-script/index.ts
const box = document.querySelector("#app")
if (box) {
  box.textContent = "来自扩展的内容脚本"
}

内容脚本里调用 chrome.storage 同样有提示,和后台共用同一套类型。你在一个上下文里学到的写法,到其他上下文直接复用,不用重新记一遍 API 名字。这也是上 TS 后的隐性收益:知识不割裂。

Tip

如果你接了模块十二讲的 webextension-polyfill,类型走 @types/webextension-polyfill。本章主线仍是 chrome.*,polyfill 只是跨浏览器时的可选桥接。

55-6 路径别名与自动导入

项目一大,到处写 ../../utils/xxx 很烦。Vite 支持配别名,TS 也要同步认识。

vite.config.ts 里加 resolve.alias

import { fileURLToPath } from "node:url"

export default defineConfig({
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("src", import.meta.url)),
    },
  },
})

tsconfig.json 里补 paths,两边路径要对上:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

配好后,代码里就能 import { foo } from "@/utils/foo",清爽很多。

Note

Vite 的别名管「打包时怎么找文件」,TS 的 paths 管「写代码时怎么提示」。两处都要配,只配一边会提示报错但能跑,体验割裂。

框架生态里常配「自动导入」:写 refcomputed 不用手动 import,插件在编译时替你补上。Vue 生态常用 unplugin-auto-import

这不是必需项,只是提效。对 0 基础来说,先手动 import 把逻辑理清更重要,等项目大了再上自动导入不迟。

Tip

自动导入会生成一份类型声明文件(如 auto-imports.d.ts)。把它加进 tsconfig.jsoninclude,否则 TS 会报「找不到名称 ref」。

55-7 类型声明文件别忘了

TS 项目里常有一个 src/vite-env.d.ts,用来给 Vite 专属的全局类型(比如导入 .vue.css 文件)兜底。

/// <reference types="vite/client" />

没有它,TS 可能报错「找不到模块 ./App.vue」。框架项目里这行几乎是标配,别漏。

Note

.d.ts 是「只声明、不产出代码」的文件。它告诉 TS 某些东西长什么样,打包时会被忽略,纯粹服务于开发期的提示。

55-8 严格模式与类型检查

strict: true 不只是拦拼写错。它还会强制你处理「可能为空」的情况,比如 document.querySelector 返回 null 时,不判断就取属性会直接标红。

const el = document.querySelector("#app")
// 严格模式下,下面这行会提示 el 可能为 null
el.textContent = "hi"

养成先判空再使用的习惯,运行时崩溃会少一大半。这对内容脚本操作 DOM 尤其关键,因为目标页面有没有那个节点,你事先无法确定。

Tip

一开始开严格模式会觉得处处报错,挺烦。但它是「写代码时烦一时,运行时稳很久」。建议从第一天就开着,别等项目写大了再回头补。

有个命令值得单独记:vue-tsc --noEmittsc --noEmit。它只做类型检查、不产出文件。

{
  "scripts": {
    "typecheck": "vue-tsc --noEmit"
  }
}

npm run typecheck 就能在提交前揪出类型错误。它和 npm run build 是两条线:构建管产物,检查管质量。

Note

Vite 默认用 esbuild 转译 TS,速度极快但不做完整类型检查。所以类型错误不会让构建失败——这正是 typecheck 命令存在的意义。

55-9 小结

框架让 UI 好写,TypeScript 让代码稳。chrome-types 是获得 chrome.* 提示的关键,tsconfiglibpaths 别漏配。

主线就一条:框架和 TS 都是「开发体验」层的增强。扩展最终调用的能力,依然是 MV3 的 chrome.* API。下一章我们把多上下文打包和热重载讲透。