webpack v2 中全局的 promise polyfill 是如何实现的?

How is the global promise polyfill implemented in webpack v2?

我想更好地理解 webpack 中 promise 实现方式的区别。通常,幸福的无知足以让我主要开发应用程序,但我肯定对如何正确开发 plugin/tool/lib.

有点困惑

在创建应用程序时,以下两种方法从未引起任何问题;我想主要是因为没关系

webpack.config.js - 使用 babel-polyfill 作为入口点

module.exports = {
  entry: {
    foo: [
      'core-js/fn/promise',          <-- here
      './js/index.js'
    ]
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader'
      }
    ]
  }
}

Q: In this approach, since it's a polyfill it modifies the global Promise?

webpack 配置 - 使用 webpacks 提供的 shimming 插件

module.exports = {
  entry: './js/index.js',

  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader'
      }
    ]
  },

  plugins: [
    new webpack.ProvidePlugin({
      Promise: 'es6-promise'          <-- here
    })
  ]
};

Q: Does this mean that the Promise is a module only specific to the webpack bundling process? Does the transpiled ES5 code have a local copy or es6-promise? Does it patch the global Promise?


关于创建一个使用 babel 进行转译的 jquery plugin/tool/lib...

webpack.config.js - 使用 babel-plugin-transform-runtime

module.exports = {
  entry: {
    foo: [
      './js/start.js'
    ]
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader'
      }
    ]
  }
}

.babelrc

{
  "presets": [ "es2015" ],
  "plugins": ["transform-runtime"]     <--here
}

start.js

require('babel-runtime/core-js/promise').default = require('es6-promise');  <--here
require('plugin');

Q: This aliases the es6-promise to the babel-runtime promise and is not global but only local to the tool?

webpack 条目中的 Polyfill

entry: ['core-js/fn/promise', './index.js']

这与在入口点顶部导入它的效果相同。

In this approach, since it's a polyfill it modifies the global Promise?

是的,这个 polyfill 改变了全局 Promise。称它为 polyfill 通常意味着它修补了全局内置插件,尽管这并没有严格遵守。如果他们不改变现有的 API 而只是提供功能,他们有时被称为 Ponyfills.

Webpack 使用 ProvidePlugin

new webpack.ProvidePlugin({
  Promise: 'es6-promise'
})

ProvidePlugin will import the configured module at the beginning of the module that uses it when the corresponding free variable is found. A free variable 是一个尚未在当前范围内声明的标识符。全局变量是所有局部范围内的自由变量。

每当遇到空闲 Promise 时,webpack 都会将以下内容添加到模块的开头:

var Promise = require('es6-promise');

Does this mean that the Promise is a module only specific to the webpack bundling process?

这是正确的,因为 ProvidePlugin 是特定于 webpack 的,任何其他工具都不太可能遵守任何 webpack 设置。

Does the transpiled ES5 code have a local copy or es6-promise?

与任何其他模块一样,它被 webpack 包含一次,所有导入都引用该模块。

Does it patch the global Promise?

它只会修改全局 Promise 如果导入的模块明确地这样做。您正在使用的 es6-promise 默认情况下不会修补全局,如 Auto-polyfill.

所示

Babel 变换运行时间

{
  "plugins": ["transform-runtime"]
}

babel-plugin-transform-runtime 使用 core-js 来提供缺少的功能,例如 Promise。你会记得,我说过 core-js 修改全局 Promise。对于这种情况,情况并非如此,因为 babel 使用不污染全局命名空间的版本,如 core-js README 中所述,它位于 core-js/library 中。例如:

const Promise = require('core-js/library/fn/promise');

Babel 将导入 core-js Promise 并将 Promise 替换为导入的变量。另请参阅 babel-plugin-transform-runtime - core-js aliasing 中的示例。这与 webpack 的 ProvidePlugin 本质上是一样的,除了 babel 不捆绑模块,所以它只是添加导入。

This aliases the es6-promise to the babel-runtime promise and is not global but only local to the tool?

它不是全局的,因为它只是一个模块。 Babel 获取你的 JavaScript 并输出一些其他的 JavaScript ,其中配置的功能被转换为 ES5。您将 运行 或捆绑生成的 JavaScript 并且它实际上与您首先编写 ES5 相同。

require('babel-runtime/core-js/promise').default = require('es6-promise');

修改导出后,模块将使用 es6-promise。但是覆盖导出不是一个好主意,特别是因为 ES 模块的导入在规范中是不可变的。 Babel 目前在这方面不符合规范。有关详细信息,请参阅 Making transpiled ES modules more spec-compliant


你应该使用哪一个?

这取决于你在做什么。除了他们是否改变全局变量的不同之外,你可以选择你喜欢的任何一个。例如使用 babel 的 transform 运行time 允许你将它与任何使用 babel 的工具一起使用,而不仅仅是 webpack。

图书馆

None.

将 polyfill 留给应用程序开发人员。但是你可能会提到它依赖于某个特性,当用户想要在不支持该特性的环境中使用该库时,他们必须对其进行 polyfill。假设 Promises 得到广泛支持也是相当合理的,如果应用程序针对较旧的环境,它们很可能已经对其进行了 polyfill。请记住,这并不意味着您不应该转换新功能/语法。这专门用于 PromiseString.prototype.trimLeft.

等新功能

对于工具

这也取决于您对工具的定义。假设工具是开发人员使用的一款软件(例如 webpack、eslint 等)。在那种情况下,它与任何应用程序完全相同,归根结底,它只是另一个应用程序,但仅针对开发人员。具体说到命令行工具,您应该决定要支持的最低 Node 版本并包括为此所需的任何内容,您可以在 engines field.[=54= 中的 package.json 中指定]

对于插件

插件是一个非常宽泛的术语,可以是介于库和应用程序之间的任何东西。例如,webpack 插件或加载器应该按原样工作,而 jQuery 插件将成为网络应用程序的一部分,您应该将其视为库(它们可能应该称为库而不是插件)。通常,无论您要扩展什么,您都希望符合准则。看看它,看看他们的目标是什么。例如 webpack 目前支持 Node verions >=4.3.0,所以你的插件也应该支持。