Next.js 客户端路由切换时如何更新 Permissions Policy?
结论
通过 Next.js <Link> 进行客户端导航时,浏览器不会加载新的 HTML 文档。即使 middleware.ts 为页面 B 的内部数据请求返回了新的 Permissions-Policy 响应头,当前文档采用的策略也不会随之改变。要让页面 B 使用另一套策略,需要触发完整的文档导航;如果继续使用客户端导航,同一个 SPA 文档就应采用一致的 Permissions Policy。
为什么页面仍在使用策略 X
题目描述的过程是:页面 A 返回策略 X。点击 <Link> 后,Next.js 请求页面 B 的 JSON props 数据,响应中包含策略 Y。页面 B 的界面更新了,但浏览器仍按策略 X 运行。完整刷新页面 B 后,浏览器才采用策略 Y。
这是因为客户端路由请求只用于获取页面数据并更新当前文档,不会触发新的顶层文档导航。浏览器在加载页面文档时,才会根据响应头设置该文档的 Permissions Policy。数据请求中返回的同名响应头不会把当前文档的策略从 X 改为 Y。
可选处理方式
让页面使用同一套策略
如果多个页面可以共用一套权限策略,可以在中间件中按需生成响应头,并确保这套策略适用于通过客户端路由访问的页面。例如:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const response = NextResponse.next()
const policy = 'camera=(), microphone=()' // 替换为应用需要的策略
response.headers.set('Permissions-Policy', policy)
return response
}
这段代码可以设置文档响应的策略,但 <Link> 发起的客户端导航不会因此替换当前文档的策略。示例中的策略仅用于展示响应头的设置方式,应按应用的实际需要配置。
需要不同策略时触发完整页面加载
如果页面 B 必须使用不同于页面 A 的策略,就让导航加载新的文档。可以使用普通链接,也可以显式执行完整导航:
window.location.assign('/page-b')
完整加载时,浏览器会收到页面 B 的文档响应,并采用其中的 Permissions-Policy。这种方式会触发完整页面加载,是否适合要看应用对导航体验的要求。
注意事项
不要根据页面 B 的 JSON props 响应头是否包含策略 Y,来判断浏览器是否已更新当前文档的策略。应检查页面 B 的文档导航响应,并结合浏览器的实际执行结果确认策略。
如果既要保留客户端导航,又要让不同页面使用不同的文档级 Permissions Policy,这与当前文档的导航机制不相符。此时需要统一文档策略,或在页面边界处改用完整导航。
备注:内容仅供参考。
