再学babel配置

babel 每次学习都有新的理解,哪怕是其配置都与咱们前端生态中的各类概念息息相关。近期再次复习babel知识从而更好的编写js类库,本文是学习过程所作的记录。前端

babel 生态里的一些npm模块

  • babel/core 核心转义功能
  • babel/cli 命令行工具,能够经过babel命令来转换代码,或者转换文件,输出转义后的文件。如 babel src --out-dir lib --watch
  • babel/node 是用他来执行es6+代码,以node方式执行。适合于去执行你写的nodejs代码。

那么,咱们使用 webpack 来打包js代码时,是使用的什么呢?答案是 babel-loader。node

babel-lodaer 是适配 webpack 的一个模块,其内部就是调起 babel/core 来实现代码转译。webpack

理解转译其实有2种类型的转译

记住:babel自身惟一能作的事情叫:代码转译。
至于新增api那些,那是须要引入polyfill垫片的事情(下文会讲)。es6

babel自己所作的事情,通常都叫他代码转译。那么所谓的转译都有哪些种类呢?以下:web

一、单纯的语法解析和转换。例如箭头函数转成function
二、须要一点辅助的语法解析和转换,例如 class语法,给你转成prototype原型语法chrome

解释下第二点:咱们知道,一种新的语法意味着新的一些关键字和符号等,那么对于一些特殊符号如箭头函数,那么他刚好有es5对应的 function 关键字,因此babel只需简单换一下字符。npm

然而有些却不是简单的换一下,例如 class 语法,babel给你转成 es5 以后,他必须改形成函数和prototype的写法,在这种状况下,babel编译后的代码中会加入一些辅助函数(也就是下文说的babel/runtime所干的事儿)以协助来用es5的方式实现 class。api

举个栗子:
image.pngpromise

看到了吗,babel生成的原型式写法中,须要在构造函数中对调用者的实例化方式进行检测,这些都封装成了 helper函数 _classCallCheck。浏览器

除此以外,还有更复杂的场景 babel转译以后甚至须要依赖polyfill垫片:例如 async 转换。 babel 会把 async 语法代码转成 generator 辅助函数,而 generator 辅助函数的功能须要依赖 regenerator-runtime 这个polyfill垫片。以下图,babel给你把async语法转换后,多了不少辅助函数;甚至其中有一个 regeneratorRuntime 的函数是没有找到定义的,而这个函数其实就是依赖全局须要引入 regenerator-runtime 这个 polyfill 才行。

image.png

理解什么是 corejs 和 babel/runtime

首先,咱们要理解什么是 polyfill 什么是辅助函数。

像上文所说的2种babel转译,其中转换以后所出现的那些辅助函数,叫作运行时所须要的helper,他们其实就是 runtime。 而这些运行时函数,有一个单独的 npm 包去实现他们,叫作 babel/runtime。

而像那些 es6 新增的 API,例如 Promise、Map、WeakMap。数据新方法 flat、includes 等等 api,这些东西不属于 babel语法编译的范畴,是属于一些新的api。这些叫作 垫片,英文名叫 polyfill。在社区里,polyfill垫片经过另外2个npm包来实现:一个叫作 corejs(用来实现除了generator以外的垫片,如今要使用他的版本3),另外一个叫作 regenerator-runtime(用来实现generator垫片)。

实战:配置 babel 的最佳实践

其实最佳实践的前提就是正确的理解上文中两个概念。从而正确的使用 babel/runtime 和 corejs3.

若是比喻成作饭,babel/runtime 和 corejs3 就是咱们的食材。有了食材,咱们就要用一个配套的刀具来加工他们。

  • babel/runtime配套的刀具是:babel/plugin-transform-runtime
  • corejs3的配套刀具是 preset/env 预设。

咱们接下来所讲的实践,其背景是在 webpack + babel 下的实践。由于,毕竟咱们一般不会单据来用 babel 操做一个 js 文件。

webpack 打包一个网站应用js时

这种场景的要求是:

  • 按需转译语法和辅助函数。例如若是个人目标平台支持了箭头函数,那么babel请不要给我转换成es5那种语法。
  • 全局polyfill垫片便可,不怕污染,由于我指望面向我产品全部用户使用的浏览器
  • 尽量的少打包垫片。即按需polyfill。由于咱们指望尽量减小没必要要的体积。好比我面向的用户浏览器大于IE10,那么IE9的polyfill不要打;同时,若是我代码中没有使用promise,那么promise的垫片也不要给我打
  • helper那些辅助函数,不要每一个js模块中都写一份(由于babel自己确定是针对每一个js编译的,因此默认每一个js确定都会出现辅助函数)

解决方法:

一、 为了能按需转译语法。在最新babel7以后,咱们只需使用preset/env预设就很简单了。

// babel.config.js
module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": false, // polyfill配置先关掉,后面再讲
      }
    ]
  ]
}

咱们能够经过修改 targets ,来决定babel编译哪些语法。例如你把chrome版本号调到最新1个版本,那么箭头函数必然是不会转换的。

二、 三、按需加载polyfill

按需加载polyfill有2种方式,一种是把 useBuiltIns配置成 entry,另外一种是配置成 usage.

使用上的区别是:

  • entry:须要你手工在你的webpack 入口js里,引入一下corejs和regenerator-runtime这俩polyfill。babel编译后,会自动在入口js里,把你那2行换成面向目标targets按需引用的corejs模块。
  • usage:不须要你手工引入。babel会自动把 corejs库的模块放到你的每一个js模块里。放置的原则就是:不只面向目标targets来按需引入,并且还按照你代码中是否使用来引入。假如你的a.js里用了promise,那么他会把corejs中promise模块引入。

以下是usage的方式:

module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": 'usage',
        "corejs": 3 // 写死就好!当前阶段就该用3
      }
    ]
  ]
}

以下是entry方式

//babel.config.js
module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": 'entry',
        "corejs": 3
      }
    ]
  ]
}

entry模式时,须要在代码里手工引入一下。

// index.js
// 手工引入
import "core-js/stable";
import "regenerator-runtime/runtime";

既然usage这么好,咱们何苦要用entry呢? 因此,就用usage模式吧!

四、问题来了:怎么解决辅助函数的冗余问题。

babel/runtime是干啥的,要杂用?

上文的配置,咱们解决了polyfill的问题。下一步,咱们要解决辅助函数在每一个js里都有冗余的问题。由于如今的配置下,若是你有 a.js 和 b.js,那么两个js中都会被babel放入那些 helper 辅助函数。当webpack打包完成一个 bundle。学过webpack原理的应该指到:最终的bundle里也会有 a.js 和 b.js模块,每一个模块中必然也存在重复的辅助函数。

怎么解决? 那就用 babel/plugin-transform-runtime 插件。
这个插件的工做就是:在babel编译每一个js时,把里面的辅助函数给换成 对 babel/runtime 的 require 引用。

当每一个js里的辅助函数,都变成 babel/runtime的引用。那么webpack打包后的bundle,必然这些辅助函数就变成一个公共模块了,解决了冗余问题。

配置方式:

module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": 'usage',
        "corejs": 3
      }
    ]
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "helpers": true,  // 这就是抽离helper 的配置
      "corejs": false, // 先设置false,后面再讲
      "regenerator": false // 先设置false,后面再讲
    }]
  ]
}

上面把 helper配置为true,就能够实现helper函数抽到 babel/runtime。

至此,网站打包讲解配置完成!开发网站时就用上述配置便可。

webpack 打包一个 jssdk 时

开发jssdk跟开发网站对bundle.js有不一样的要求。

  • 咱们指望咱们的sdk能够支持不少target环境,所以须要polyfill垫片。但咱们不知道jssdk的运行环境是否有垫片。这里有2个方法,方法1:经过文档告诉开发者你须要加哪些垫片;方法2:咱们本身polyfill,可是不能污染全局
  • jssdk若是本身进行polyfill。那固然也但愿按需polyfill,减小体积
  • 辅助函数一样须要减小冗余,跟上文网站相同。

能够显而易见,对于开发jssdk,咱们第一要作的,就是先把全局polyfill给他关掉。好比关掉那个 usage:

module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": false
      }
    ]
  ]
}

幸运的是,jssdk的另外几个要求,靠 babel/plugin-transform-runtime 均可以搞定。以下配置便可:

module.exports = {
  "presets": [
    [
      "@babel/env",
      {
        "targets": "last 50 Chrome versions",
        "useBuiltIns": false
      }
    ]
  ],
  "plugins": [
    ["@babel/plugin-transform-runtime", {
      "helpers": true,  // 这就是抽离helper 的配置
      "corejs": 3, // 这是局部polyfill的配置
      "regenerator": true // 这是局部polyfill generator的配置
    }]
  ]
}

咱们只需把该插件的配置全开。把 preset/env 的polyfill配置关掉,就是一个适配jssdk开发的配置了。

相关文章
相关标签/搜索