仅在需要时添加 Intl.Locale 填充(如何使用异步函数阻止脚本标签)

问题描述 投票:0回答:2

我正在尝试将 Intl polyfill 添加到 ember 应用程序中,但遇到了一个问题,即我需要添加一个在评估其他脚本标签之前执行异步函数的脚本标签。

在 ember 中,我可以向

<script>
添加一个新的
index.html
标签,该标签放置在 emberjs 标签之前:

<body>
  <script src="assets/polyfills.js"></script>   
  <script src="assets/vendor.js"></script> <-- this throws an exception if polyfill is not loaded for iOS < 14
</body>

assets/polyfills.js
看起来像这样时,一切正常:

import '@formatjs/intl-locale/polyfill';

然而,问题是所有设备都会加载 Polyfill——无论是否需要。但根据文档,有办法检查是否确实需要 polyfill https://formatjs.io/docs/polyfills/intl-locale/:

import {shouldPolyfill} from '@formatjs/intl-locale/should-polyfill'
async function polyfill() {
  // This platform already supports Intl.Locale
  if (shouldPolyfill()) {
    await import('@formatjs/intl-locale/polyfill')
  }
}

现在的问题是,我正在处理一个异步函数,并且我找不到在执行任何其他 js 代码之前加载 polyfill 的方法。

我尝试修改

polyfills.js
以使用顶级等待并启用实验性Webpack功能
topLevelAwait: true
,但后续代码在加载polyfill之前执行:

await import('@formatjs/intl-getcanonicallocales/polyfill');

我也尝试将它包装在一个函数中,但这也没有改变任何东西:

async function load() {
  await import('@formatjs/intl-locale/polyfill');
};
await load();

我也尝试过类似的方法,效果完全一样:

(async () => {
  await import('@formatjs/intl-locale/polyfill');
})();

我需要的东西几乎是这样的:

if (shouldPolyfill) {
  import '@formatjs/intl-locale/polyfill';
}

但是,这是无效的并会导致此错误:

An import declaration can only be used at the top level of a module.

我该如何解决这个问题?

编辑(添加更多余烬细节)

该错误出现在 ember

chunk.*.js
文件之一中,因此我认为这是由自动导入加载的依赖项引起的。看内容的话,好像是
ember-intl

我配置了自动导入以在其他依赖项之前添加polyfill:

ember-cli-build:

autoImport: {
      insertScriptsAt: 'auto-import-script',
      webpack: {
        target: 'web',
        entry: {
          polyfills: './lib/polyfills.js',
        },

index.html:

<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>

targets.js

'use strict';

const browsers = [
  'last 2 Chrome versions',
  'last 2 Firefox versions',
  'last 4 Safari versions',
  'last 1 Edge versions',
  'last 2 ChromeAndroid versions',
  'last 4 iOS versions',
];

module.exports = {
  browsers,
  node: '12'
};

stacktrace

TypeError: undefined is not a constructor (evaluating 'new Intl.Locale(a[0])')
1
File "https://static.app.com/app/assets/chunk.367.65428fe8e29cd2560eec.js", line 1404 col 34 in resolveLocale
2
File "https://static.app.com/app/assets/chunk.367.65428fe8e29cd2560eec.js", line 1396 col 296 in c
3
File "addon-tree-output/ember-intl/-private/formatters/format-message.js", line 61 col 1 in [anonymous]
return new _intlMessageformat.default(ast, locales, formatConfig, {
4
File "https://static.app.com/app/assets/chunk.367.65428fe8e29cd2560eec.js", line 873 col 30 in e
5
File "[native code]", line (unknown) in e
6
File "addon-tree-output/ember-intl/-private/formatters/format-message.js", line 84 col 1 in format
const formatterInstance = this.createNativeFormatter(ast, locale, this.readFormatConfig());
7
File "@ember/-internals/glimmer/index.js", line 2808 col 24 in getValue
let ret = instance.compute(positional, named);
8
File "@glimmer/reference.js", line 121 col 35 in [anonymous]
lastValue = ref.lastValue = compute();
9
File "@glimmer/validator.js", line 677 col 5 in track
callback();
10
File "@glimmer/reference.js", line 120 col 21 in m
tag = ref.tag = track(() => {
11
File "@glimmer/runtime.js", line 3777 col 31 in [anonymous]
vm.stack.push(toContentType(valueForRef(reference)));
12
File "@glimmer/runtime.js", line 1205 col 17 in evaluate
operation.evaluate(vm, opcode);
13
File "@glimmer/runtime.js", line 4882 col 20 in evaluateSyscall
APPEND_OPCODES.evaluate(vm, opcode, opcode.type);
14
File "@glimmer/runtime.js", line 4838 col 12 in evaluateInner
this.evaluateSyscall(opcode, vm);
15
File "@glimmer/runtime.js", line 4830 col 12 in evaluateOuter
this.evaluateInner(opcode, vm);
16
File "@glimmer/runtime.js", line 5790 col 22 in next
this[INNER_VM].evaluateOuter(opcode, this);
17
File "@glimmer/runtime.js", line 5774 col 21 in _execute
result = this.next();
18
File "@ember/-internals/glimmer/index.js", line 5194 col 43 in render
let result = this.result = iterator.sync(); // override .render function after initial render
19
File "@ember/-internals/glimmer/index.js", line 5513 col 16 in [anonymous]
root.render();
20
File "@glimmer/runtime.js", line 4725 col 7 in Nt
cb();
21
File "@ember/-internals/glimmer/index.js", line 5492 col 7 in _renderRoots
inTransaction(runtime.env, () => {
22
File "@ember/-internals/glimmer/index.js", line 5545 col 12 in _renderRootsTransaction
this._renderRoots();
23
File "@ember/-internals/glimmer/index.js", line 5479 col 10 in _renderRoot
this._renderRootsTransaction();
24
File "@ember/-internals/glimmer/index.js", line 5385 col 10 in _appendDefinition
this._renderRoot(rootState);
25
File "@ember/-internals/glimmer/index.js", line 5367 col 10 in appendOutletView
this._appendDefinition(view, curry(0
26
File "backburner.js", line 275 col 24 in invokeWithOnError
method.apply(target, args);
27
File "backburner.js", line 182 col 21 in flush
invoke(target, method, args, onError, errorRecordedForStack);
28
File "backburner.js", line 341 col 27 in flush
if (queue.flush(false /* async */) === 1 /* Pause */) {
29
File "backburner.js", line 784 col 38 in _end
result = currentInstance.flush(fromAutorun);
30
File "backburner.js", line 582 col 14 in end
this._end(false);
31
File "backburner.js", line 827 col 22 in _run
this.end();
32
File "@ember/application/lib/application.js", line 430 col 9 in e
run(this, 'domReady');
javascript ember.js polyfills intl
2个回答
0
投票

因为你正在使用 webpack。您可以使用 file-loader 获取块上的 URL 并根据需要通过脚本标记同步加载它:

import {shouldPolyfill} from '@formatjs/intl-locale/should-polyfill'
import polyfillUrl from "!file-loader!@formatjs/intl-locale/polyfill";
async function polyfill() {
  // This platform already supports Intl.Locale
  if (shouldPolyfill()) {
      document.write(`<script src="${polyfillUrl}"><\/script>`);
  }
}

-1
投票

警告:此解决方案不再有效,并会带来意想不到的后果,例如此处概述的: https://www.theregister.com/2024/06/25/polyfillio_china_crisis/

我认为对于跨浏览器功能,使用 CDN 是最好的方法,而不是自己安装和导入 polyfill 库。 Polyfill.io 的 CDN 使用

User-Agent
HTTP 标头自动检查请求的 polyfill 是否必要,并有条件地发送 polyfill 脚本。

HTML

<script>
标签的正常行为是在解析 HTML 文档时一旦找到脚本就加载并执行,并且每个脚本都是按顺序加载和执行的,因此将 polyfill 脚本添加到其他脚本标签的顶部有道理并且应该按预期工作:

<head>
  <script src="https://polyfill.io/v3/polyfill.min.js?features=Intl.Locale"></script>
  <script src="assets/vendor.js"></script>
</head>
© www.soinside.com 2019 - 2024. All rights reserved.