关于babel的一些总结

·  阅读 215

前言

项目中看到了一堆babel包,但是不知道这些babel包各自的作用到底是啥,因此想梳理一下这些babel包的用法,了解各自的原理,确认是否有必要引入该babel包。

本文通过babel + webpack来介绍babel。

babel是啥?

官网中就可以知道

babel是JavaScript的编译器

它主要的作用是以下三点:

  1. 转换语法
  2. 为目标环境中缺失的语法提供垫片(Polyfill)
  3. 源码转换(模块代码)

目录结构

|- node_modules
|- src
	- index.js
|- .browserslistrc
|- babel.config.js
|- index.html
|- package.json
|- webpack.config.js
复制代码

ES6转ES5

有三个包密不可分,@babel/core@babel/preset-envbabel-loader

babel-loader

作用:该包允许使用babelwebpack编译JavaScript文件

因此我们可以在webpack.config.js文件中配置

module.exports = {
    // ...
    module: {
    	rules: [
        	{
            	test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            }
        ]
    }
}
复制代码

@babel/core

有了babel-loader还不够,从babel-loader源码中可以发现,babel-loader包中依赖了@babel/core,如下图所示:

那这个包到底是干啥用的呢?

把 js 代码分析成 ast(Abstract Syntax Tree,抽象语法树) ,方便各个插件分析语法进行相应的处理。有些新语法在低版本 js 中是不存在的,如箭头函数,rest 参数,函数默认值等,这种语言层面的不兼容只能通过将代码转为 ast,分析其语法后再转为低版本 js。

总结:把js代码转换成AST,便于插件分析。

@babel/preset-env

有了以上两个包还不够,我们需要确定我们的目标浏览器来判断是否需要转换代码,因此还需要@babel/preset-env,它的作用是:

@babel/preset-env是一个智能预设,它使你可以使用最新的JavaScript,而无需微观管理目标环境所需的语法转换(以及可选的浏览器polyfill)。这都既使工作更加方便,又能让JavaScript包更小。

因此我们可以在babel.config.js中配置

module.exports = {
    presets: [
    	["@babel/preset-env"]
    ]
}
复制代码

同时在.browserslistrc指定目标浏览器,更多配置请查看github

babel会根据目标浏览器判断是否需要把新的语法转成旧的语法

> 1%
last 2 versions
ie >= 11
chrome >= 49
复制代码

然后在index.js中写一段包含ES6语法的代码,如:

const box = document.createElement('div');
box.innerText = 'Hello world!!!';
document.body.appendChild(box);
复制代码

然后使用webpack打包就能发现const转换成了var,实现了ES6转换ES5。

还有一个问题,为啥我配了babel.config.js就能作用于webpack打包呢?

从源码中可以发现,在@babel/core中会读取babel.config.js文件(如下图),通过对文件内的配置对JS代码进行转换

配置查看babel

  • target:目标浏览器
  • useBuiltIns: 'entry'(入口导入) | 'usage'(按需加载) | false

ES6 API转换

但是Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API,比如Set、Map等。

因此如果在index.js书写如下代码,其中map将不会被转换

const box = document.createElement('div');
const map = new Map();
map.set('Hi', 'Hello world!!!');
box.innerText = map.get('Hi');
document.body.appendChild(box);
复制代码

那如果需要转换,如何实现?

@babel/polyfill

作用:为不支持ES6 API语法的浏览器提供polyfill(垫片),有三种导入方式:

  1. 一开始直接在index.js中导入
  2. webpack.config.js指定
module.exports = {
	//...
    entry: ['@babel/polyfill', './src/index.js']
}
复制代码
  1. 在babel.config.js中指定
module.exports = {
	presets: [
    	'@babel/preset-env',
        {
        	useBuiltIns: 'usage'
        }
    ]
}
复制代码

core.js & regenerator-runtime

从babel 7.4.0开始,babel不推荐直接使用@babel/polyfill而是直接使用core.js(为ES6提供polyfill)和regenerator-runtime(提供generator、async、await的 polyfill)

core.js介绍中可以发现根据需要引入不同的文件,例如:

import "core-js/stable"; // ES和web标准的polyfill
import "core-js/es"; // 仅包含ES的polyfill
复制代码

值得注意的是core-js中ES6支持的程度比@babel/polyfill更大,比如core-js就支持globalThis,但是@babel/polyfill不支持

不过在项目中一般不直接导入,安装了这两个包后利用@babel/preset-env相关配置就可以转换ES6 API(只安装,不导入),在babel.config.js配置如下:

module.exports = {
	presets: [
    	["@babel/preset-env", {
            useBuiltIns: "usage",
            corejs: 3 // 由corejs安装的版本抉择,如果是2.x,则不需要另外写,因为默认值是2
        }]
    ]
}
复制代码

注:最好加上@babel/plugin-transform-runtime插件,不需要创建多个相同的helper

@babel/plugin-transform-runtime & @babel/runtime-corejs3

@babel/plugin-transform-runtime:

作用:使helper和polyfill统一引入,引入的对象与全局变量完全隔离

注:

  1. helper为一些通用函数,如果不使用该插件的话,那么每一个文件遇到需要转换的代码时都会生成相应的helper,比如class的_classCallCheck函数造成多次引入
  2. API不会直接写到全局对象上,因此与全局对象隔离,这也是区别于上述的两种方法

@babel/runtime-corejs3

作用:与core.js功能类似,都能提供polyfill,其中包依赖regenerator-runtime,因此也能转换异步函数

babel.config.js配置如下:

module.exports = {
  presets: [
    [
      "@babel/preset-env"
    ]
  ],
  plugins: [
    [
      "@babel/plugin-transform-runtime",
      {
        corejs: 3
      }
    ]
  ]
};
复制代码

tips

如果在node_modules内有ES6需要转成ES5,可以在webpack.config.js中这样配置:

module.exports = {
  //...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules(?!(\/|\\)xxx)/, // 单个包
        loader: "babel-loader",
      }
    ]
  }
};
复制代码

@babel/plugin-proposal-class-properties

作用:转换class中使用static的静态属性

因为该方案是一个提案,因此需要使用插件来转换

module.exports = {
	//...
	plugins: ["@babel/plugin-proposal-class-properties"]
}
复制代码

项目中看到babel-plugin-transform-class-properties有这个包,功能和上面介绍的包功能类似,但由于该包很长时间不维护了,因此推荐使用@babel/plugin-proposal-class-properties

配置查看babel

总结

目前使用的babel包就是这些,后面如果有用到更多的babel包将会在本文补充。

参考文章

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改