babel起手式

Babel7

以下是各个 ECMAScript 版本引入的一些主要新语法和功能的汇总

ES5 / ECMAScript 5(2009年)

  1. 严格模式 "use strict"
  2. JSON 对象。
  3. Array.prototype.forEach()Array.prototype.map()Array.prototype.filter()Array.prototype.reduce() 等数组方法。
  4. Object.keys()Object.create() 等对象方法。

ES6 / ECMAScript 2015(也称为 ES2015 或 ECMAScript 6,2015年)

  1. letconst 变量声明。
  2. 箭头函数。
  3. 模板字符串。
  4. 解构赋值。
  5. 默认参数和剩余参数。
  6. 类和继承。
  7. 模块化 importexport
  8. Promise
  9. 增强的对象字面量。

ES7 / ECMAScript 2016(2016年)

  1. Array.prototype.includes() 方法。
  2. 指数操作符 **

ES8 / ECMAScript 2017(2017年)

  1. 异步函数 asyncawait
  2. Object.values()Object.entries()
  3. 字符串填充方法 String.prototype.padStart()String.prototype.padEnd()

ES9 / ECMAScript 2018(2018年)

  1. 对象扩展运算符。
  2. 异步迭代器。
  3. 正则表达式命名捕获组。

ES10 / ECMAScript 2019(2019年)

  1. Array.prototype.flat()Array.prototype.flatMap()
  2. String.prototype.trimStart()String.prototype.trimEnd()

ES11 / ECMAScript 2020(2020年)

  1. 可选链操作符 ?.
  2. 空值合并操作符 ??

ES12 / ECMAScript 2021(2021年)

  1. 数字分隔符。
  2. Promise.allSettled()
  3. String.prototype.replaceAll()

以上列举了每个版本中的一些主要语法和功能。每个版本都还包含了其他一些小的改进和新增功能。要详细了解每个版本中的所有变化,可以查阅官方的 ECMAScript 规范或其他权威资源。

解析 const和箭头函数是es6的语法


npx babel src/index.js -d dist/ --plugins=@babel/plugin-transform-block-scoping,@babel/plugin-transform-arrow-functions



本文根据babel@7 进行测试的


前言

Babel 是一个 JavaScript 编译器,主要用于将 ECMAScript 2015+版本的代码转换为向后兼容的 JavaScript 代码,以便能够在旧版本的浏览器或其他环境中运行。但是,Babel 本身是无法转换代码的,它的转换功能是通过不同的插件来实现的,Babel 插件是用于指定转换规则的工具,每个插件都可以处理不同的语法或功能转换。

作用

将某些低版本容器(主要是浏览器,主要是IE…)不支持的js语法或api,用该容器支持的语法或api重写,使开发者可以使用更前沿的方式愉快的编写代码。

但实际上更准确点说,是一堆插件在做代码的转换,babel本身是个容器,负责代码解析、转换抽象语法树,然后通过各种插件做代码转换,最后根据转换后的抽象语法树生成最终的代码。这个过程以后再细说,这里想说的就是插件对于babel的作用,而我们使用者可能比较关心的,也就是在做代码转换时,会用到哪些插件。

babel-cli官方最新版本为7.24.3

Babel 本身就是一个可以独立使用的工具,但是目前开发中很少直接使用 Babel,都是和 Webpack ,Rolllup,vite等构建工具来配合使用。

编译器的作用就是可以将一种代码转换为另一种代码。
Babel 不仅可以转换 ES6+ 的语法,也可以转换 React、TypeScript。

Babel 和 PostCSS 都是微内核架构,工具本身只编写自身核心的代码,具体的功能通过不同的插件来实现。

ES6+ 的语法通过 Babel 就可以转换,为什么还需要 Polyfill?

一些 ES6+ 的语法特性,例如箭头函数、模板字符串、常量声明等,只涉及到语法层面的改变,因此可以在不引入新的 JavaScript 对象或方法的情况下通过简单的语法转换为旧版本的 JavaScript。
但是,有些 ES6+ 特性涉及到新的 JavaScript 对象、方法或内置功能,例如 Map、Set、Symbol、Promise、async/await 等,这些特性不能通过简单的语法转换来实现,因此需要使用 Polyfill。

原理:

Babel 的工作原理整体上可以分为三个阶段:解析阶段 Parsing、转换阶段 Transformation、生成阶段 Code Generation。
1

具体来说,可以分为以下步骤:

  1. 词法分析:读取源代码文件,对文件中的每个单词做分析。其实就是读取源代码文件中的每个单词。

    2

  2. 生成 tokens 数组:将词法分析出来的单词生成 tokens 数组。其实就是对词法分析读取出来的单词进行分类。

    3

  3. 语法分析:语法分析 tokens 数组中的每一项代表什么含义。其实就是分析 tokens 数组的每一项其真正的语法表示的是什么。

  4. 生成 AST 抽象语法树:根据语法分析出来的每一项的真正含义生成 AST 抽象语法树。

    4

  5. 对 AST 抽象语法树进行遍历。

  6. 访问 AST 抽象语法树中的每个结点。

  7. 在访问结点的过程中对结点应用对应的插件,进行结点的修改。

  8. 遍历完之后就生成新的 AST 抽象语法树。

  9. 最后根据新的抽象语法树生成转化后的新的代码。

5

配置文件:

通常,需要指定 Babel 的编译规则来编译代码。Babel 的配置文件默认会在当前目录寻找文件,有:.babelrc.babelrc.jsbabel.config.jsonbabel.config.jspackage.json,它们的配置项都是一样的,作用也一样,只需要选择一种即可。

// JSON
{
  "presets": [...],
  "plugins": [...]
}

// JavaScript
const presets = [ ... ];
const plugins = [ ... ];
module.exports = { presets, plugins };

// package.json
{
  "name": "my-package",
  "version": "1.0.0",
  "babel": {
    "presets": [ ... ],
    "plugins": [ ... ],
  }
}

7

babel7主要就是两个包,@babel/cli和@babel/core,cli用于执行命令行,core则是babel用于解析、转换、代码生成的核心包。在项目下执行以下命令即可完成安装。
有了这两个包以后,就可以对指定文件执行babel命令了

@babel/core

@babel/core:Babel 的核心代码。必须安装。

```bash 
npm install --save-dev @babel/core
```

基本用法:可以在 JS 程序中直接引入并使用。

const babel = require("@babel/core");
babel.transformSync(code, optionsObject);

@babel/cli

@babel/cli :是 babel 提供的内建命令行工具。如果需要在命令行中直接使用 Babel,就需要安装 @babel/cli ,否则不需要安装。

安装命令:

 npm install --save-dev @babel/cli

基本用法:

src目录文件index.js代码如下:

const fn = () => {
    Array.isArray([1, 2, 3]);
};

现在在项目根目录下执行

npx babel src/index.js -d dist/
即可在dist目录下生成同名文件index.js,而里面代码与src/index.js中的代码完全一样,生成的dist/index.js内容如下:

const fn = () => {
  Array.isArray([1, 2, 3]);
};


之所以代码完全一样,其实就是上面所说的,babel在没有使用任何插件时,就是把代码变成抽象语法树,再把抽象语法树原封不动的变成代码,中间没有做任何处理,当然代码也就原样还原回来了。下面,让我们来给babel加点儿料~

Plugins

const和箭头函数是es6的语法,相应的,@babel/plugin-transform-block-scoping插件用于转换const和let,@babel/plugin-transform-arrow-functions插件用于转换箭头函数。安装完这两个插件之后,分别执行:

@babel/plugin

@babel/plugin*:代码转换功能以插件的形式出现,插件是小型的 JS 程序,用于指导 Babel 如何对代码进行转换。

安装命令:

npm install --save-dev @babel/plugin-transform-arrow-functions // 将箭头函数转换为 ES5 兼容的函数表达式的插件

基本用法:

npx babel src/index.js -o dist/index.js  --plugins=@babel/plugin-transform-arrow-functions // 多个插件之间用逗号隔开


扫完一眼plugins列表,估计和我一样一脸懵逼,这么多插件谁能记得住用得到哪些啊,babel能帮忙整理下打个包给我用么?当然可以,presets就是用来干这事儿的。

Presets

一个特定的preset可以简单理解为是一组特定的plugins的集合。不同的presets包含着不同的plugins,当然适用的场景也就各不相同了。比如@babel/preset-react包含了写react需要用到的@babel/plugin-syntax-jsx,@babel/plugin-transform-react-jsx,@babel/plugin-transform-react-display-name等插件;@babel/preset-es2017包含了@babel/plugin-transform-async-to-generator插件。

而最为常用,也是被官网推荐的,是@babel/preset-env。默认情况下,所有已被纳入规范的语法(ES2015, ES2016, ES2017, ES2018, Modules)所需要使用的plugins都包含在env这个preset中。

还是以上面例子来说
先安装@babel/preset-env

@babel/preset

@babel/preset-env:是 Babel 提前预设好的一系列 Babel 中的插件的组合。如果要使用的 Babel 插件很多,一个一个安装配置是很麻烦的,就可以使用 Babel 中的预设。

plugins 插件数组的执行顺序是从左到右执行的,presets 预设数组的执行顺序是从右到左执行的。
插件 plugins 在预设 presets 之前执行。
安装命令:

npm install --save-dev @babel/preset-env // 在代码中使用了多种 ES6+ 的语法,一个一个地使用插件很麻烦,就可以使用一个名称为 env 的 preset


基本用法:

npx babel src/index.js -o dist/index.js --presets=@babel/env

@babel/preset-env的参数项:

  1. target:用来设定目标浏览器。默认值为空对象。
presets: [
[
'@babel/preset-env',
{
targets: {
ie: "11"
}
}
]
]

  1. useBuiltIns:用来设置预设以什么样的方式来使用 Polyfill。默认值为 false。
    属性值有:
  • false:不使用 Polyfill。
  • usage:无需手动引入 Polyfill。代码中需要哪些 Polyfill,Babel 会自动按需加载需要的 Polyfill。
  • entry:需要在入口文件中手动引入 Polyfill。只要是目标浏览器需要的 polyfill,且包含在入口文件引入的 core-jsregeerator-runtime包中,不管代码中是否需要都全部引入。

使用 entry 属性值时,还需要在打包的入口文件处引入 core-js 和 regenerator-runtime。css // 不是直接引入 core-js。而是引入 core-js/stable,表示引入 core-js 中成为标准的那些部分;也可以引入 core-js 中某些具体的功能。 import 'core-js/stable' import 'regenerator-runtime/runtime'

  1. corejs:设置 corejs 的版本。默认值为 2。

corejs 的属性值需要与安装的 core-js 的版本能对应上,否则会报错。corejs 取值为 2 的时候,需要安装并引入 core-js@2 版本;如果 corejs 取值为3,必须安装并引入 core-js@3 版本。

  1. modules:用来设置是否把 ES6 的模块化语法改成其它模块化语法。默认值为 auto,可以取值amd、umd、systemjs、commonjs、cjs、auto、false。

  2. debug:如果需要在命令行打印加载项,可以设置 debug。

主体部分与同时使用两个plugins是完全一样的。实际上,presets可以理解为就是把其包含的plugins依次执行一遍。
当然env这个presets不是万能的,其只包含了规范中的语法转换,尚未被纳入规范的处于各个阶段的提案,比如目前处于stage-2(draft)阶段的装饰器语法,光是用presets是不会帮我们转好的,还得单独再使用@babel/plugin-proposal-decorators这个专门用于转换装饰器代码的插件。

值得一提的是,babel7明确指出用stage-x命名的presets已被弃用。具体原因见

关于stage-x各代表什么含义?

在 Babel 中,stage-x 表示 ECMAScript 提案的不同阶段,其中 x 代表阶段的编号,从 0 到 4。这些阶段表示了 ECMAScript 规范中新功能的开发进度,以及这些功能被社区接受和推动的程度。具体解释如下:

  1. Stage 0 - Strawman(稻草人阶段)

    • 提案处于初始阶段,只是一个想法或者概念,尚未形成正式的提案。
  2. Stage 1 - Proposal(提案阶段)

    • 提案已经提交到 TC39(ECMAScript 标准的技术委员会)进行讨论,并且已经得到了初步的认可,但尚未成为官方的 ECMAScript 提案。
  3. Stage 2 - Draft(草案阶段)

    • 提案已经在 TC39 中被正式接受,成为了 ECMAScript 的草案,并且开始详细讨论和规划实现细节。
  4. Stage 3 - Candidate(候选人阶段)

    • 提案已经成为了 ECMAScript 的候选人,表示它已经被确定为规范的一部分,并且可以进入到浏览器和 JavaScript 引擎中进行实现和测试。
  5. Stage 4 - Finished(完成阶段)

    • 提案已经经过了所有必要的评审和测试,并且已经被确定为规范的一部分,可以作为下一个 ECMAScript 版本的一部分发布。

因此,stage-x presets 表示一组转换规则,用于处理处于不同阶段的 ECMAScript 提案功能。这些 presets 包含了针对各个阶段提案的转换规则,可以帮助开发者在项目中使用尚未正式纳入规范的 JavaScript 新特性。然而,由于语义上的不清晰以及规范的变化,Babel 在版本 7 中废弃了以 stage-x 命名的 presets,并推荐开发者直接使用具体的插件来处理特定的 ECMAScript 提案功能。

https://babeljs.io/blog/2018/07/27/removing-babels-stage-presets

如果希望和之前一样使用处于各阶段的提案功能,建议直接通过引入相应的plugins:

{
  plugins: [
    // Stage 0
    "@babel/plugin-proposal-function-bind",

    // Stage 1
    "@babel/plugin-proposal-export-default-from",
    "@babel/plugin-proposal-logical-assignment-operators",
    ["@babel/plugin-proposal-optional-chaining", { loose: false }],
    ["@babel/plugin-proposal-pipeline-operator", { proposal: "minimal" }],
    ["@babel/plugin-proposal-nullish-coalescing-operator", { loose: false }],
    "@babel/plugin-proposal-do-expressions",

    // Stage 2
    ["@babel/plugin-proposal-decorators", { legacy: true }],
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions",

    // Stage 3
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    ["@babel/plugin-proposal-class-properties", { loose: false }],
    "@babel/plugin-proposal-json-strings",
  ],
}


这些插件会将对应阶段的语法特性转换为向后兼容的代码,以确保它们可以在当前浏览器环境中正常运行。通过配置这些插件,你可以在项目中使用最新的 ECMAScript 语法特性,而不必担心浏览器的兼容性问题

@babel/ployfill

Polyfill可以理解为是补丁,其实就是实现某些功能或特性的 JavaScript 脚本。有一些语法特性浏览器是不认识的,使用的话必然报错(例如:Promise、Generator、Symbol 等,以及一些实例方法 Array.prototype.includes 等),此时就可以使用 Polyfill 来打一个补丁,将相关功能的代码包含进项目中。
Polyfill 并不是 Babel 特有的功能,只不过此处使用的是 Babel 中的 Polyfill。

@babel/polyfill:由 core-jsregenerator-runtime 组成。

  • 前者是 JS 标准库,包含不同版本 JavaScript 语法的实现;
  • 后者是 facebook 开源库,用来实现对 Promise、Generator、async/await 函数等的支持。

Babel 7.4.0 之前,是使用 @babel/polyfill 包,但是该包现在已经不建议使用了,因为 @babel/polyfill 把两个 npm 包全部都引入到了打包后的文件里了,导致打包后的体积过大;而且@babel-polyfill 可能会污染全局变量,给很多类的原型链上都作了修改,这就有不可控的因素存在。

Babel7.4.0 之后,建议单独使用core-jsregenerator-runtime

安装命令:

npm install --save core-js regenerator-runtime

基本用法:

import "core-js/stable";
import "regenerator-runtime/runtime";

如果您正在将生成器或异步函数编译为 ES5,并且您使用的 @babel/core 或 @babel/plugin-transform-regenerator 版本早于 7.18.0 ,则还必须加载 regenerator runtime 包。

如果确切地知道所需要的功能,可以只引入使用的方法。

import 'core-js/features/array/from'; 
import 'core-js/features/set';      
import 'core-js/features/promise';

@babel/runtime

@babel/runtime:可以用于提取公共函数,但是提取出来之后,代码里不会自动引用这些公共函数。
@babel/plugin-transform-runtime可以自动引用公共函数。

例如:使用 class 类的时候,Babel 会在前面添加一个帮助函数。

// 源代码
class Test {}

// 编译后的代码
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Test = function Test() {
  _classCallCheck(this, Test);
};


在编译后的代码里,帮助函数被直接迁入到代码中了。如果有多个脚本文件,每个都用到了 class,那么每个编译后的文件都包含一个一模一样的帮助函数,造成了冗余。可以使用@babel/runtime@babel/plugin-transform-runtime来解决:

  1. 安装依赖:npm i --S @babel/runtime @babel/plugin-transform-runtime

  2. 在 Babel 配置文件中进入如下配置:

plugins: [
 '@babel/plugin-transform-runtime'
]

  1. 再次编译,可以看到不再是直接把帮助函数迁入代码中,而是使用 require 加载公共帮助函数。
"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var Test = function Test() {
  (0, _classCallCheck2.default)(this, Test);
};


鄙人觉得 7.18.0以后就不用这些运行时包 regenerator-runtime @babel/runtime

@babel/register

@babel/register 提供了动态编译,换句话说,源代码能够真正运行在生产环境下,不需要 babel 编译这一环节了。但是动态编译会导致程序在速度、性能上有所损耗。

安装:

npm install --save-dev @babel/register

然后在入口文件引入:


require('@babel/register')
require('./app')


在入口文件头部引入 @babel/register 后,在 app 文件中即可使用任意 ES2015 的特性。

@babel/node

@babel-register 提供动态编译,能够让源代码真正运行在生产环境下,但仍需要做部分调整,比如新增一个入口文件,并在该文件中 require(‘@babel/register’);而 @babel-node 能真正做到一行源代码都不需要调整:


npm install --save-dev @babel/core @babel/node

npx babel-node app.js

使用 Babel

  1. 新建一个文件夹 test-babel。

  2. 在终端进入这个文件夹下,执行 npm install --save-dev @babel/core @babel/cli 安装 Babel。

  3. 在 test-babel 文件夹下新建 src/index.js,并编写一些 ES6+ 的新语法。

  4. 在终端执行 npx babel src/index.js -o dist/index.js,会自动生成 dist/index.js。但此时,dist/index.js 的代码和 src/index.js 的代码并没有太大区别,因为现在所安装的 Babel 只是一个有命令行的空壳而已,还需要为 Babel 添加一些插件来让 babel 工作。

  5. 在终端执行 npm install --save-dev @babel/preset-env 来安装 @babel/preset-env 插件。

  6. 在根目录下新建 babel.config.js 的配置文件进行配置,告知 Babel 来使用插件。


module.exports = {
  presets: [
    [
      '@babel/preset-env'
    ]
  ]
};

  1. 在终端再次执行 npx babel src/index.js -o dist/index.js,会发现 dist/index.js 已经转换为 ES5 的语法了。

  1. 文件 a.js .babelrc

a.js

import 'core-js/stable'
import 'regenerator-runtime/runtime'
class A{

}

.babelrc

{"presets": [
  [
    "@babel/preset-env",
    {
      "targets": {
        "ie": "11"
      },
      "useBuiltIns":"entry",
      // "corejs":3 
    }
  ]
]

}


“corejs”:3 如果配置这个 ,那么这些能让新的语法实现的 导入就会打包到文件中 说白啦就是 core-js/stable的内部文件会一起打包到文件里,增加代码量,不建议设置,全量引用
在这里插入图片描述

  1. a.js .babelrc

a.js

class A{

}

.babelrc

{"presets": [
  [
    "@babel/preset-env",
    {
      "targets": {
        "ie": "11"
      },
      "useBuiltIns":"usage",
      //"corejs":3

    }
  ]
],
  "plugins": [
    // "@babel/plugin-transform-arrow-functions",
    ["@babel/plugin-transform-runtime",{
      "corejs": "3"
    }]
  ]

}


自动导入 和 按需
在这里插入图片描述

在这里插入图片描述

项目开发

useBuiltIns使用usage,尽量使用社区广泛使用的优质库以优化打包体积,不使用暂未进入规范的特性。plugin-transform-runtime只使用其移除内联复用的辅助函数的特性,减小打包体积。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        // targets 官方推荐使用 .browserslistrc 配置
        "useBuiltIns": "usage",
        "corejs": {
          "version": 3,
          "proposals": false
        }
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": false // 默认值,即使如此依然需要 yarn add @babel/runtime
      }
    ]
  ]
}

并在入口文件处 import 如下内容

import ‘core-js/stable’;
import ‘regenerator-runtime/runtime’;
// 入口文件代码

类库开发

类库开发尽量不使用污染全局环境的polyfill,因此@babel/preset-env只发挥语法转换的功能,polyfill由plugin-transform-runtime来处理,推荐使用core-js@3,并且不使用未进入规范的特性。

{
  "presets": [
    [
      "@babel/preset-env",
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": {
          "version": 3,
          "proposals": true
        },
        "useESModules": true
      }
    ]
  ]
}

参考文章

Babel7-1
babel7-2

babel7-3拿到项目就是copy一下

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值