You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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}} &nbsp; {{ currentWeather }} &nbsp; {{ 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);

为什么之前的方案无效?

  1. 拆分脚本失败:可能是文件路径错误,需确保脚本路径相对于manifest的位置正确
  2. CSP配置错误:Manifest V3的CSP需放在content_security_policy的extension_pages字段下,且不允许直接使用unsafe-inline
  3. 硬编码nonce无效:Chrome要求nonce是每次请求唯一的随机值,固定字符串会被CSP策略拒绝

内容的提问来源于stack exchange,提问作者Jayden Pu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 04:45:43