指标
本文的目标是帮忙对Babel不相熟的人理解 如何书写Babel的配置文件,设计一个“最完满”的 Polyfill 计划
因为内容比拟多,我会把上面的目录中的内容分成四个章节去解说
第一章节
- 什么是Babel
- 插件和预设
- 配置文件
- 简略Demo演示
第二章节
- core-js
第三章节
- 什么是Polyfill
- @babel/polyfill介绍
- @babel/preset-env实战
第四个章节
- @babel/runtime实战
- @babel/plugin-transform-runtime实战
- @babel/core介绍
- @babel/cli介绍
文章中展现的demo都会增加源码链接
├── README.md├── lerna.json├── package.json└── packages ├── tutor-basic ├── tutor-polyfill01 ├── tutor-polyfill02 ├── tutor-polyfill03 ├── tutor-polyfill04 ├── tutor-polyfill05 ├── tutor-preset_env ├── tutor-preset_env02 ├── tutor-preset_env03 ├── tutor-preset_env04 ├── tutor-preset_env05 ├── tutor-preset_env06 ├── tutor-preset_env07 ├── tutor-runtime01 ├── tutor-runtime02 ├── tutor-runtime03 └── tutor-runtime04
前言
本章节内容只有介绍一下 老成长谈货色——Babel的一些概念,思考以下问题:
须要记住的内容:
- 为什么要编译?
- 插件和预设的区别和关系?
- 插件和预设解析程序?
- 没有配置预设和插件转换后果是什么?
- 指标环境配置表是什么货色?
什么是Babel
Babel is a JavaScript compiler.
Babel是一个Javascript编译器。Babel官网解释很简略,然而想要真正理解Babel的确不简略。
为什么要编译转换
究其原因,前端语言个性(esnext、typescript、flow等)和宿主环境(浏览器/Node.js)高速倒退,宿主环境无奈及时反对新的语言个性。因而,须要把新的语言个性降级解决,转换成指标环境反对的语法,并且对指标环境不反对的API增加Polyfill
Babel对代码降级解决,能够概括为两局部:
- 语法转换——例如:箭头函数语法、async函数语法、class定义类语法、解构赋值等。
- 补齐新的API——例如:
Array.prototype.includes
,String.prorotype.includes
,Promise
、Map
、Symbol
等
Babel的编译流程
Babel是源代码
到指标环境源代码
的转换,依然是高级语言到高级语言的转换,整个过程可分为三个阶段:
- 解析(parse)—— 通过
@babel/parser
把源代码字符串转成形象语法树(AST) - 转换(transform)——通过
@babel/traverse
遍历形象语法树(AST),并调用Babel配置文件中的插件,对形象语法树(AST)进行增删改 - 生成(generate)——通过
@babel/generator
把转换后的形象语法书(AST)生成指标代码
Babel官网的插件和预设的数量十分多,不过罕用的插件和预设只有几个,接下来重点解说这几个插件和预设,其余的能够触类旁通,举一反三。
插件(Plugin)
在转换的过程中,Babel插件会对形象语法树(AST)进行增删改,如果不给Babel装上插件,形象语法树将会不变,代码就会原样输入。正是因为有插件的存在, Babel能力对形象语法书(AST)进行增删改,继而产生指标源代码。
插件分类
Babel插件大抵分为以下两种:
- 语法插件(syntax plugin)——作用于
@babel/parser
,解析特定类型的语法,将代码解析为形象语法树(AST)。jsx
,flow
- 转换插件(transform plugin)—— 负责对形象语法树进行增删改
语法插件虽名为插件,但其自身并不具备功能性。语法插件所对应的语法性能其实都已在@babel/parser
里实现,插件的作用只是将对应语法的解析性能关上。
转换插件会主动启用相应的语法插件。因而,如果曾经应用了相应的转换插件,则无需指定语法插件。是不是对这两种插件的区别有点懵逼?不必放心,持续往下看。
下文提及的 Babel 插件将专指转换插件
Babel@7官网有90多个插件,不过大半曾经整合在@babel/preset-env
、@babel/preset-react
、@babel/preset-typescript
等预设里了,个别状况下,咱们在开发的时候间接应用预设就能够了。
预设是什么?
预设(Presets)
插件(plugin)只是对单个性能进行转换,当配置的插件比拟多的时候,就能够封装成预设(Presets),来简化插件(Plugins)的应用的,预设即一组事后设定的插件。
目前Babel官网举荐的preset,有上面四个:
@babel/preset-env
所有我的项目都会用到的@babel/preset-react
react框架须要的@babel/preset-flow
flow须要的。Flow 是一个动态类型检测工具,进行类型查看,相似于ts@babel/preset-typescript
typescript须要的
其它的preset,如在Babel@6的时代,常见的babel-preset-es2015、babel-preset-es2016、babel-preset-es2017、babel-preset-latest、babel-preset-stage-0、babel-preset-stage-1、babel-preset-stage-2 等这些从Babel@7开始曾经不举荐应用了
插件程序
如果两个转换器都拜访同一个“ Program ”节点,则转换器会依照以下程序运行
- 插件在 Presets 前运行。
- 插件能够指定从头到尾的程序(数组坐标0权重最大)。
- Preset 程序是相同的 (从后到前).
例如:
{ "plugins": [ "transform-decorators-legacy", "transform-class-properties" ]}
将会运行 transform-decorators-legacy 而后是 transform-class-properties
对于 presets 肯定要记住,程序是相同的。如下:
{ "presets": ["@babel/preset-env", "@babel/preset-react"]}
按以下程序运行:@babel/preset-react 再运行 @babel/preset-env。
2.接下来咱们会学习@babel/polyfill
、@babel/preset-env
、@babel/plugin-transform-runtime
与等内容
从配置文件内容方面辨别,Babel提供了两种配置内容。一种是由js 编写,通过module.exports={}形式输入对象。例如Vue的Babel配置文件内容
const babelPresetFlowVue = { plugins: [ require('@babel/plugin-proposal-class-properties'), // require('@babel/plugin-syntax-flow'), // not needed, included in transform-flow-strip-types require('@babel/plugin-transform-flow-strip-types') ]}module.exports = { presets: [ require('@babel/preset-env'), // require('babel-preset-flow-vue') babelPresetFlowVue ], plugins: [ require('babel-plugin-transform-vue-jsx'), require('@babel/plugin-syntax-dynamic-import') ], ignore: [ 'dist/*.js', 'packages/**/*.js' ]}
这种有js编写的文件后缀都是.js
。比方babel.config.js
、.babelrc.js
。
另外一种是间接书写 json内容 。例如 Element-UI的配置文件
{ "presets": [],//省略了内容 "plugins": ["transform-vue-jsx"]}
此时文件后缀个别为.json或者没有 例如:babel.config.json
、.babelrc
两种类型的文件,个别都会放到我的项目根目录上面
或者同package.json
同目录。如果是一个由Lerna 治理的Monorepo类型的我的项目,这两种形式会有一些区别。如下
指标环境配置表
大家可能在我的项目的根目录见过这么一个文件.browserslistrc
,或者在package.json
中见browserslist
字段吧?
他的中文名字叫指标环境配置表,他的用户很大,须要引起足够的器重。
例如:Autoprefixer
和Babel
都是依据指标环境配置表提供的的指标浏览器的环境来,主动的增加css前缀,js的polyfill垫,而不是无脑的增加兼容,以进步代码的编译品质。
艰深的说,指标环境配置表通知babel
指标浏览器有哪些,如果指标浏览器不反对某种API或者语法,babel
会主动解决。
怎么批改和配置指标环境配置表呢?答案是看官网文档 browserslist ,或者更简略的办法去看一下vue-cli
或create-react-app
生成的我的项目外面怎么配置的。理解一下字段的意思,用到的时候在去改。
如果我的项目中没有配置指标环境配置表 browserslist,能够指定默认配置
配置文件类型:
Babel 有两种并行的配置文件形式:
- 我的项目范畴的配置(Project-wide)
文件相干的配置(File-relative)
.babelrc
(和.babelrc.js
)文件- 带有 "babel" 键的 package.json 文件
Version | Changes |
---|---|
v7.8.0 | 反对 .babelrc.mjs , babel.config.mjs |
v7.7.0 | 反对 .babelrc ,.babelrc.json , .babelrc.cjs , babel.config.json , babel.config.cjs |
两种配置文件的区别,如果不明确能够临时略过,不影响前面的学习,最初完结之后,在回来认真斟酌
我的项目范畴的配置
Babel 7.x 中的新性能,Babel 具备 "root" 目录的概念,"root"目录默认为以后的工作目录
。编译时,Babel
将主动搜寻绝对于此根目录下的babel.config.js
文件,或其Babel认可的文件,比方:babel.config.json
,babel.config.cjs
,babel.config.mjs
等
长处
它们是十分适合并值得广泛应用的配置,它甚至容许plugins
和 presets
能够轻松利用于node_modules
或符号链接包中的文件
毛病
因为它依赖于工作目录,如果一个我的项目属于Monorepo类型我的项目,当工作目录不是的根目录(root),编译时就找不到配置文件,如此一来在Monorepo
中应用会比拟苦楚。
babel.config.jspackage.jsonpackages/ mod1/ package.json src/index.js mod2/ package.json src/index.js
各个子模块构建的时候,用户将须要通过rootMode
手动设置它的门路,以此来加载babel.config.js
文件
CLI
babel --root-mode upward src -d lib
Webpack
module: { rules: [ { loader: "babel-loader", options: { rootMode: "upward", }, }, ];}
具体内容能够参考官网文档的演示
文件相干配置
编译时,Babel 从 正在被编译的 文件 所在的 目录开始 去搜寻 .babelrc.json
或其余Babel认可的配置文件。比方:babelrc
,.babelrc.js
,/package.json#babel
。 有了这个性能,就能够为 package的子模块 创立独立的配置。
同时,文件相干配置
和我的项目相干配置
能够独特应用。上面是官网的解释:
File-relative configurations are also merged over top of project-wide config values, making them potentially useful for specific overrides, though that can also be accomplished through "overrides".
艰深的翻译就是以下两点:
- 不同的配置:文件相干配置 和 我的项目相干配置 能够合并到一起
雷同的配置:文件相干配置 会笼罩 我的项目相干配置
babel.config.jspackage.jsonpackages/mod1/ package.json src/index.js .babelrcmod2/ package.json src/index.js .babelrc
<!--
- @Desc:
- @FilePath: /tutor-babel/docs/md/start.md
- @Author: liujianwei1
- @Date: 2021-05-14 16:43:54
- @LastEditors: liujianwei1
- @Reference Desc:
-->
理论我的项目中,Babel须要工程化合作,须要和各种工具(如Webpack)相互配合,因为Babel肯定是宏大简单的。
这里先配置一个最简略的Babel编译工程,相熟一下整个过程。
源码地址
Demo代码地址:tutor-basic 传送门
装置
创立 tutor-basic
我的项目。 装置npm包
npm install --save-dev @babel/core @babel/cli @babel/preset-env
配置和编译
step1:在根目录上面 新建一个.babelrc
文件(或babel.config.js
,.babelrc.js
)。增加如下内容
{ "presets": ["@babel/preset-env"]}
step2:在package.json
文件的 scripts
外面增加如下配置
"scripts": { "build": "babel src/index.js --out-file dist/index.js" }
step3:在新建 lib/index.js
文件,并增加如下内容
let number1 = 10let number2 = 20const sum = (num1, num2) => num1 + num2
step4:运行命令
npm run build#或者yarn build#或npx babel src/index.js --out-file dist/index.js
step5:查看编译后的后果
"use strict";var number1 = 10;var number2 = 20;var sum = function sum(num1, num2) { return num1 + num2;};
以上展现了一个最简略的Babel应用工程,输入后果和Babel介绍的例子后果是一样的
其余
- @babel/core: Babel实现转换的外围,它能够依据配置(
.babelrc
)中的规定,进行源码的转换,提供根底的转换能力 - @babel/cli: Babel提供的命令行性能,依赖
@babel/core
,在终端中通过命令行形式运行,编译文件或目录。 - @babel/preset-env 容许
去配置
须要反对的指标环境
,提供ES6转换ES5的语法转换规则。如果不应用它,也能够实现转换,然而转换之后仍旧是E66,说白了就是没有转换。 - .babelrc Babel配置文件,编译时,默认会寻找以后我的项目根目录上面的配置文件。转换规则都写在这外面。前面会具体介绍