/awesome-chrome-extension-boilerplate

Use react + typescript + webpack to enhance your chrome extension development experience

Primary LanguageTypeScriptMIT LicenseMIT

awesome-chrome-extension-boilerplate

Build Status dependencies Status devDependencies Status Known Vulnerabilities Percentage of issues still open

一个超棒的基于 React & TypeScript & webpack 的 chrome 扩展开发模板

✨ 特性

  • 🔥 支持修改 content scripts 代码自动重载扩展和刷新注入了 content scripts的页面,再也不用修改了 content scripts 后手动刷新扩展和页面了。
  • 🌴 optionspopup 页面支持 react hot reload & react devtools,充分享受现代前端工程化的便捷,让你从开发 SPA 无缝切换到 chrome 扩展开发。
  • 🛡️ 整个模板包括 webpack 配置都是用 TypeScript 编写的,使用 TypeScript 配置 webpack 减少查阅文档和手残的概率。
  • 💄 ​ 支持 css/less/sass,使用 mini-css-extract-plugin 将 CSS 分离成 content CSS Script。
  • ⚒️ 集成了社区很多的优秀的 webpackeslintbabel 插件,优化开发,构建和打包分析体验,还配置了 husky , format-imports, stylelint, travisaudit-ci 构建工具。
  • 🌈 默认集成了 jquerylodashantd 等常用工具库,并对它们的打包进行了优化

📦 安装

# 克隆这个模板
git clone https://github.com/tjx666/awesome-chrome-extension-boilerplate.git

# 安装依赖,推荐使用 pnpm
pnpm

🛠️ 开发

🔔 请确保你对 chrome 扩展开发已经有基本的了解,入门推荐:Chrome 插件(扩展)开发全攻略。如果你对项目的配置有疑问。

准备工作

修改清单文件

在 src 目录下有两个清单文件:manifest.dev.jsonmanifest.prod.json,分别是开发环境和生产环境的配置文件。

注意:任何注入了 content scripts 的页面也必须被注入 js/all.jscss/all.css ,为了实现这一点,它俩的 matches 应该是其它所有 content scriptsmatches 的父集。

示例的配置是:

"content_scripts": [
    // 所有注入了 content scripts 的页面都注入了 js/all.js 和 css/all.css
    {
        "matches": ["https://github.com/*"],
        "css": ["css/all.css"],
        "js": ["js/all.js"]
    },
    // 注入到 github pull requests 页面
    {
        "matches": ["https://github.com/pulls"],
        "css": ["css/pulls.css"],
        "js": ["js/pulls.js"]
    }
]

添加静态资源

public 下的文件会被打包到扩展的根目录,manifest 中用到的图标等资源可以直接放到 public 文件夹下面。模板在 public/icons 放了一些默认的图标,因此可以在 manifest 中这样引用图标:

// manifest.dev.json
{
  "icons": {
    "16": "icons/extension-icon-x16.png",
    "32": "icons/extension-icon-x32.png",
    "48": "icons/extension-icon-x48.png",
    "128": "icons/extension-icon-x128.png"
  }
}

启动 devServer

yarn start

无论是开发环境还是生产环境都会在项目根目录生成 extension 文件夹,chrome 访问 chrome://extensions/ 也就是扩展管理页面,点击右上角的按钮开启开发者模式,选择加载已解压的扩展程序,再选择刚刚生成的 extension 文件夹即可加载扩展。

load extension

由于 chrome 的限制,官方的 chrome 扩展 react devtools 并不能审查 chrome-extension:// 协议的页面如 optionspopup 页面。所以需要使用独立的 react devtools,使用下面的命令启动 devServer 的同时打开独立的 devtools 窗口:

npm run devtools

react devtools

你可以通过 open 参数配置在 webpack 初次编译成功打开某个 URL:

"scripts": {
        "start": "cross-env-shell NODE_ENV=development ts-node --files -P ./server/tsconfig.json ./server --open=https://xxx.xxx.com",
    },

编写代码

模板默认的代码实现的功能是修改 github 导航栏的颜色,模板使用了 normalize.css 和一些自定义样式对 CSS 进行样式重置。

如果你想开发 background 脚本,你可以在 src/background 文件夹编写你的代码。src/background/index.tsbackground 脚本的入口,也是 webpack 的一个 entry,其它像 optionspopup 页面也类似。你可以查看 webpackentry 配置: src/server/utils/entry.ts 了解更多实现细节。

它俩的 webpack entry 分别是 src/options/index.tsxsrc/popup/index.tsx。这两个页面很相似,都只是一个普通的 web 页面,因此你可以像开发一个 react SPA 一样开发它们。

这个模板使用了 react 的最新版本,因此你可以使用 react hooks 去开发函数组件,react hookseslint 规则也集成了。

模板使用 React Fast Refresh 支持 react 的热更新。

这个模板会扫描 src/contents 文件夹,将所有子文件夹中的 index.tsxindex.ts 作为 webpack entry

content scripts 都放在 src/contents 目录下。默认有个 all.ts,也是个 webpack entry,它不能被删除,因为这个 webpack entry 被用于注入实现 chrome 扩展自动刷新功能的补丁。

举个 🌰:

当你要给 URL 是 https://www.example.com/discuss 页面开发 content script,你需要做下面两步:

  1. 添加 content scripts 和页面 URL 之间的映射到 manifest.dev.jsonmanifest.prod.json:

    "content_scripts": [
        {
            "matches": ["https://www.example.com/discuss*"],
            "css": ["css/discuss.css"],
            "js": ["js/discuss.js"]
        }
    ],
  2. 创建一个和上面 content script 路径对应的文件夹 src/contents/discusssrc/discuss/index.tsx 或者 src/discuss/index.ts 将会被视为一个 webpack entry。 webpack 会通过这个 entry 最终产出 js/discuss.js 这个 chunk

    mini-css-extract-plugin 会将所有被 discuss/index.ts 导入的样式文件合并再分离到 extension/css/discuss.css,这也是为什么上面的 manifest 中 content CSS script 可以使用 css/discuss.css 的原因

dev server 代理

你可以在 server/configs/proxy.ts 中配置 dev server 的代理,所有向 dev server 发送的请求都会根据你配置的规则被代理转发,修改配置后需要重启 dev server 才会生效,更多细节请查看使用的中间件 http-proxy-middleware

const proxyTable: ProxyTable = {
  // 如果 devServer 启动地址是 http://127.0.0.1:3600
  // 那么请求 http://127.0.0.1:3600/path_to_be_proxy 将会被 dev server 转发到 http://target.domain.com/path_to_be_proxy
  '/path_to_be_proxy': { target: 'http://target.domain.com', changeOrigin: true },
};

👷 打包

构建生产级别的包直接运行:

pnpm build

如果你想分析打包情况:

pnpm build-analyze

📢 注意事项

src/allsrc/background 下的文件包含了实现修改 content script 自动重载扩展和刷新注入了 content script 页面的功能的代码。除非你不开发 content scripts,否则,不能删除它

📚 Blog

核心原理:使用 webpack 构建 chrome 扩展的热更新问题

🎯 TODO

  • 给 manifest.json 增加 JSON 校验,目前使用的是 SchemaStore 提供的 schema,有极少部分内容已经过时了,有时间要去提个 PR。
  • 支持 webpack dev server 代理
  • 针对 chrome 扩展本身是个多页面应用的特点,提取多个页面的公共依赖到单独的 chunk
  • 集成 jest 测试

🤝 贡献 PRs Welcome

欢迎提交 PRs 和 issues。