Chrome扩展Manifest V3+Vue开发内联脚本执行失败求助
Chrome扩展Manifest V3内联脚本执行被阻止的解决方案
核心问题分析
你遇到的问题是Manifest V3严格的内容安全策略(CSP)导致的:
- V3默认禁止内联脚本,
unsafe-inline在extension_pages场景下不允许直接使用 - 硬编码的
nonce无效,Chrome要求nonce必须是每次页面加载随机生成的唯一值 - 之前拆分脚本可能存在路径错误或引用问题
解决方案1:拆分内联脚本到单独文件(最合规推荐)
这是Manifest V3最提倡的方案,完全规避内联脚本限制:
1. 新建外部脚本文件
在/scripts/目录下创建app.js,将原内联脚本内容转移进去,同时修复代码里的bug:
const { createApp, ref, reactive, onMounted } = Vue // 时间组件 const time = createApp({ setup(){ const d = new Date() const date = reactive({ year: d.getFullYear(), month: d.getMonth() + 1, // 修复:getMonth()返回0-11,需加1 day: d.getDate(), // 修复:getDay()是星期几,getDate()才是日期 weekday: d.getDay() }) // 修复switch缺少break的问题 switch(date.weekday){ case 0: date.weekday = "天"; break; case 1: date.weekday = "一"; break; case 2: date.weekday = "二"; break; case 3: date.weekday = "三"; break; case 4: date.weekday = "四"; break; case 5: date.weekday = "五"; break; case 6: date.weekday = "六"; break; } return { date } } }).mount('.title') // 天气组件 const weather = createApp({ setup(){ let currentWeather = ref() let currentLocation = ref() let currentTemperature = ref() onMounted(() => { axios.get('http://api.weatherapi.com/v1/current.json?key=(key deleted)&q=Singapore&aqi=no&lang=zh') .then((response)=>{ currentWeather.value = response.data.current.condition.text currentLocation.value = response.data.location.name currentTemperature.value = response.data.current.temp_c }) .catch(error => console.log(error)) }) return { currentWeather, currentLocation, currentTemperature } }, }).mount('#weather')
2. 修改main.html引用外部脚本
移除原内联<script>标签,替换为外部脚本引用:
<body> <script src="../vue/dist/vue.global.js"></script> <script src="../axios.min.js"></script> <link rel="stylesheet" href="../styles/left.css"> <div class="title"> <div id="title">{{ date.year }}年{{ date.month }}月{{ date.day }}日</div> <div id="subTitle">星期{{ date.weekday }}</div> </div> <div id="weather"> {{currentLocation}} {{ currentWeather }} {{ currentTemperature }}℃ </div> <!-- 引用外部脚本 --> <script src="app.js"></script> </body>
3. 修正manifest.json的错误配置
V3的background字段不支持直接指定vue文件,需改为service_worker(不需要后台逻辑可直接删除该字段):
{ "name": "Handy New Tab", "description": "New Tab", "version": "1.0", "manifest_version": 3, "permissions": ["storage"], "action": { "default_icon": { "16": "/images/icon16.png", "48": "/images/icon48.png", "128": "/images/icon128.png" } }, "chrome_url_overrides": { "newtab": "/scripts/temp.html" }, "background": { "service_worker": "background.js" // 若无需后台,可删除此字段 } }
解决方案2:使用动态Nonce(适合必须用内联脚本的场景)
如果必须保留内联脚本,需生成随机nonce并同步到CSP策略:
1. 配置manifest.json的权限和CSP
添加webRequest、webRequestBlocking权限,并设置基础CSP:
{ // ...其他配置 "permissions": ["storage", "webRequest", "webRequestBlocking"], "host_permissions": ["chrome-extension://<你的扩展ID>/*"], "content_security_policy": { "extension_pages": "script-src 'self'; object-src 'self';" } }
2. 创建background.js动态修改CSP
通过后台service worker拦截页面请求,添加带随机nonce的CSP头:
chrome.webRequest.onHeadersReceived.addListener( (details) => { const nonce = crypto.randomUUID(); const newHeaders = details.responseHeaders.map(header => { if (header.name.toLowerCase() === 'content-security-policy') { header.value = `script-src 'self' 'nonce-${nonce}'; object-src 'self';`; } return header; }); return { responseHeaders: newHeaders }; }, { urls: ["chrome-extension://<你的扩展ID>/*"] }, ["blocking", "responseHeaders"] );
3. 在页面中动态插入带nonce的内联脚本
通过外部脚本生成带随机nonce的内联脚本标签:
// 比如在app.js中插入内联脚本 const nonce = crypto.randomUUID(); const inlineScript = document.createElement('script'); inlineScript.nonce = nonce; inlineScript.textContent = `// 你的内联代码`; document.body.appendChild(inlineScript);
为什么之前的方案无效?
- 拆分脚本失败:可能是文件路径错误,需确保脚本路径相对于manifest的位置正确
- CSP配置错误:Manifest V3的CSP需放在
content_security_policy的extension_pages字段下,且不允许直接使用unsafe-inline - 硬编码nonce无效:Chrome要求nonce是每次请求唯一的随机值,固定字符串会被CSP策略拒绝
内容的提问来源于stack exchange,提问作者Jayden Pu
相关产品推荐
相关产品推荐

