使用babel插件的注意事项

2021-02-22 14:56:57 浏览数 (1)

这里我们主要讲解babel7的相关方法。

首先第一点就是babel的三个核心包:@babel/core、@babel/cli、@babel/type。

如果要使用命令行来执行babel则需要安装@babel/cli,如果需要在js脚本中使用babel,那么就需要安装@babel/core、和@babel/type了。

这里要注意一下这个@这个符号,这个是只有babel7才特有的,babel6都木有,市面上大量代码都是基于6的所以要特别注意。

babel常用的库有以下几种:

@babel/cli

@babel/core

@babel/preset-env

@babel/polyfill

@babel/runtime

@babel/plugin-transform-runtime

@babel/plugin-transform-xxx。

这里需要注意@babel/cli依赖@babel/core。

babel转化js语法分为两部分一部分与语法结构如const、let一部分为api也就是内置函数如includes等。

而@babel/preset-env就是转化语法结构的,@babel/preset-env是一系列插件的集合,包含了我们在babel6中常用的es2015,es2016, es2017等最新的语法转化插件,允许我们使用最新的js语法,比如 let,const,箭头函数等等。

而@babel/polyfill的polyfill直译过来就是垫片的意思就是为了转化api的工具。

@babel/preset-env如何使用呢?

简单的使用如下:

代码语言:javascript复制
{
  "presets": [
    [
      "@babel/preset-env"
    ]
  ]
}

此时会根据只会转译语法而不会转译api,并且会根据另外一个配置来转译,这就是browserslist,这个配置有三种配置方式,一种是在.babelrc中的target参数中配置,一种是在.browserslistrc中配置,一种是在package.json中配置。

package.json中配置如下:

代码语言:javascript复制
{
  "browserslist": [
    "last 1 version",
    "> 1%",
    "maintained node versions",
    "not dead"
  ]
}

.browserslistrc配置如下:

代码语言:javascript复制
# 注释是这样写的,以#号开头
last 1 version #最后的一个版本
> 1%  #代表全球超过1%使用的浏览器
maintained node versions #所有还被 node 基金会维护的 node 版本
not dead

在.babelrc中的配置方式为:

代码语言:javascript复制
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "chrome": "58",
          "ie": "11"
          }
      }
    ]
  ]
}

通过配置browserslist可以根据浏览器版本转译相应的语法,那api如何转译呢?也就是@babel/polyfill如何使用呢?答案就是配置babel/preset-env。babel/preset-env中有一个参数是开启@babel/polyfil的默认为false,也就是不开启垫片,默认如下:

代码语言:javascript复制
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": false
      }
    ]
  ]
}

上面这种方式,即不开启垫片,不需要转译api。

第二种方式,参数值为entry,意思是在入口文件引入所有api,代码如下:

代码语言:javascript复制
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "entry",
        "debug": true
      }
    ]
  ]
}

使用这种方式,还需要在入口文件手动引入@babel/polyfill,如图:

上图展示了如何引入babel/polyfill和其转译后的结果,我们可以通过转译结果看到其弊端,有两个,1是覆盖全局变量,2是引入所有垫片,用这种方式第一个缺点是无法解决的,但是我们可以通过配置browserlistrc来限制引入的方法,代码如下:

代码语言:javascript复制
// .browserslistrc

Chrome > 75

这样转译是会根据chrome的版本来适量引入,而不是全部。

第三种方式,参数为useage,此时代码中用到谁就引入谁,比较智能。

代码语言:javascript复制
{
  "presets": [
    ["@babel/preset-env", 
      {
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ]
}

corejs指定polyfill的版本。

但是此时还有两个问题:api转译是全局引入,一些helper函数也是会在编译时重复生成,例如如下代码:

这样就会生成无数重复代码,咋办呢?

此时需要@babel/plugin-transform-runtime 与@babel/runtime-corejs3,其中 @babel/plugin-transform-runtime 的作用是转译代码,转译后的代码中可能会引入 @babel/runtime-corejs3 里面的模块。所以前者运行在编译时,后者运行在运行时。类似 polyfill,后者需要被打包到最终产物里在浏览器中运行。最终代码如下:

代码语言:javascript复制
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "debug": true
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3 // 指定 runtime-corejs 的版本,目前有 2 3 两个版本
      }
    ]
  ]
}

再看转译后的代码:

从上图可以看到,在引入了 transform-runtime 这个插件后:api 从之前的直接修改原型改为了从一个统一的模块中引入,避免了对全局变量及其原型的污染,解决了第一个问题,helpers 从之前的原地定义改为了从一个统一的模块中引入,使得打包的结果中每个 helper 只会存在一个,解决了第二个问题

babel 在转译的过程中,对 syntax 的处理可能会使用到 helper 函数,对 api 的处理会引入 polyfill。

默认情况下,babel 在每个需要使用 helper 的地方都会定义一个 helper,导致最终的产物里有大量重复的 helper;引入 polyfill 时会直接修改全局变量及其原型,造成原型污染。

@babel/plugin-transform-runtime 的作用是将 helper 和 polyfill 都改为从一个统一的地方引入,并且引入的对象和全局变量是完全隔离的,这样解决了上面的两个问题。

@babel/plugin-transform-runtime需要与@babel/runtime、@babel/runtime-corejs3结合使用。

以上便是babel使用时的一些注意事项,希望对你有所帮助。

参考:https://zhuanlan.zhihu.com/p/147083132

https://segmentfault.com/a/1190000021188054

0 人点赞