最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端監(jiān)控上報:Script Error問題的解決方法

 更新時間:2025年12月02日 08:39:21   作者:米花丶  
在微前端和多國業(yè)務(wù)的場景下,我們經(jīng)常會遇到HTML頁面域名和靜態(tài)資源域名不統(tǒng)一的情況,這種架構(gòu)雖然帶來了部署和CDN優(yōu)化的便利,但也引入了一個常見的問題,跨域Script Error,本文將詳細介紹這個問題的原因、影響,以及一套完整的解決方案,需要的朋友可以參考下

前言

在微前端和多國業(yè)務(wù)的場景下,我們經(jīng)常會遇到 HTML 頁面域名和靜態(tài)資源域名不統(tǒng)一的情況。這種架構(gòu)雖然帶來了部署和 CDN 優(yōu)化的便利,但也引入了一個常見的問題:跨域 Script Error。本文將詳細介紹這個問題的原因、影響,以及一套完整的解決方案。

問題背景

業(yè)務(wù)場景

我們的項目是一個多國業(yè)務(wù)的前端應(yīng)用,采用了以下架構(gòu):

  • HTML 頁面域名:每個地區(qū)/業(yè)務(wù)線使用不同主域名
    • 例如:countryA-web.example.com, countryB-web.example.com, ...
    • 靜態(tài)資源 CDN 域名:統(tǒng)一使用一個公共 CDN 地址
    • 例如:cdn.example.com

問題表現(xiàn)

在這種架構(gòu)下,我們遇到了以下問題:

監(jiān)控上報的 JS 錯誤全部顯示為 "Script Error"

  • 無法獲取詳細的錯誤堆棧信息
  • 無法定位具體的錯誤位置
  • SourceMap 無法正確映射

錯誤信息丟失

  • 錯誤消息被瀏覽器隱藏
  • 無法獲取錯誤發(fā)生的文件、行號、列號
  • 調(diào)試和問題排查變得困難

問題原因分析

1. 瀏覽器的同源策略

當(dāng)腳本從不同源加載時,瀏覽器會應(yīng)用同源策略(Same-Origin Policy)的安全機制:

  • 如果腳本發(fā)生錯誤,且腳本的源與頁面不同,瀏覽器會隱藏錯誤的詳細信息
  • 只返回通用的 "Script Error" 消息
  • 這是為了防止惡意網(wǎng)站通過錯誤信息獲取敏感數(shù)據(jù)

2. 缺少 CORS 配置

要獲取跨域腳本的詳細錯誤信息,需要滿足兩個條件:

  1. 腳本標簽添加 crossorigin 屬性 
  2. 服務(wù)器返回正確的 CORS 響應(yīng)頭

當(dāng)你在 <script><link> 標簽上添加 crossorigin="anonymous",實際上是在告訴瀏覽器:“允許以跨源模式拉取這個資源,并且無須附帶任何憑證(cookie、鑒權(quán)頭等) 。”
如果該標簽沒有 crossorigin 屬性,瀏覽器以默認的“no-cors”模式加載資源,只要腳本跨域,一旦腳本發(fā)生錯誤,錯誤信息就會被隱藏,只能看到 Script Error,同時 sourceMap 也無法正確還原堆棧。

只有當(dāng) crossorigin 屬性為 anonymous 并且服務(wù)器響應(yīng)了正確的 CORS 頭,瀏覽器才會呈現(xiàn)完整的報錯信息和堆棧文件行號,對異常監(jiān)控和調(diào)試至關(guān)重要。

與此同時,服務(wù)器/CDN 響應(yīng)也必須包含正確的 CORS 相關(guān)頭部,如 Access-Control-Allow-Origin。
這是因為瀏覽器在跨域加載資源時,會先根據(jù)標簽屬性判斷是否允許訪問細節(jié),再檢查服務(wù)器是不是“回應(yīng)放行”了該跨域請求。如果服務(wù)器未設(shè)置這些頭部,哪怕你加了 crossorigin 屬性,瀏覽器也會隱藏資源細節(jié)和所有報錯內(nèi)容,僅顯示 Script Error

如果缺少其中任何一個,瀏覽器都會隱藏錯誤詳情。

3. Webpack/Rspack 動態(tài)加載機制

現(xiàn)代前端構(gòu)建工具(Webpack、Rspack)在實現(xiàn)代碼分割和懶加載時,會通過 document.createElement('script') 動態(tài)創(chuàng)建 script 標簽來加載 chunk。如果這些動態(tài)創(chuàng)建的標簽沒有 crossorigin 屬性,同樣會導(dǎo)致 Script Error。

解決方案

我們采用了一套三層防護的解決方案:

方案架構(gòu)圖

解決方案架構(gòu)

1. HTML 模板層

手動添加 crossorigin 屬性

2. 構(gòu)建時處理層

Webpack 插件自動添加

3. 運行時攔截層

攔截 createElement 全局處理

HTML層:模板引用資源手動修改

對于 HTML 模板中直接引用的靜態(tài)資源,我們手動添加 crossorigin="anonymous" 屬性:

<!-- 所有跨域的 script 標簽 -->
<script
  src="https://cdn.example.com/static/polyfill.min.js"
  defer
  crossorigin="anonymous"
></script>

<!-- 所有跨域的 link 標簽(CSS) -->
<link
  rel="stylesheet"
   rel="external nofollow" 
  crossorigin="anonymous"
/>

打包工具層:Webpack/Rspack 插件自動處理(構(gòu)建時)

對于構(gòu)建工具自動插入的腳本和樣式,我們創(chuàng)建了一個自定義插件:

class CrossOriginAssetsPlugin {
  apply(compiler) {
    const pluginName = 'CrossOriginAssetsPlugin';
    compiler.hooks.compilation.tap(pluginName, (compilation) => {
      const HtmlWebpackPlugin = require('html-webpack-plugin');
      if (HtmlWebpackPlugin && HtmlWebpackPlugin.getHooks) {
        HtmlWebpackPlugin.getHooks(compilation).alterAssetTags.tap(
          pluginName,
          data => {
            // 處理 script
            data.assetTags.scripts.forEach(tag => {
              if (isCrossOrigin(tag.attributes?.src)) {
                tag.attributes.crossorigin = 'anonymous';
              }
            });
            // 處理 link
            data.assetTags.styles.forEach(tag => {
              if (isCrossOrigin(tag.attributes?.href)) {
                tag.attributes.crossorigin = 'anonymous';
              }
            });
            return data;
          }
        );
      }
    });
  }
}

function isCrossOrigin(url) {
  // 補充判斷邏輯:絕對路徑跨域、相對路徑同源
  return url && /^https?:///.test(url);
}

異步加載層:運行時全局攔截(動態(tài)加載)

在業(yè)務(wù)開發(fā)中我們會遇到許多異步動態(tài)加載的腳本文件,通過攔截 document.createElement,為所有動態(tài) script/link 節(jié)點設(shè)置跨域?qū)傩?,無死角覆蓋 chunk、第三方庫等異步加載場景。

function isCrossOriginUrl(url: string | null | undefined): boolean {
  return !!url && /^https?:///.test(url);
}

export function setScriptCrossOrigin(script: HTMLScriptElement) {
  const src = script.src || script.getAttribute('src');
  if (isCrossOriginUrl(src)) script.crossOrigin = 'anonymous';
}

export function setLinkCrossOrigin(link: HTMLLinkElement) {
  const href = link.href || link.getAttribute('href');
  if (isCrossOriginUrl(href)) link.crossOrigin = 'anonymous';
}

export function initCrossOriginScriptHandler() {
  const originCreateElement = document.createElement.bind(document);
  document.createElement = function (tagName: string, options?: ElementCreationOptions) {
    const el = originCreateElement(tagName, options);
    if (tagName.toLowerCase() === 'script') {
      // 當(dāng) src 插入時才設(shè)置
      const observer = new MutationObserver(mutations => {
        mutations.forEach(m => {
          if (m.type === 'attributes' && m.attributeName === 'src') {
            setScriptCrossOrigin(el as HTMLScriptElement);
            observer.disconnect();
          }
        });
      });
      observer.observe(el, { attributes: true, attributeFilter: ['src'] });
    }
    if (tagName.toLowerCase() === 'link') {
      const observer = new MutationObserver(mutations => {
        mutations.forEach(m => {
          if (m.type === 'attributes' && m.attributeName === 'href') {
            setLinkCrossOrigin(el as HTMLLinkElement);
            observer.disconnect();
          }
        });
      });
      observer.observe(el, { attributes: true, attributeFilter: ['href'] });
    }
    return el;
  };
}

在應(yīng)用啟動處調(diào)用初始化:

為什么這個方案可以覆蓋所有場景?

Webpack/Rspack 在運行時加載 chunk 時,會生成類似這樣的代碼:

// Webpack 生成的 chunk 加載代碼
function loadChunk(chunkId) {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');  // ← 關(guān)鍵
    script.src = chunkUrl;
    script.onload = resolve;
    script.onerror = reject;
    document.head.appendChild(script);
  });
}

通過攔截 document.createElement,我們可以捕獲所有通過此方式創(chuàng)建的 script 標簽,包括:

  • React.lazy 懶加載的組件
  • 動態(tài) import() 加載的模塊
  • Webpack chunk 動態(tài)加載
  • 第三方庫的動態(tài)加載
  • 手動通過 loadScript() 加載的腳本

服務(wù)器端配置

后端需確保 CDN/stastic 資源的 HTTP 響應(yīng)頭允許跨源:

Nginx 配置樣例(脫敏)

location /static/ {
  add_header 'Access-Control-Allow-Origin' '*' always;
  add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
  add_header 'Access-Control-Allow-Headers' 'Range' always;
  add_header 'Access-Control-Expose-Headers' 'Content-Length, Content-Range' always;
  if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
    add_header 'Access-Control-Max-Age' 1728000;
    add_header 'Content-Type' 'text/plain; charset=utf-8';
    add_header 'Content-Length' 0;
    return 204;
  }
}

總結(jié)

通過這套三層防護的解決方案,我們成功解決了多域名架構(gòu)下的跨域 Script Error 問題:

  1. HTML層:手動為靜態(tài)資源添加 crossorigin 屬性
  2. 打包工具層:Webpack 插件自動處理構(gòu)建時插入的資源
  3. 異步加載層:全局攔截 document.createElement,處理所有動態(tài)加載

以上就是前端監(jiān)控上報:Script Error問題的解決方法的詳細內(nèi)容,更多關(guān)于前端監(jiān)控上報:Script Error的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

化德县| 鱼台县| 黄龙县| 镇赉县| 体育| 石台县| 县级市| 开远市| 波密县| 万山特区| 贺兰县| 黄梅县| 侯马市| 康乐县| 舞钢市| 永善县| 建湖县| 岢岚县| 宝丰县| 六盘水市| 平凉市| 定兴县| 巴彦县| 平乐县| 聂拉木县| 元朗区| 家居| 元朗区| 广河县| 丽江市| 化隆| 河北区| 乾安县| 邯郸市| 通化市| 崇文区| 裕民县| 凤冈县| 南阳市| 繁峙县| 广汉市|