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 吗? #30

Open
sunyongjian opened this issue Sep 13, 2017 · 27 comments
Open

你真的会用 babel 吗? #30

sunyongjian opened this issue Sep 13, 2017 · 27 comments

Comments

@sunyongjian
Copy link
Owner

sunyongjian commented Sep 13, 2017

引入

这个问题是对自己的发问,但我相信会有很多跟我一样的同学。
对于 babel 的使用,近半年来一直停留在与 webpack 结合使用,以及在浏览器开发环境下。导致很多 babel 的包,我都不清楚他们是干嘛的。比如 babel-register,还有 babel-runtime,各种 presets 的区别,transform-runtime 和 babel-polyfill 的区别,helpers 是干嘛的。尽管网上的 babel 的教程很多了,但是解答自己的一些疑问,还是要花费一些功夫。所以抽出时间来总结一下。如果你对于以上概念已经比较清楚了,就不需要往下看了。

本次的 example 代码都在 github 上,而且每个文件夹都有详细的 README,说明我的使用方式。可以去参照一下用例的使用,并 clone 下来自己研究一下。

版本变化

说实话,从我做前端的时候,接触 babel 的时候,就已经是 babel 6 了,但是这不妨碍了解一下它的重大版本变化。
上一个版本 babel 5 是全家桶,包括各种package, plugins,尽可能的想通过你的一次安装,达到全能的效果。不过你现在安装npm install babel,会得到一个 warning。babel 6 是 2015年10月30号发布,主要做了以下更新:

  • 拆分成几个核心包,babel-core,babel-node,babel-cli...
  • 没有了默认的转换,现在你需要手动的添加 plugin。也就是插件化
  • 添加了 preset,也就是预置条件。
  • 增加了 .babelrc 文件,方便自定义的配置。

差不多了,我感觉其他的也不需要了解了。

babel 里面有好多的包,所以必须搞清楚他们都是干嘛的,才能让我们更好的使用这个工具。

babel-core

可以看做 babel 的编译器。babel 的核心 api 都在这里面,比如 transform,主要都是处理转码的。它会把我们的 js 代码,抽象成 ast,即 abstract syntax tree 的缩写,是源代码的抽象语法结构的树状表现形式。我们可以理解为,它定义的一种分析 js 语法的树状结构。也就是说 es6 的新语法,跟老语法是不一样的,那我们怎么去定义这个语法呢。所以必须要先转成 ast,去发现这个语法的 kind,分别做对应的处理,才能转化成 es5.

主要 api:

var babel = require('babel-core');
var transform = babel.transform;
  • babel.transform(code: string, options?: Object)
transform("code", options) // => { code, map, ast }
  • babel.transformFile(filename: string, options?: Object, callback: Function)
var path = require('path');
var result = babel.transformFileSync(path.resolve(__dirname) + "/test.js", {
  presets: ['env'],
  plugins: ['transform-runtime'],
}, function(err, result) {// { code, map, ast }
    console.log(result);
});
  • babel.transformFileSync(filename: string, options?: Object)
var result = babel.transformFileSync(path.resolve(__dirname) + "/test.js", {
  presets: ['env'],
  plugins: ['transform-runtime'],
});
console.log(result, 'res');
  • babel.transformFromAst(ast: Object, code?: string, options?: Object)

反转,你把 ast 传入,解析为 code 代码。

options

babel-cli

提供命令行运行 babel。也就是你可以 babel filename 去对文件转码。
安装的话

npm install --save-dev babel-cli

npm isntall babel-cli -g

使用对应就是

node_module/.bin/babel script.js --out-file script-compiled.js

babel script.js --out-file script-compiled.js

具体使用还是看官方文档吧,我就不搬文档了。

babel-external-helpers

babel-cli 中的一个command,用来生成一段代码,包含 babel 所有的 helper 函数。

首先我们需要了解什么是 helpers。babel 有很多帮助函数,例如 toArray函数, jsx转化函数。这些函数是 babel transform 的时候用的,都放在 babel-helpers这个包中。如果 babe 编译的时候检测到某个文件需要这些 helpers,在编译成模块的时候,会放到模块的顶部。
像这样

(function(module, exports, __webpack_require__) {

function _asyncToGenerator(fn) { return function () {  }; } // 模块顶部定义 helper

// some code 
// async 语法已经被 transform-async-to-generator 转化,再利用 helper 函数包装,消费 generator。
const func = (() => {
  var _ref = _asyncToGenerator(function* () {
    console.log('begin');
    yield new Promise(function (resolve) {
      setTimeout(function () {
        resolve();
      }, 1000);
    });
    console.log('done');
  });
})

})

但是如果多个文件都需要提供,会重复引用这些 helpers,会导致每一个模块都定义一份,代码冗余。所以 babel 提供了这个命令,用于生成一个包含了所有 helpers 的 js 文件,用于直接引用。然后再通过一个 plugin,去检测全局下是否存在这个模块,存在就不需要重新定义了。

使用:

  1. 执行 babel-external-helpers 生成 helpers.js 文件,
 node_modules/.bin/babel-external-helpers > helpers.js

注意:示例代码的包都是装到项目中的,也就是本地。同样你可以全局安装直接执行。
2. 安装 plugin

npm install --save-dev babel-plugin-external-helpers
  1. 然后在 babel 的配置文件加入
{
  "plugins": ["external-helpers"]
}
  1. 入口文件引入 helpers.js
require('./helpers.js');

这样就可以啦,还是可以减少很多代码量的。另外如果使用了 transform-runtime,就不需要生成 helpers.js 文件了,这个在后面的 babel-runtime 再说。

babel-node

也是 babel-cli 下面的一个 command,主要是实现了 node 执行脚本和命令行写代码的能力。举两个栗子就清楚了。

执行脚本

默认的node 环境肯定是不支持 jsx 的

// test.js
const React = require('react');
const elements = [1, 2, 3].map((item) => {
  return (
    <div>{item}</div>
  )
});

console.log(elements);

执行 test.js,会报错,不认识这个语法。

node test.js //报错

但是使用 babel-node 就可以。

 node_modules/.bin/babel-node --presets react test.js

--presets react 是参数,等同于

{
  "presets": ["react"]
}

执行正常。

node 命令行写代码

注意: 本文所有代码示例,均在 node 版本 4.8.4 下执行。

写个解构赋值的,直接运行 node,不支持。

node
运行 node_modules/.bin/babel-node --presets env

babel-node

得到 a 的 value 是 1。

通过栗子基本已经介绍了 babel-node 的用法了,就是方便我们平常开发时候,写一些脚本的。所以它不适用于生产环境。另外,babel-node 已经内置了 polyfill,并依赖 babel-register 来编译脚本。好,那 babel-register 是什么呢

babel-register

npm install babel-register --save-dev

babel-node 可以通过它编译代码,可以了解到,它其实就是一个编译器。我们同样可以在代码中引入它 require('babel-register'),并通过 node 执行我们的代码。

它的原理是通过改写 node 本身的 require,添加钩子,然后在 require 其他模块的时候,就会触发 babel 编译。也就是你引入require('babel-register')的文件代码,是不会被编译的。只有通过 require 引入的其他代码才会。我们是不是可以理解,babel-node 就是在内存中写入一个临时文件,在顶部引入 babel-register,然后再引入我们的脚本或者代码?

举个栗子,还是 node 中执行 jsx,要通过 babel 编译。我们可以把 jsx 的代码 a.js 编译完输出到一个 b.js,然后 node b.js 也是可以执行的。但是太麻烦,不利于开发。让我们看一下通过 register 怎么用:

// register.js 引入 babel-register,并配置。然后引入要执行代码的入口文件
require('babel-register')({ presets: ['react'] });
require('./test')
// test.js 这个文件是 jsx...
const React = require('react');
const elements = [1, 2, 3].map((item) => {
  return (
    <div>{item}</div>
  )
});
console.log(elements);
// 执行
$ node register.js

它的特点就是实时编译,不需要输出文件,执行的时候再去编译。所以它很适用于开发。总结一下就是,多用在 node 跑程序,做实时编译用的,通常会结合其他插件作编译器使用,比如 mocha 做测试的时候。

值得一提的是,babel-register 这个包之前是在 babel-core 下面的,所以也可以 require('babel-core/register') 去引入,跟require('babel-register')是一样的。但是,babel 的团队把 register 独立出来了,而且未来的某一天(升 7.0)会从 babel-core 中废除,所以我们现在最好还是使用 babel-register 吧。babel-core/register.js

babel-runtime

npm install babel-runtime --save

这个包很简单,就是引用了 core-js 和 regenerator,然后生产环境把它们编译到 dist 目录下,做了映射,供使用。那么什么是 core-js 和 regenerator 呢。
首先我们要知道上面提到的 babel-core 是对语法进行 transform 的,但是它不支持 build-ints(Eg: promise,Set,Map),prototype function(Eg: array.reduce,string.trim),class static function (Eg:Array.form,Object.assgin),regenerator (Eg:generator,async)等等拓展的编译。所以才要用到 core-js 和 regenerator。

core-js

core-js 是用于 JavaScript 的组合式标准化库,它包含 es5 (e.g: object.freeze), es6的 promise,symbols, collections, iterators, typed arrays, es7+提案等等的 polyfills 实现。也就是说,它几乎包含了所有 JavaScript 最新标准的垫片。不过为什么它不把 generator 也实现了... 😁

// 比如,只不过需要单个引用
require('core-js/array/reduce');
require('core-js/object/values');

regenerator

它是来自于 facebook 的一个库,链接。主要就是实现了 generator/yeild, async/await。

所以 babel-runtime 是单纯的实现了 core-js 和 regenerator 引入和导出,比如这里是 filter 函数的定义,做了一个中转并处理了 esModule 的兼容。

module.exports = { "default": require("core-js/library/fn/array/filter"), __esModule: true };

helpers

还记得提 babel-external-helpers 的时候,介绍 helpers 了吗。babel-runtime 里面的 helpers 就相当于我们上面通过 babel-external-helpers 生成的 helpers.js。只不过它把每个 helper 都单独放到一个文件夹里。这样,配合 transform-runtime 使用的时候,需要用 helper 转化的时候,就从 babel-runtime 中直接引用了。

var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');

var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);

文件结构:

文件结构

使用

可以单独引入require('babel-runtime/core-js/object/values');

不过这些模块都做了 esModule 的兼容处理,也就是上面引入的模块是{ "default": require("core-js/library/fn/array/filter"), __esModule: true }这样的,要使用还得加上 .default。所以我们期待的是,最好能有帮我们自动处理的插件,babel-plugin-transform-runtime就是用来做这个的。这个我们放到 plugin 去讲。

babel-polyfill

npm install babel-polyfill --save

babel-runtime 已经是一堆 polyfill 了,为什么这里还有一个类似的包,它同样是引用了 core-js 和 regenerator,垫片支持是一样的。官网是这么说的,babel-polyfill 是为了模拟一个完整的ES2015 +环境,旨在用于应用程序而不是库/工具。并且使用babel-node时,这个polyfill会自动加载(这个我们在介绍 babel-node 的最后已经说了)。

也就是说,它会让我们程序的执行环境,模拟成完美支持 es6+ 的环境,毕竟无论是浏览器环境还是 node 环境对 es6+ 的支持都不一样。它是以重载全局变量 (E.g: Promise),还有原型和类上的静态方法(E.g:Array.prototype.reduce/Array.form),从而达到对 es6+ 的支持。不同于 babel-runtime 的是,babel-polyfill 是一次性引入你的项目中的,就像是 React 包一样,同项目代码一起编译到生产环境。

使用

我们结合 babel-register 去使用一下

// index.js
require('babel-core/register')({});
require('babel-polyfill'); // 是的,你要手动引入。
require('./async');
// async.js
async function a() {
  console.log('begin');
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, 1000)
  })
  console.log('done');
}
a();
$ node index.js

完美运行。

注意:babel-polyfill 只是为当前环境全局下注入垫片,ES6 语法(E.g: arrow func,esModules)还是要加入 plugins 去 transform 的。

plugins

要说 plugins 就不得不提 babel 编译的过程。babel 编译分为三步:

  1. parser:通过 babylon 解析成 AST。
  2. transform[s]:All the plugins/presets ,进一步的做语法等自定义的转译,仍然是 AST。
  3. generator: 最后通过 babel-generator 生成 output string。

所以 plugins 是在第二步加强转译的,所以假如我们自己写个 plugin,应该就是对 ast 结构做一个遍历,操作。

babel-plugin-transform-runtime

上面我们知道,transform-runtime 是为了方便使用 babel-runtime 的,它会分析我们的 ast 中,是否有引用 babel-rumtime 中的垫片(通过映射关系),如果有,就会在当前模块顶部插入我们需要的垫片。试一下:

npm install babel-plugin-transform-runtime
// 编译前
console.log(Object.values({ 1: 2 }));
node_modules/.bin/babel --plugins transform-runtime values.js
// 编译后
'use strict';

var _values = require('babel-runtime/core-js/object/values');

var _values2 = _interopRequireDefault(_values);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

onsole.log((0, _values2.default)({ 1: 2 }));

另外,它还有几个配置

// 默认值
{
  "plugins": [
    ["transform-runtime", {
      "helpers": true,
      "polyfill": true,
      "regenerator": true,
      "moduleName": "babel-runtime"
    }]
  ]
}

如果你只需要用 regenerator,不需要 core-js 里面的 polyfill 那你就可以在 options 中把 polyfill 设为 false。helpers 设为 false,就相当于没有启用 babel-plugin-external-helpers 的效果,比如翻译 async 的时候,用到了 asyncToGenerator 函数,每个文件还会重新定义一下。moduleName 的话,就是用到的库,你可以把 babel-runtime 换成其他类似的。

transform-runtime 对比 babel-polyfill

其实通过上面的介绍我们已经了解他们是干什么的了,这里再稍微总结区分一下吧。我在这里把 babel-runtime 和 babel-plugin-transform-runtime 统称为 transform-runtime,因为一起用才比较好。

  • babel-polyfill 是当前环境注入这些 es6+ 标准的垫片,好处是引用一次,不再担心兼容,而且它就是全局下的包,代码的任何地方都可以使用。缺点也很明显,它会污染原生的一些方法,polyfill 把原生的方法重写了,如果当前项目已经有一个 polyfill 的包了,那你只能保留其一。而且一次性引入这么一个包,会大大增加体积。如果你只是用几个特性,就没必要了,如果你是开发较大的应用,而且会频繁使用新特性并考虑兼容,那就直接引入吧。
  • transform-runtime 是利用 plugin 自动识别并替换代码中的新特性,你不需要再引入,只需要装好 babel-runtime 和 配好 plugin 就可以了。好处是按需替换,检测到你需要哪个,就引入哪个 polyfill,如果只用了一部分,打包完的文件体积对比 babel-polyfill 会小很多。而且 transform-runtime 不会污染原生的对象,方法,也不会对其他 polyfill 产生影响。所以 transform-runtime 的方式更适合开发工具包,库,一方面是体积够小,另一方面是用户(开发者)不会因为引用了我们的工具,包而污染了全局的原生方法,产生副作用,还是应该留给用户自己去选择。缺点是随着应用的增大,相同的 polyfill 每个模块都要做重复的工作(检测,替换),虽然 polyfill 只是引用,编译效率不够高效。另外,instance 上新添加的一些方法,babel-plugin-transform-runtime 是没有做处理的,比如 数组的 includes, filter, fill 等,这个算是一个关键问题吧,直接推荐用 polyfill。link

另外,关于 babel-runtime 为什么是 dependencies 依赖。它只是一个集中了 polyfill 的 library,对应需要的 polyfill 都是要引入项目中,并跟项目代码一起打包的。不过它不会都引入,你用了哪个,plugin 就给你 require 哪个。所以即使你最终项目只是 require('babel-runtime/core-js/object/values')其中的一个文件,但是对于这包来说,也是生产依赖的。

polyfill
注意:babel-polyfill 并不是一定会污染全局环境,在引入这个 js,并运行的时候,它会先判断当前有没有这个方法,在看要不要重写。如上图

presets

各种配置 plugin 实在是费劲,es6+ 编译要加入好多 plugins,比如为了在 node 中使用 esmodule,要把 esmodule 转化成 commomjs,使用 transform-es2015-modules-commonjs,还有 asyncToGenerator,React jsx转化等等,不仅要装好多,还要配好多。

presets 就是 plugins 的组合,你也可以理解为是套餐... 主要有

大部分的 presets 我觉得都不需要介绍了,官网上写的比较详细。而且 babel-preset-lastet 已经废弃,被 babel-preset-env 代替。

{ "presets": ["latest"] } === { "presets": ["env"] }

babel-preset-env

这个 preset 真是神器啊,它能根据当前的运行环境,自动确定你需要的 plugins 和 polyfills。通过各个 es标准 feature 在不同浏览器以及 node 版本的支持情况,再去维护一个 feature 跟 plugins 之间的映射关系,最终确定需要的 plugins。

preset-env 配置

详情:

{
  "presets": [
    [
      "env",
      {
        "targets": { // 配支持的环境
          "browsers": [ // 浏览器
            "last 2 versions",
            "safari >= 7"
          ],
          "node": "current"
        },
        "modules": true,  //设置ES6 模块转译的模块格式 默认是 commonjs
        "debug": true, // debug,编译的时候 console
        "useBuiltIns": false, // 是否开启自动支持 polyfill
        "include": [], // 总是启用哪些 plugins
        "exclude": []  // 强制不启用哪些 plugins,用来防止某些插件被启用
      }
    ]
  ],
  plugins: [
    "transform-react-jsx" //如果是需要支持 jsx 这个东西要单独装一下。
  ]
}

主要介绍 debug 和 很好用的 useBuiltIns 吧。

debug

开启debug后,编译结果会得到使用的 targets,plugins,polyfill 等信息

Using targets:
{
  "chrome": "59",
  "android": "4.4.3",
  "edge": "14",
  "firefox": "54",
  "ie": "10",
  "ios": "10",
  "safari": "7",
  "node": "4.8.4"
}

Modules transform: commonjs

Using plugins:
  check-es2015-constants {"android":"4.4.3","ie":"10","safari":"7","node":"4.8.4"}
  transform-es2015-arrow-functions {"android":"4.4.3","ie":"10","safari":"7","node":"4.8.4"}
  transform-es2015-block-scoped-functions {"android":"4.4.3","ie":"10","safari":"7"}
  transform-es2015-block-scoping {"android":"4.4.3","ie":"10","safari":"7","node":"4.8.4"}
  ...
Using polyfills:
  es6.typed.array-buffer {"android":"4.4.3","ie":"10","safari":"7","node":"4.8.4"}
  es6.typed.int8-array {"android":"4.4.3","ie":"10","safari":"7","node":"4.8.4"}
  es6.typed.uint8-array {"android":"4.4.3","ie":"10","safari":"7","node":"4.8.4"}
  es6.typed.uint8-clamped-array {"android":"4.4.3","ie":"10","safari":"7","node":"4.8.4"}
  es6.typed.int16-array {"android":"4.4.3","ie":"10","safari":"7","node":"4.8.4"}
  ...

useBuiltIns

env 会自动根据我们的运行环境,去判断需要什么样的 polyfill,而且,打包后的代码体积也会大大减小,但是这一切都在使用 useBuiltIns,而且需要你安装 babel-polyfill,并 import。它会启用一个插件,替换你的import 'babel-polyfill',不是整个引入了,而是根据你配置的环境和个人需要单独的引入 polyfill。 我尝试了一下是否真的有效,下面是我的对比实验过程:

step1: 首先是这样一段测试编译的代码,有 jsx,Object.values,async。env 的配置除了 useBuiltIns 都跟上面的配置一样。然后通过 webpack + babel-loader 打包,生成 build.js

require('./async');
// import 'babel-polyfill';

const React = require('react');
const elements = [1, 2, 3].map((item) => {
  return (
    <div>{item}</div>
  )
});

console.log(elements);

async function a() {
  console.log('begin');
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, 1000)
  })
  console.log('done');
}
a();

console.log(Object.values({ 1: 2 }));

console.log(Array.isArray([]));

step2: 然后通过设置不同的参数,打包,获取 build.js,并执行。得到下表

image

  • 用 preset-es2015,并引入 polyfill
    plugins 多加一个 transform-regenerator,这方面确实不如 env 方便一些。
    体积 418k,执行通过。不方便在要配好多 plugins。

具体的过程、截图猛戳 这里

最终的结论就是,使用了 useBuiltIns 确实体积变小了,比直接 import 'babel-polyfill' 好了许多。

step3: 然后... 我又试了一下 env 下,使用 transform-runtime。在不加 useBuiltIns,不引入 babel-polyfill 的情况下。build.js 体积234k,执行通过。

咦,这样好像体积更小啊。别忘了,我们的 babel-polyfill 是配置了执行环境的,通过环境看你需要哪些 polyfill。而 transform-runtime,是发现我们代码需要什么 polyfill,当然会少很多了。所以,又回到了用哪个的问题... 😓 参考上面的总结。

then

helpers 的问题。开发项目,使用 preset-env,并 import 'babel-polyfill',但是 helpers 好像没有地方配置。而且我试了两个文件分别用 async 函数,编译后每个模块都定义了 asyncToGenerat 函数,这种情况下我觉得最后就是自己生成一个 helpers.js 文件了。

总结

现在看起来开发大点的项目,最好用的配置应该就是 preset-env,确定自己的运行环境,如果有需要,再加上 useBuiltIns,并生成一份 helpers.js 的文件。不过,一切还是看你的需求,我的前提是开发大点的“项目”,不过了解了这些东西,你会做出自己的选择。

babel 的配置

目前 babel 官方推荐是写到 .babelrc 文件下,你还可以在 package.json 里面添加 babel 字段。不用配置文件的话,可以把配置当做参数传给 babel-cli

  • .babelrc
{
  "presets": [
    "env"
  ],
  "plugins": [
    ["transform-runtime", {
      "helpers": true,
      "polyfill": true,
      "regenerator": true,
      "moduleName": "babel-runtime"
    }]
  ]
}
  • 写到 package.json
"babel": {
  "presets": [
    "env"
  ],
}
  • babel cli
babel script.js --plugins=transform-runtime --presets=env

配合其他工具

webpack

比较常用,除了 babel 自己的包,多装一个 babel-loader 配合 webpack 使用。并在 webpack.config.js 中加入 loader 的配置

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

mocha

项目里的代码都是用 es6+ 写的,但是做单元测试的时候,测试框架并不认识你的什么 esModule,es6+ 的一些语法,mocha 是 node 程序,所以你要把 esModule 转化成 commomjs 之类的。

mocha 是支持编译器的,通过 --compilers 指定,这里我们用 babel,举个栗子

// 求和函数 add.js
const add = (x, y) => x + y;

export default add;

// 测试脚本 add.test.js
import { expect } from 'chai'; // chai 是断言库
import add from './add';

describe('es6 两数相加', () => {
  it('2 + 4 = 6', () => {
    expect(add(2, 4)).equal(6);
  })
});
./node_modules/mocha/bin/mocha --compilers js:babel-register add.test.js

因为 mocha 终究是在跑 node 程序的,适用于实时编译,所以可以用 babel-register 做编译器。

最后

总结这些东西花了我两三天的时间,虽然搞清楚了这些包是干嘛的,但是又在想到底应不应该花时间研究这些,工具始终是用来使用的,对于 babel 来说更应该研究的是它对 ast 的处理方式?不过看到自己的产出,我觉得是有必要的,另外,因为对工具更进一步的了解,才能更好的在项目中使用它们,而不是一顿照搬,代码是可以正常用了,但是可能会有很多不需要的东西,导致代码体积变大。“割鸡焉用牛刀”,我觉得还是需要有精益求精的精神。希望对你有所帮助。

我的个人博客地址 https://github.com/sunyongjian/blog ,欢迎订阅,star,谢谢。

@jiangtao
Copy link

写的不错哦

@sunyongjian
Copy link
Owner Author

@jiangtao 谢谢大佬。

@rylan0119
Copy link

transform-runtime中的"helpers"和"moduleName"到底设置了什么,我是用webpack打包的,感觉没什么影响

@sunyongjian
Copy link
Owner Author

@liruilong119 你试一下 helpers 为 false 和 true 的情况,最后包的大小应该不一样。开了 helpers 跟 babel-external-helpers 的效果是一样的啊。 moduleName 就是在转译的时候,用哪个包去换语法啊,不过都是用 babel-runtime 就是了,包含 core-js,regenerator,helpers 三个静态包。

@rylan0119
Copy link

@sunyongjian 我试了分别helpers 为 false 和 true ,打包后没区别,连chunkhash都没变

这是我的配置

目录结构

.
├── dist
│   └── main-c9017c04383c5e9a1924.js
├── package-lock.json
├── package.json
├── src
│   └── main.js
└── webpack.config.js
{
    "presets": ["env"],
    "plugins": [
        ["transform-runtime", {
            "helpers": false,
            "polyfill": true,
            "regenerator": false,
            "moduleName": "babel-runtime"
        }]
    ]
}

webpack.config.js

var path = require("path");

module.exports = {
    entry: {
        main: "./src/main.js"
    },
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "[name]-[chunkhash].js"
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: [path.resolve(__dirname, "node_modules")],
                include: [path.resolve(__dirname, "src")],
                use: ["babel-loader"]
            }
        ]
    }
};

js就写了个Promise

var p = new Promise(function (reslove, reject) {
    reslove("hello");
});

p.then(function (succ) {
    console.log(succ);
});

麻烦看下有什么问题

@sunyongjian
Copy link
Owner Author

@liruilong119 promise 是用不到辅助函数的。你首先得了解,什么时候用得到 helper 函数,https://github.com/babel/babel/blob/master/packages/babel-helpers/src/helpers.js

我测试过,在另一个仓库里有,你去看看。https://github.com/sunyongjian/babel-usage/tree/master/runtime

@rylan0119
Copy link

@sunyongjian 谢谢,了解了

@ron0115
Copy link

ron0115 commented Nov 8, 2017

支持,写得真不错

@sunyongjian
Copy link
Owner Author

@ron0115 谢谢。

@RalfZhang
Copy link

您好,关于这一段
step3: 然后... 我又试了一下 env 下,使用 transform-runtime。在不加 useBuiltIns,不引入 babel-polyfill 的情况下。build.js 体积234k,执行通过。
我想请教一下这实现了新的原型和类上的静态方法吗?如果实现了,是修改了全局吗?

@sunyongjian
Copy link
Owner Author

@RalfZhang 没有啊,使用 transform-runtime 这个插件,就是识别到你需要什么垫片,就在当前模块引入垫片,不会修改全局的。另外 polyfill 并不一定是会修改全局的,是先判断当前环境有没有,再重写。

@whucj
Copy link

whucj commented Mar 5, 2018

请问可以不引入 babel-polyfill吗? 代码里面我用到了 Array的findIndex方法? 如果不引入babel-polyfill,在IE下直接报错?

@sunyongjian
Copy link
Owner Author

@whucj 对,IE 下会直接报错,所以需要 polyfill 啊。不过如果你只用到了这一个方法,可以只对这个方法做兼容...

@Elainelele
Copy link

这里是我理解错了吗? 好像反了?
image

@sunyongjian
Copy link
Owner Author

@Elainelele 感谢细心的阅读,的确是表格第一行写反了。结论是没错的~

@L-Chris
Copy link

L-Chris commented Apr 6, 2018

那其实是不是有transfrom-runtime就可以不用babel-polyfill呢?

@sunyongjian
Copy link
Owner Author

sunyongjian commented Apr 6, 2018

@L-Chris 从编译完的效果、目的来说,是这样的。不过数组的 includes()、filter() 等方法,babel-plugin-transform-runtime 是不会进行扩展的。如果遇到类似的问题,需要单独对 includes 打补丁

@rylan0119
Copy link

请问使用 babel-runtime的话,对于实例方法你是怎么处理的?文档说babel-runtime并不处理实例方法

@L-Chris
Copy link

L-Chris commented Apr 9, 2018

感觉目前最好好的方案还是直接确定好浏览器范围,然后直接babel-polyfill + useBuiltIns
要么尝试下polyfill.io,自动根据浏览器user-agent返回polyfill,但肯定不能做到百分百正确

@sunyongjian
Copy link
Owner Author

@L-Chris 嗯,赞同,项目中的确是这样搞的。

@sunyongjian
Copy link
Owner Author

@liruilong119 嗯,原型上新添加的方法 babel-plugin-transform-runtime 没法处理。最好的办法就是引入 babel-polyfill 吧,并打开 env 的 useBuiltIns。

@rylan0119
Copy link

@sunyongjian 使用useBuiltIns的话,transform-runtime的polyfill、regenerator的就要关闭吧,感觉这两种polyfill方式各有优缺点,我更想使用babel-time,实例方法可以手动引入polyfill,但感觉太麻烦,不知道有没有自动化的方式

@jiangtao
Copy link

@liruilong119 babel-preset-env

@simongong
Copy link

打酱油的路过,忍不住点赞

@Chang1ng
Copy link

请问使用 babel-runtime的话,对于实例方法你是怎么处理的?文档说babel-runtime并不处理实例方法

现在7.4版本的runtime已经支持实例方法了。指定corejs: 3

@Ulanxx
Copy link

Ulanxx commented Sep 1, 2019

非常清晰!

@nickli
Copy link

nickli commented Dec 26, 2019

好棒

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