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

Ember应用如何按需加载Intl.Locale polyfill并保证优先执行

问题根因

你遇到的加载顺序问题本质是:所有包含异步逻辑(动态import()、async函数内await)的脚本,都不会阻塞后续普通script标签的解析执行。哪怕开启webpack顶层await特性,只要ember-auto-import的代码分割策略把动态导入的polyfill拆成独立chunk,polyfill chunk就会和后续vendor、app chunk并行加载,最终在iOS<14这类不支持Intl.Locale的环境下,ember-intl初始化时调用new Intl.Locale()的时机早于polyfill挂载完成,就会抛出你看到的构造函数不存在的错误。

可行解决方案

方案1:基于targets配置自动按需注入polyfill(最适配Ember生态,零顺序问题)

你已经在config/targets.js中配置了需要兼容的浏览器范围(包含iOS<14版本),可以直接通过babel插件自动判断目标环境是否需要polyfill,在编译期完成静态注入,完全规避异步加载顺序问题:

  • 安装依赖:
    npm install @formatjs/intl-locale babel-plugin-formatjs --save-dev
    
  • 修改ember-cli-build.js,添加babel插件配置:
    let app = new EmberApp(defaults, {
      babel: {
        plugins: [
          ['formatjs', {
            polyfill: {
              locale: true
            }
          }]
        ]
      },
      // 保留原有autoImport配置即可
    });
    
  • 该方案会在编译阶段根据你配置的浏览器兼容列表自动判断:对原生支持Intl.Locale的环境,完全不打入polyfill代码;对需要兼容的低版本环境,自动把polyfill代码注入到vendor.js最顶部,不存在异步加载的顺序问题。

方案2:内联同步判断+阻塞式脚本加载(完全可控,无构建依赖)

不要把polyfill的判断逻辑交给webpack打包,直接在index.html所有业务脚本之前写内联同步脚本,判断当前环境是否需要polyfill,需要时插入同步阻塞的script标签,保证polyfill加载完成前后续脚本不会执行:

  • 修改index.html的body开头部分:
    <body>
      <!-- 内联脚本同步执行,无异步逻辑 -->
      <script>
        // 手动实现polyfill判断逻辑,和官方判断逻辑一致,体积可忽略
        function shouldPolyfillIntlLocale() {
          try {
            new Intl.Locale('en-US');
            return false;
          } catch (e) {
            return true;
          }
        }
        if (shouldPolyfillIntlLocale()) {
          // 必须用document.write插入脚本,该方式插入的script是同步阻塞的
          // polyfill文件提前打包放到assets目录即可
          document.write('<script src="{{rootURL}}assets/intl-locale-polyfill.min.js"><\/script>');
        }
      </script>
      <!-- 原有脚本顺序保持不变 -->
      <auto-import-script entrypoint="polyfills"></auto-import-script>
      <script src="{{rootURL}}assets/vendor.js"></script>
      <auto-import-script entrypoint="app"></auto-import-script>
      <script src="{{rootURL}}assets/app.js"></script>
    </body>
    
  • 注意不要用document.createElement('script')+appendChild的方式插入脚本,这种方式插入的脚本默认是异步非阻塞的,无法保证加载顺序。

方案3:修正webpack顶层await配置(适合需要保留动态导入逻辑的场景)

你之前配置顶层await不生效,核心原因是webpack默认的代码分割策略把polyfill拆成了独立chunk,没有和polyfills入口做执行顺序绑定,修改ember-cli-build.js的webpack配置即可:

autoImport: {
  insertScriptsAt: 'auto-import-script',
  webpack: {
    target: 'web',
    experiments: {
      topLevelAwait: true
    },
    entry: {
      polyfills: './lib/polyfills.js',
    },
    optimization: {
      splitChunks: {
        chunks: 'async',
        cacheGroups: {
          // 强制把所有@formatjs下的polyfill代码打进polyfills入口,不拆分独立chunk
          polyfills: {
            test: /[\\/]node_modules[\\/]@formatjs[\\/]/,
            name: 'polyfills',
            chunks: 'all',
            enforce: true
          }
        }
      }
    }
  },
}
  • 同时修改lib/polyfills.js的代码,使用顶层await:
    import {shouldPolyfill} from '@formatjs/intl-locale/should-polyfill';
    if (shouldPolyfill()) {
      await import('@formatjs/intl-locale/polyfill');
      // 其他需要的intl相关polyfill都可以在这里按顺序await加载
      await import('@formatjs/intl-getcanonicallocales/polyfill');
    }
    
  • 该方案配置相对复杂,不同版本的ember-auto-import对顶层await的支持程度有差异,优先选择前两个方案。
避坑说明
  • 不要用异步立即执行函数(IIFE)、普通async函数调用的方式加载polyfill,这类写法只要存在await,后续同步脚本就会立刻开始执行,完全无法保证加载顺序。
  • ember-intl在应用初始化阶段就会同步调用new Intl.Locale(),执行时机非常早,polyfill必须在vendor.js执行前完成全局挂载,没有延迟加载的空间。

内容的提问来源于stack exchange,提问作者st-h

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 00:03:19