Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

了解 Babel 6 & 7 生态 #25

Open
creeperyang opened this issue Feb 15, 2017 · 29 comments
Open

了解 Babel 6 & 7 生态 #25

creeperyang opened this issue Feb 15, 2017 · 29 comments

Comments

@creeperyang
Copy link
Owner

creeperyang commented Feb 15, 2017

了解Babel 6生态

现在写一个babel的简介好像已经不太必要了(太晚了😄 )。但大多数情况下,会配置babel来编译代码,不代表我们清楚babel的概念,而且Babel 6相关的多个babel-xx包还是容易让人混淆的。所以这里还是希望帮助理清整个Babel 6生态。

参考:

Babel 6的核心特性(相比5的巨大变化)

我刚开始用babel的时候,版本是5,一个月后Babel 6发布——变化简直天翻地覆。

相比前一版本,Babel 6最大的变化是更模块化,各种内置库都被分散到独立的模块;其次,让所有插件可选,这意味着Babel默认不会编译ES2015代码,并且所有的transformer完全独立;同时,为了减少配置的复杂性,引入了preset;最后,提升了性能。

下面列出一些Babel 6的核心模块/变化:

  1. babel package被弃用。

    我们可以看babel@6.x的源码,两个提示很明显:

    1. babel/index.js::node API 被转移到 babel-core
    2. babel/src/cli.js: cli 被转移到babel-cli
  2. babel-core是babel的core compiler,主要用来对你的源码跑一系列变换(transform)。但默认情况下,不会应用任何变换——你必须自己安装和注册这些变换。

  3. babel-cli是babel的command line,有babel/babel-external-helpers/babel-node3个命令。babel-doctor已被移除,见Moving babel-doctor to a separate package/removing babel/babel#4678

    1. babel即用于编译代码。
    2. babel-external-helpers用于生成一段js代码(里面是一些helper函数)。这些helper如果被用到,一般被置于生成代码顶部(公用),所以生成的代码不会有内连这些helper好几遍。但是如果你有多个文件的话,你可能又要重复这些helper好几遍了。所以你可以生成这样一份代码,然后在每个文件中直接引入(node通过require,browser通过<script>)。详情见external-helpers
    3. babel-node是方便开发的node二进制(非生产使用),内置了babel-polyfill,并用babel-register来编译被require的模块。
  4. babel-registerrequire hook,替换了node的require

    The require hook will bind itself to node's require and automatically compile files on the fly.

    1. 如果模块是内置模块或者是node_modules内的模块,则使用node的require
    2. 否则使用babel的require,自动编译模块。

Babel 6plugins

详情见https://babeljs.io/docs/plugins/

这里不多说,只简单说两点:

  1. Babel引入了preset的概念,preset其实是一组plugins
  2. 我们常用的babel-preset-es2015包括了完整的ES2015特性,引入它即可编译ES2015代码到ES5

用transform还是polyfill实现?

babel-core仅仅聚焦于code transform,所以不是什么事都可以用babel来转换的。

比如,检索上面的plugins列表,你会发现没有一个plugin用来转换Promise;事实上,如果环境不支持Promise,你应该自己引入相应polyfill。

那么什么时候应该用tranform,什么时候该用polyfill呢?如果一个新特性你可以用ES5实现,那么,你应该用polyfill,比如Array.from。否则,你应该用transform,比如箭头函数。

babel-polyfill vs babel-runtime

这可能是babel中最让人误解的一组概念:当你需要支持ES2015的所有特性时,究竟用babel-polyfill 还是 babel-runtime

babel-polyfillbabel-runtime是达成同一种功能(模拟ES2015环境,包括global keywordsprototype methods,都基于core-js提供的一组polyfill和一个generator runtime)的两种方式:

  1. babel-polyfill通过向全局对象和内置对象的prototype上添加方法来达成目的。这意味着你一旦引入babel-polyfill,像MapArray.prototype.find这些就已经存在了——全局空间被污染。

  2. babel-runtime不会污染全局空间和内置对象原型。事实上babel-runtime是一个模块,你可以把它作为依赖来达成ES2015的支持。

    比如当前环境不支持Promise,你可以通过require(‘babel-runtime/core-js/promise’)来获取Promise。这很有用但不方便。幸运的是,babel-runtime并不是设计来直接使用的——它是和babel-plugin-transform-runtime一起使用的。babel-plugin-transform-runtime会自动重写你使用Promise的代码,转换为使用babel-runtime导出(export)的Promise-like对象。

    注意: 所以plugin-transform-runtime一般用于开发(devDependencies),而runtime自身用于部署的代码(dependencies),两者配合来一起工作。

那么我们什么时候用babel-polyfill,什么时候用babel-runtime

  1. babel-polyfill会污染全局空间,并可能导致不同版本间的冲突,而babel-runtime不会。从这点看应该用babel-runtime
  2. 但记住,babel-runtime有个缺点,它不模拟实例方法,即内置对象原型上的方法,所以类似Array.prototype.find,你通过babel-runtime是无法使用的。
  3. 最后,请不要一次引入全部的polyfills(如require('babel-polyfill')),这会导致代码量很大。请按需引用最好。
@mengxingshike2012
Copy link

额,好像现在是习惯性的引入babel-polyfill; 其实关于babel-core这个库,如果使用webpack的话,自然会有babel-loader,但是现在loader又不依赖babel-core,是不是一般都不用加入项目的依赖了

@creeperyang
Copy link
Owner Author

@mengxingshike2012 babel-loaderpeerDependencies

"peerDependencies": {
    "babel-core": "^6.0.0",
    "webpack": "1 || 2 || ^2.1.0-beta || ^2.2.0-rc"
  },

@lgh06
Copy link

lgh06 commented Mar 7, 2017

我只用了这三个 babel-preset-env + babel-polyfill + whatwg-fetch 。
babel-preset-env很智能的。各种transform傻瓜化配置了。useBuiltIns选项对polyfill也做了筛选,不用引入全部的polyfill

@creeperyang
Copy link
Owner Author

@lgh06 才知道babel-preset-env 👍

是个不错的选择,不过看起来它是依赖指定node版本或者browser版本来决定引入哪些polyfill的;如果不用内置对象原型上的方法,babel-runtime可能更好一点。

@lgh06
Copy link

lgh06 commented Mar 7, 2017

太喜欢Array.from、forEach、Promise,没办法不污染原型了…… 新浏览器里面都内置了,也不能算污染……

@For-me
Copy link

For-me commented Jul 19, 2017

有个小问题。比如你引用了一些第三方库,直接require是使用的编译后的文件。但是如果第三方库作者使用了es6并且没有使用相应的polyfill来转换类似于for(item of list) 这种语法转换的时候会变成Symbol。造成某一些环境下对其不支持的错误。这应该是开发者本人来引入polyfill解决还是第三方作者在转换的时候就应该引入polyfill转换?还是说是babel本身的一些问题

@creeperyang
Copy link
Owner Author

@For-me 我觉得是库作者的责任,他应该提供转换后的可用库文件,或者明确告知使用者必须自己转换。

@xwenliang
Copy link

为什么vue-cli生成的项目里,同时使用了transform-runtimebabel-preset-env

.babelrc:

{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}

@creeperyang
Copy link
Owner Author

@xwenliang 很好的问题。

我仔细查阅了文档,答案是 babel-preset-env@1.x 没法很好地消除未使用的polyfill(就是说有未使用的代码被引入进来了)。如果希望避免这一点,那么就会禁用useBuiltIns: true,而用更好的 transform-runtime 代替。

babel-preset-env@2.x 中可以用 useBuiltIns: 'usage' 达到按需引入的目的。

详情可见:

babel/babel-preset-env#84

babel/babel-preset-env#241


可以看到 vuejs-templates/webpack/ 引入的是 1.3babel-preset-env

@xwenliang
Copy link

@creeperyang 专业👍

@qingmingsang
Copy link

react不需要同时 用transform-runtime和babel-preset-env ,感觉和vue有关系,或者react里有的工作被babel-preset-react做了

@lawpachi
Copy link

请教作者vue-cli 使用的babel-preset-env 是@1.x ,有不能很好地消除未使用的polyfill的缺点,那么为什么不直接使用transform-runtime而是即使用env又使用runtime,这样重复不会造成打包体积变大吗。并且我没发现vue-cli中有useBuiltIns: true。

@azumia
Copy link

azumia commented May 4, 2018

@lawpachi 这个可能是应为transform-runtime对实例方法的支持不是很好,所以只只能结合polyfill来进行转码

@yimijc
Copy link

yimijc commented May 6, 2018 via email

@creeperyang
Copy link
Owner Author

creeperyang commented Jun 20, 2018

Babel 7 及新用法

Babel 7 快正式发布了(2018-06-20是@babel/core@^7.0.0-beta.51),相比6有非常多的更新,这里例举一些主要的,详情可以阅读官方博文。

  • deprecated Yearly Presets (如 babel-preset-es20xx),preset-env 代替
  • 最新更新的 TC39 Proposals
  • 重命名,@babel scope (防止被占用)
  • ...

presets 详解

babel 的工作流是

输入字符串 -> @babel/parser parser -> AST -> transformer[s] -> AST -> @babel/generator -> 输出字符串

其中在 transformer[s] 阶段,就是使用plugins来转换代码的阶段。不同的plugin转换特定的代码,而preset是一组完成特定转换的plugins,比如babel-preset-es2017即包含syntax-trailing-function-commas | transform-async-to-generator两个plugin,用于支持ES2017的新特性。

plugin 分为 transform plugin(实际转换代码) 和 syntax-plugin(语法支持,即parse阶段),一般transform plugin包含对应的syntax plugin。

以前常用的yearly presets(preset-es2015 | preset-es2016 | preset-es2017)在 Babel 7 中已经不推荐使用了,建议用 preset-env 代替。我们先跳过 preset-env (下一节重点讲),来讲一下常用的preset-state-x presets。

对JS语言的更新需要一个过程(即0-4的stage,过程如下),用于支持这些尚未被加入到标准(如ES5/2016)的更新的presets即preset-state-x

The TC39 categorizes proposals into the following stages:

  • Stage 0 - Strawman: just an idea, possible Babel plugin.
  • Stage 1 - Proposal: this is worth working on.
  • Stage 2 - Draft: initial spec.
  • Stage 3 - Candidate: complete spec and initial browser implementations.
  • Stage 4 - Finished: will be added to the next yearly release.

其中,preset-stage-3 用于支持 stage 3/4,其余一一对应。另外需要注意,preset-stage-0~3是一个包含关系,即preset-stage-0包含preset-stage-1所有的plugins和 stage 0 对应的plugins。

preset-env 详解

preset-env 是 JS 中的 autoprefixer,可以根据具体环境(浏览器/node版本)来应用不同的plugins,用于替代原来的babel-preset-es20xx/latestbabel-preset-es2015/babel-preset-es2016/babel-preset-es2017)。

严格来说,preset-env支持的plugins超过babel-preset-latest(2015-2017),包括部分stage-3支持的plugins及其它plugin——多出的plugins可以某种程度理解为preset-es2018(不会出现了),并且随着落地标准越来越多(es2018/es2019/...),包括的plugins也会越来越多。

多出的plugins :
@babel/plugin-proposal-async-generator-functions
@babel/plugin-proposal-object-rest-spread
@babel/plugin-proposal-optional-catch-binding
@babel/plugin-proposal-unicode-property-regex
@babel/plugin-syntax-async-generators
@babel/plugin-syntax-object-rest-spread
@babel/plugin-syntax-optional-catch-binding
@babel/plugin-transform-dotall-regex
@babel/plugin-transform-new-target

下面来讲讲preset-env的用法:

{
  "presets": [
    [
      "env",
      {
        "targets": { // 目标环境
          "browsers": [ // 浏览器
            "last 2 versions",
            "ie >= 10"
          ],
          "node": "current" // node
        },
        "modules": true,  // 是否转译module syntax,默认是 commonjs
        "debug": true, // 是否输出启用的plugins列表
        "spec": false, // 是否允许more spec compliant,但可能转译出的代码更慢
        "loose": false, // 是否允许生成更简单es5的代码,但可能不那么完全符合ES6语义
        "useBuiltIns": false, // 怎么运用 polyfill
        "include": [], // 总是启用的 plugins
        "exclude": [],  // 强制不启用的 plugins
        "forceAllTransforms": false, // 强制使用所有的plugins,用于只能支持ES5的uglify可以正确压缩代码
      }
    ]
  ],
}

preset-env的配置如上,大部分都很简单,重点讲一下useBuiltIns

useBuiltIns用于指定怎么处理polyfill(通过@babel/polyfill),可选的值为"usage" | "entry" | false,默认是false

  • useBuiltIns: 'usage':当每个文件里用到(需要polyfill的特性)时,在文件中添加特定的import语句。这可以保证每个polyfill的特性仅load一次。

    /// input
    var a = new Promise(); // a.js
    var b = new Map(); // b.js
    /// output
    // a.js
    import "core-js/modules/es6.promise";
    var a = new Promise();
    // b.js
    import "core-js/modules/es6.map";
    var b = new Map();
  • useBuiltIns: 'entry':替换import "@babel/polyfill" / require("@babel/polyfill")语句为独立的(根据环境)需要引入的polyfill特性的import语句。

    // input
    import "@babel/polyfill";
    // output
    import "core-js/modules/es7.string.pad-start";
    import "core-js/modules/es7.string.pad-end";

    需要注意,在整个项目中,"@babel/polyfill"只能require一次,否则报错。建议用独立的entry文件引入。

  • useBuiltIns: false,禁止使用上面两种转换,可以理解为不使用polyfill,此时需要使用者自己手动处理。

ReferenceError: regeneratorRuntime is not defined 错误:

需要注意,当你使用async/await并被preset-env转译后,运行时可能会出现以上错误,这是因为:

plugin-transform-regenerator 使用regenerator来转译 async/generator 函数,但是它本身不包括regeneratorRuntime,你需要使用babel-polyfill/regenerator runtime来使regeneratorRuntime 存在

通常情况下,加上transform-runtime plugin即可。

@creeperyang creeperyang changed the title 了解 Babel 6 生态 了解 Babel 6 & 7 生态 Jun 21, 2018
@webjohnjiang
Copy link

@creeperyang babel7 这里这一段讲的应该是有问题的:

  1. useBuiltIns 使用的前提就是页面入口引入了 babel-polyfill. 哪怕把useBuiltIns 设置为false, 也不代表 不适用polyfill,只是没有按环境需要引入而已

plugin-transform-regenerator 使用regenerator来转译 async/generator 函数,但是他本身不包含regeneratorRuntime。

是的, 但是babel-polyfill里面包含了regeneratorRuntim,只要使用了babel-polyfill,就可以支持这个generator。否则就要使用transform-runtime+babel-runtime来支持了

@YardWill
Copy link

@cuiyongjian 赞同第一点,实际操作了一下,entry应该是按照目标浏览器的兼容性把所有需要的polyfill都放上去,
而usage就是上面提到的按照目标兼容性和按需原则引入,而当值为false的时候,就是不管三七二十一,把polyfill里面所有的内容都引入。
第二点的话,依我的理解一个完整和高兼容性的项目是需要babel-runtime和babel-polyfill一起支持的,babel-runtime提供runtimeHelper,babel-polyfill提供instance method。

@YardWill
Copy link

包括拆分后的runtime-corejs2也不支持添加instance method。

@creeperyang
Copy link
Owner Author

@cuiyongjian @YardWill

其实按你们的说法,既然polyfill全引入了,怎么还会有 ReferenceError: regeneratorRuntime is not defined 的错误?

对于 useBuiltIns: false,babel 将不会加入任何 polyfill。

可以打开debug看到调试信息:

Using polyfills: No polyfills were added, since the useBuiltIns option was not set.

@HsuTing
Copy link

HsuTing commented Aug 30, 2018

我自己的使用結果是,給您們做參考。

useBuiltIns: false 是會按照目前兼容性把需要的 polyfill 放在當前編譯的檔案
useBuiltIns: entry 是會把兼容性需要的 polyfill 放在唯一 import '@babel/polyfill' 的檔案裡面(我沒用過)

ReferenceError: regeneratorRuntime is not defined 不是 babel 的問題,是 regenerator-runtime 的問題,已經被修了,但還沒 publish https://github.com/facebook/regenerator/pull/353/files.

如果有用 useBuiltIns 是可以不用在使用 babel-polyfill/regeneratoruseBuiltIns 加入 polyfill的方式是 require('core-js/lib/<modules>'),把需要 polyfillfunctionvariable 以全域的方式補齊。 babel-polyfill/regenerator 則是以 const module = require('core-js/lib/<modules>') 的方式,僅當前編譯當案有 polyfill

比較起來 useBuiltIns: falsebabel-polyfill/regenerator 基本上是一樣的,只是一個是用全域的方式,一個是本地的方式。

@YardWill
Copy link

const presets = [
  [
    '@babel/env',
    {
      targets: '> 0.25%, not dead',
      useBuiltIns: false,
      debug: true,
      modules: 'commonjs',
    },
  ],
];

我尝试了一下仅用这个preset,然后在index内加上import '@babel/polyfill';使用async await 并没有报ReferenceError: regeneratorRuntime is not defined
我也看过polyfill的代码,transform-regenerator库是被require了。
我感觉没什么问题,不知道你是怎么出现这种情况的。

@YardWill
Copy link

@creeperyang 你也可以看一看最后打包出来的文件,看一下有没有这句代码。我按照上面的配置是能搜索到的。

__webpack_require__(/*! regenerator-runtime/runtime */ "./node_modules/regenerator-runtime/runtime.js");

@creeperyang
Copy link
Owner Author

@YardWill 理解你的意思了。你手动引入了@babel/polyfill,再设置useBuiltIns: false,这就是告诉preset-env不要处理任何有关polyfill的事(你自己会手动处理,即你手动引入了@babel/polyfill)。

所以我的意思是对的:设置useBuiltIns: false时,babel不会帮你处理任何polyfill的事,你必须手动处理。

@YardWill
Copy link

YardWill commented Aug 30, 2018

@creeperyang 多谢指教,不过我还有一个问题https://github.com/babel/babel/blob/master/packages/babel-preset-env/src/index.js#L286 为什么entry还需要自己引入polyfill?
以下是debug信息

  1. 没有手动引入polyfill。
Using polyfills with `entry` option:
`import '@babel/polyfill'` was not found.
  1. 手动引入polyfill之后
Using polyfills with `entry` option:
Replaced `@babel/polyfill` with the following polyfills:
  es6.array.copy-within { "android":"4.4", "chrome":"29", "ie":"11" }
  es6.array.fill { "android":"4.4", "chrome":"29", "ie":"11" }
  es6.array.find { "android":"4.4", "chrome":"29", "ie":"11" }// 太长就不贴全了,几乎所有polyfill和target有关。

@creeperyang
Copy link
Owner Author

@YardWill @HsuTing

以一个简单示例来说明。有文件 test.js 如下:

async function hello(a) {
    console.log(a);
}

hello(Promise.resolve(1));

我们用不同的babel配置来验证一些东西。

1. 如果只设置useBuiltIns: false

可以看到,由于transform-async-to-generator plugin起作用,async/wait被翻译到了regeneratorRuntime
但是regeneratorRuntimePromise都没有相应的polyfill被引入——即polyfill完全被忽略(不处理)。

"use strict";

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

function hello(_x) {
  return _hello.apply(this, arguments);
}

function _hello() {
  _hello = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee(a) {
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            console.log(a);

          case 1:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));
  return _hello.apply(this, arguments);
}

hello(Promise.resolve(1));

2. 如果只设置useBuiltIns: 'usage'

可以看到文件开头引入了需要的promise/regeneratorRuntime polyfill,但是由于我们未手动依赖(手动安装)regenerator-runtime 库,会报错 ReferenceError: regeneratorRuntime is not defined

同时(require("core-js/modules/es6.promise"))同样会报错,因为我们也没有安装这个依赖。

"use strict";

require("regenerator-runtime/runtime"); // 污染全局,提供 regeneratorRuntime (代码`runtime = global.regeneratorRuntime = inModule ? module.exports : {};`)

require("core-js/modules/es6.promise"); // 污染全局,提供 Promise

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

function hello(_x) {
  return _hello.apply(this, arguments);
}

function _hello() {
  _hello = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee(a) {
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            console.log(a);

          case 1:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));
  return _hello.apply(this, arguments);
}

hello(Promise.resolve(1));

3. 设置useBuiltIns: 'usage' 且同时使用plugin-transform-runtime

可以看到,上面的inline代码(如_asyncToGenerator等)被替换成@babel/runtime中相应的引用了。
同时通过require("@babel/runtime/regenerator")@babel/runtime为我们提供了缺失的regenerator-runtime 库。

但(require("core-js/modules/es6.promise"))还是会报错,因为我们没有安装这个依赖。

"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

// @babel/runtime/regenerator 依赖 regenerator-runtime/runtime,但以不污染全局的方式引入。
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));

// 然后还是会引入regenerator-runtime/runtime;有重复引入和污染全局的问题;
// TODO:这里需要注意一下!问题待跟进
require("regenerator-runtime/runtime");

// 其实可以看到,core-js 我们并没有作为依赖,但这里却引入了。
// 这一块详见 <https://babeljs.io/docs/en/v7-migration#babel-runtime-babel-plugin-transform-runtime>
require("core-js/modules/es6.promise");

var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));

function hello(_x) {
  return _hello.apply(this, arguments);
}

function _hello() {
  _hello = (0, _asyncToGenerator2.default)(
  /*#__PURE__*/
  _regenerator.default.mark(function _callee(a) {
    return _regenerator.default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            console.log(a);

          case 1:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));
  return _hello.apply(this, arguments);
}

hello(Promise.resolve(1));

4. 设置useBuiltIns: 'usage' 且同时使用plugin-transform-runtime(且配置core-js)

对比下上面,可以发现Promise被正确修复——代码已经可以正常运行。

"use strict";

var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");

var _regenerator = _interopRequireDefault(require("@babel/runtime-corejs2/regenerator"));

require("regenerator-runtime/runtime");

// promise 指向 @babel/runtime-corejs2/core-js/promise,被修复正确
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));

var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime-corejs2/helpers/asyncToGenerator"));

function hello(_x) {
  return _hello.apply(this, arguments);
}

function _hello() {
  _hello = (0, _asyncToGenerator2.default)(
  /*#__PURE__*/
  _regenerator.default.mark(function _callee(a) {
    return _regenerator.default.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            console.log(a);

          case 1:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));
  return _hello.apply(this, arguments);
}

hello(_promise.default.resolve(1));

@HsuTing
Copy link

HsuTing commented Aug 31, 2018

@creeperyang 我想了解您說的未手動安裝是指 install ? 如果只是未手動安裝,不是只要在個別 install 就好了?我猜 @babel/env 本身因為還有處理其他東西,所以 babel 團隊不希望在把整個 @babel/polyfill 加入到 dependencies 裡面,才需要自行安裝,但不需要自己在 code 裡面一一引入所需要的 core-js

另外

regeneratorRuntime (代码runtime = global.regeneratorRuntime = inModule ? module.exports : {};)

這段我也有找到,但是我自己測試的情況是,在 只設置useBuiltIns: 'usage' 的情況下,就算裝了 core-js@babel/polyfill 一樣會出現 ReferenceError: regeneratorRuntime is not defined,但我手動把 require("regenerator-runtime/runtime"); 改成 var regeneratorRuntime = require("regenerator-runtime/runtime"); 是可以的,所以我猜他是沒有被加到全域變數裡面。

我有試過用 patch-package 方式把 https://github.com/facebook/regenerator/pull/353/files 這段 PR 覆蓋掉 node_modulesregenerator-runtime,則確實是有把 regeneratorRuntime 加到全域變數,則不用另外在手動修正成 var regeneratorRuntime = require("regenerator-runtime/runtime");

@creeperyang
Copy link
Owner Author

@HsuTing
https://github.com/facebook/regenerator/pull/353/files 里已经不在注册全局的regeneratorRuntime了,但是@babel/runtime@7.0.0或者说regenerator-runtime <= 0.13.0里的regenerator-runtime/runtime都是会直接提供全局的regeneratorRuntime的(挂载到global上)。

我说的 手動安裝 更多的意思是必须自己指定 @babel/polyfill 或者 @babel/runtime 等包作为依赖并 install,而不是 babel 自动添加/管理。

@webjohnjiang
Copy link

webjohnjiang commented Sep 11, 2018

现在对generator这里更明白一点了:

  1. async编译后的代码需要 辅助函数+polyfill 才能在目标浏览器正常运行,polyfill包括regenerator-runtime,甚至是promise
  2. 因此,问题就变成了如何引入这两个 polyfill。 这里方法有很多:例如全局全部引入babel-polyfill, 或全局useBuiltIns配置为"usage","entry" 等方式的按需引入; 又或者transform-runtime的局部引入

写了一篇长文,啰嗦了这些实验过程..

@zhishaofei3
Copy link

useBuiltIns

有项目地址吗

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests