strict-dynamic 下 Vue 动态组件加载被 CSP 阻止怎么办?
我在项目里启用了 CSP 的 strict-dynamic 策略,结果 Vue 的动态组件渲染直接被浏览器拦了,控制台报“Refused to execute inline script”。明明没写内联脚本啊,是不是 Vue 的模板编译触发了什么?
试过加 nonce 和 hash,但动态组件路径是变量,根本没法预知。现在卡在这儿了,求问怎么在保持 strict-dynamic 的前提下让 Vue 正常工作?
<template>
<component :is="currentView" />
</template>
<script setup>
import Home from './views/Home.vue'
import About from './views/About.vue'
const currentView = ref(Home) // 或根据路由切换
</script>
先说原因,
strict-dynamic本身只信任带有 nonce 的脚本以及它动态创建的子元素。问题在于 Vue 的异步组件加载或者某些模板编译行为,在浏览器看来就是在"动态执行脚本",尤其如果你用了字符串形式的组件名或者动态 import。解决思路有几个方向。
第一个方案,确保所有组件都是预编译的静态导入。你现在的代码其实已经是这样了,如果还被拦,检查一下 Vue 构建版本。确保用的是 runtime-only 版本,别用带编译器的完整版。带编译器的版本会在运行时编译模板,这个行为会被 CSP 拦截。在 vite 或 webpack 配置里确认一下 alias 指向
vue.runtime.esm-browser.js或类似的 runtime-only 入口。第二个方案,如果你需要真正的动态加载组件,用
defineAsyncComponent配合动态 import,而且要确保构建工具把这些 import 预处理成单独的 chunk。这样构建工具会提前把组件打包成独立文件,运行时只是动态加载已经编译好的 JS,不会触发运行时编译。
第三个方案,如果以上都不行,检查你的 CSP header 配置。
strict-dynamic需要配合一个有效的 nonce 或 hash 才能生效。确保你的主 JS 文件带了正确的 nonce 属性,而且服务端每次响应都要生成新的 nonce 值。还有个容易忽略的点,检查项目里有没有用
v-html或者字符串模板,比如template: '... '这种写法。这些都会触发运行时编译,直接被 CSP 干掉。全部改成 .vue 单文件组件或者 render 函数。浏览器兼容方面,老版本浏览器对
strict-dynamic支持不完整,可能需要降级方案,不过现代浏览器基本都没问题了。实在搞不定的话,把 CSP 改成
'unsafe-eval'加 nonce 的组合,虽然安全性差一点但至少能跑。当然这是下策。