blog
blog copied to clipboard
使用 Next.js 开发扩展程序的思考
尝试结果:失败。但还是记录一下过程。
遇到的第一个问题是 Next.js 的静态导出会输出一个名为 _next 的文件夹,而 Chrome 不允许扩展程序根目录有别的 _ 开头的文件夹。这个问题好解决,调整了一下 next.config.js 的 basePath(比如 web),然后将 Next.js 输出的所有文件放到扩展程序的对应目录名(前面设置成 web,那么这里也要叫 web)下,变成了 /web/_next/...、/web/404.html 这样。
遇到了第二个问题解决不了。Chrome 有一个内置的内容安全策略(见文档),默认不允许执行内联脚本,但是 Next.js 输出的文件里,是会在 html 里有内联脚本的。于是我在 Chrome manifest.json 的内容安全策略里加上了 unsafe_inline,但是加载扩展的时候报错了。
看了 Chrome 的文档(见上面链接)才知道,Chrome 顶多允许加一个 wasm-unsafe-eval,其余的都不让加;而 Next.js 的这些内联脚本也找不到办法去掉(即使去掉可以在 html 代码里见到的,说不定在运行时也会动态加载见不到的),所以这个问题解决不了。
最后还是只能自己用 webpack + html-webpack-plugin 生成 html。
以下是原文。
什么是 Next.js?
Next.js 是一款支持服务器渲染(Server Side Rendering,简称 SSR)和静态站点生成(Static Site Generation,简称 SSG)的 React 框架。
Next.js 已经火了好几年了,而我最近才开始尝试,一用就停不下来了,体验确实很好,以后做前端项目肯定都是优先用它了。
为什么要用 Next.js 开发扩展程序?
好处:
- 开发体验非常好。
- 开箱即用、错误明确等。
- 首屏渲染快速。
- 虽然扩展程序是从本地磁盘读取静态文件的,但如果 html 默认是空的,依赖 js 加载完成之后再通过 React 生成界面,那么用户还是会经常看到网页先是一片白屏,然后突然出现界面。
- 这一点在扩展程序的 popup 页面尤其明显,因为空的 html 页面会导致它的高度为 0,用户在点击扩展按钮后,会先看到一个空空的箭头,就在用户感到疑惑的时候,大概半秒左右又会突然出现界面。而如果使用了 SSG 这种首屏渲染,就不会有这个问题了。
- 方便跟设计师合作。
- 如果有设计师参与扩展程序的设计,那么我们就可以把界面单独发在线上的网站里,供设计师验收,而不用每次改动都打包一个完整的 crx 安装包给设计师了。
坏处:
next run dev目前还不支持 SSG 模式,所以每次修改代码之后,如果想在扩展程序内看到变化,都需要重复这三个步骤:- 运行
next run build - 复制 output 到扩展程序内(下文有说明)
- 在浏览器的扩展程序页面刷新扩展程序
- 运行
- 增加了项目结构的复杂度。Next.js 项目是一个单独的代码库,要把它的产物整合进扩展程序需要很多额外的操作。
能不能用于扩展程序开发?
我的结论:部分可以。
扩展程序的代码分为三大类:
- 设置页、Popup 页、Offscreen Document 等:真正等同于网页环境,唯一不同是加入了 chrome.* API 的支持。
- Service Worker:扩展程序的后台进程,纯 Service Worker 运行环境,没有 DOM。
- 内容脚本:有 DOM 的类网页环境,不同之处在于它是由扩展程序插入进宿主网页的,没有自己单独的网页
接下来逐个进行分析。
设置页、Popup 页、Offscreen Document 等
这一类完全等同于网页环境,所以可以用 Next.js 的 SSG 能力输出纯静态页面,然后复制粘贴进扩展程序里,再通过 url 打开即可。
做个详细的说明。
Next.js 的 SSG 产物大概是下面这个样子:
output/
--- .next/ 各种 js、css 等静态文件
--- options.html
--- popup.html
--- offscreen.html
我们只需要把 output 文件夹下面的内容复制粘贴到扩展程序的根目录下,即可在浏览器里通过 chrome-extension://extension-id/options.html 的方式打开这个页面了。
注意:一定要复制到根目录下,因为 html 里引用静态文件时默认是用的绝对路径,e.g. /_next/static/chunks/xxx.js。当然,也许可以通过修改 Next.js 的配置让它的引用路径是相对的,这一点有待验证。
复制到扩展程序根目录下后,文件夹结构是这样:
browser-extension/
--- .next/
--- options.html
--- popup.html
--- offscreen.html
--- manifest.json
--- 其它扩展程序文件如 serviceworker.js
Service Worker
这部分代码不能通过 Next.js 生成,还是只能通过 Rollup / Webpack 等工具生成。
内容脚本
这部分能否通过 Next.js 来开发,由于还没有实际操作过,所以我目前的看法是:存疑。
内容脚本支持 DOM 环境,所以代码是可以用 Next.js 来生成的,比如创建一个 app/content-script/page.tsx,然后在其中编写内容脚本的代码,最后分析一下 SSG 在 content-script.html 里引用的 <script> 标签,就能得到内容脚本的 js 文件列表了,然后写进 manifest.json 里即可。
但是,这里面有几个疑问:
- 动态加载。Next.js 很可能(但由于没实操过所以不能百分百确定)会动态引入 js 代码,而内容脚本的动态注入需要在 Service Worker 中操作。我猜测,也许只要我完全不使用动态引入(即
const mod = await import('./path/to/module')的形式),应该就不会出现。 - 静态文件引用。在内容脚本里引用扩展程序里的静态文件(图标、css、图片等),需要加上
chrome-extension://extension-id/的前缀,不过这应该能通过修改 Next.js 的配置来实现。
最终结论
等我实际操作之后再下定论。