Babel初识

1.什么是Babel

简单讲Babel 是 Javascript 编译器 ,将 ES6,ES7 ,ES8 转换成 浏览器都支持的ES5 语法,并提供一些插件来兼容浏览器API的工具

babel是怎么实现的呢:Babel 会将源码转换 AST(抽象语法树)之后,通过遍历AST树,对树做一些修改,然后再将AST转成code,即成源码。通俗讲就是整了个容,浏览器觉得挺漂亮的,让代码可以在不支持高版本es语法的浏览器上运行。如下:

// 转码前
input.map(item => item + 1);

// 转码后
input.map(function (item) {
  return item + 1;
});

2.Babel的设计和组成

2.1 设计理念

在这里插入图片描述
从图上可以看到,Babel 会将我们写的代码转换AST 之后,通过遍历AST 树,对树做一些修改,然后再将AST转成 code,这样我们用的 箭头函数,let,const,解构赋值才能用的舒坦。

2.2 组成

Babel 的核心是在 @babel/core 这个npm 包,围绕在它周围的分别是

  • @babel/core
    AST转换的核心
  • @babel/cli
    打包工具
  • @babel/plugin
    Babel 插件机制,Babel基础功能不满足的时候,可以用此
  • @babel/preset-env
    把许多 @babel/plugin 综合了下,减少配置
  • @babel/runtime
    把你使用到的浏览器某些不支持API,按需导入,代码少
  • @babel/polyfill
    把浏览器某些不支持API,兼容性代码全部导入到项目,不管你是不是用到,缺点是代码体积特别大
  • .babelrc/babel.config.json
    上面这些配置需要放在一个地方,.babelrc/babel.config.json就是放这些配置地方。

3.babel.config.js配置文件

3.1 简单示例

下面是.babelrc/babel.config.json示例(具体配置方法可以有很多种,可以看官网):

{
    "presets": [
        ["@babel/preset-env"]
    ],
    "plugins": ["@babel/plugin-syntax-dynamic-import"],
    ]
}

项目根目录下.babelrc或者babel.config.json 是 Babel的配置文件,Babel 会自动查找并读取内容。目录结构一般如下:

.
├──src
    └──main.js
├──package.json
└──babel.config.json

下面来看一个例子,先安装依赖:

npm install --save-dev @babel/preset-env @babel/cli

配置babel:

{
    "presets": ["@babel/preset-env"],
    "plugins": []
}

@babel/preset-env的参数项数量很多,但大部分我们都用不到。我们只需要重点掌握四个参数项即可:targetsuseBuiltInsmodulescorejs。如果要使用配置参数,就需要对babel.config.js做一些简单的修改:

{
  "presets": [
    [
      // 将原来的 @babel/preset-env 字符串改成数组
      "@babel/preset-env",
      { // @babel/preset-env 的配置对象
        ...
      }
    ]
  ],
  "plugins": []
}

3.2 browserslist(目标环境配置表)

在开始讲具体配置项的作用之前,我们先说说 browserslist(目标环境配置表) ,具体 demo 如下:

"browserslist": [
    "> 1%",
    "not ie <= 8"
]

上面配置的含义是:目标环境是市场份额大于 1% 的浏览器并且不考虑 IE8 以下的浏览器。

browserslist 可以写在 package.json 中,也可以单独写在工程目录下的 .browserslistrc 文件里。我们可以用 browserslist 来指定代码最重要运行在那些浏览器或者 Node.js 环境。如下是配置在package.json里面的browserslist示例:
在这里插入图片描述

browserslist 比较常见的一个作用就是帮助 Autoprefixer、postcss 判断是否要增加 CSS 前缀(例如 -webkit-)。我们的 Babel 也会用到 browserslist,当我们使用了 @babel/preset-env 这个预设,那么在 Babel 进行代码转换的过程中,就会读取 browserslist 的配置,从而按目标环境进行相应的转换。

如果我们的 @babel/preset-env 不设置任何参数,Babel 就会完全根据 browserslist 的配置来做语法转换。如果没有 browserslist ,那么 Babel 就会把所有 ES6 的语法转换成 ES5 版本。如下是一个例子:

@babel/preset-env 无配置且不配置 browserslist:如图所示,我们定义了一个箭头函数,babel 最后会将其转换为 ES5 语法的普通函数。
在这里插入图片描述

假如我们配置 browserslist:
在这里插入图片描述
当我们制定了 chrome 60 版本以后,babel 并没有转换箭头函数,因为这个版本的 chrome 本身就支持箭头函数:
在这里插入图片描述

3.3 @babel/preset-env中的配置参数

3.3.1 targets

该参数项可以取值为字符串、字符串数组或对象,不设置的时候取默认值空对象{}。

该参数项的写法与 browserslist 是一样的,如果我们对 @babel/preset-env 的 targets 参数项进行了设置,那么就不使用 browserslist 的配置,而是使用 targets 的配置。如不设置 targets ,那么就使用 browserslist 的配置。

3.3.2 useBuiltIns

useBuiltIns 项取值可以是usage、 entry 或 false。如果该项不进行设置,则取默认值 false。

  • false
    啥也不干
  • entry
    必须在项目中主动引入@babel/polyfill,打包后会把所有的 polyfill 都引入
  • usage
    main.js 主动引入 @babel/polyfill;打包后只会把用到的 polyfill 引入

3.3.3 corejs

这一块可以看看官网。

该参数项的取值可以是 2 或 3,没有设置的时候取默认值为 2。这个参数只有 useBuiltIns 参数为 usage 或者 entry 时才会生效。

因为某些新的 API 只有 core-js@3 里才有,例如数组的 flat 方法,我们需要使用 core-js@3 的 API 模块进行补齐,这个时候我们就把该项设置为 3。

当我们将 corejs 配置为 2 或者使用默认配置时,需要安装 core-js@2 并引入,也可以安装并引入 @babel/polyfill(内部引入了 core-js2,又集成了 regenerator-runtime)。 如果我们的代码中使用了,生成器函数(Generator Functions)、async、await ,我们就需要引入 regenerator-runtime 模块。

从 Babel 7.4.0 开始,@babel/polyfill 软件包已被弃用, 所以还是建议使用 core-js@3。使用 core-js@3 时必须预先安装 core-js@3, 如果 useBuiltIns 是 entry, 则在入口文件引入 core-js@3 ,如果 useBuiltIns 是 usage,则不需要在入口文件引入。

3.3.4 modules

这个参数项的取值可以是 amd、umd、systemjs、commonjs、cjs、auto、false。在不设置的时候,取默认值 auto 。

在该参数项值是 auto 或不设置的时候,会发现我们转码前的代码里 import 都被转码成 require 了。

如果我们将参数项改成 false,那么就不会对 ES6 模块化进行更改,还是使用 import 引入模块。使用 ES6 模块化语法有什么好处呢?在使用 Webpack 一类的打包工具,可以进行静态分析,从而可以做 tree shaking 等优化措施。

4.presets VS plugin

这篇聊下Babel的@babel/babel-preset-* 预设插件, @babel/babel-plugin-*我下面的例子都是以 Babel7讲的。

4.1 presets是什么

presets 是 plugins 的集合,把很多需要转换的ES6的语法插件集合在一起,避免大家各种配置,比如:
在这里插入图片描述

最常用的 presets 是 @babel/preset-env, @babel/preset-env 包含了我们大部分能想到的 ES6 语法,没有的,它会在打包报错,自己加 plugins, 用过 Babel6的可能会看到 stage-0,stage-1 这样的配置,但在 Babel已经全部舍弃,具体 Babel6 和 Babel7差别,可以看看官网,有空我会再写一篇文章。
在这里插入图片描述

4.2 presets和plugins的加载顺序

在这里插入图片描述
presets 加载顺序和一般理解不一样 ,是倒序的,比方上方代码中,先加载 @babel/preset-react,再加载 @babel/preset-env。

而plugins 按照数组的 index 增序(从数组第一个到最后一个)进行编译

另外plugins 优先于 presets进行编译。

5.@babel/polyfill VS @babel/plugin-transform-runtime

5.1 @babel/polyfill

这篇主要讲polyfill和runtime。 总结下, Babel只是转换syntax层语法,所以需要@babel/polyfill来处理API兼容,又因为polyfill体积太大,所以通过presetuseBuiltIns来实现按需加载,再接着为了满足npm 组件开发的需要出现了@babel/runtime来做隔离。

下面来看一段代码:

let array = [1, 2, 3, 4, 5, 6];
array.includes(item => item > 2);
new Promise()

Babel转换后代码:

var array = [1, 2, 3, 4, 5, 6];
array.includes(function (item) {
  return item > 2;
});
new Promise()

Babel 默认只是转换了 箭头函数 let ,Promise 和 includes 都没有转换 ,这是为什么?

Babel把Javascript语法分为 syntax和 api。

  • api
    api 指那些我们可以通过 函数重新覆盖的语法 ,类似 includes,map,includes,Promise,凡是我们能想到重写的都可以归属到 api
  • syntax
    像 箭头函数,let,const,class, 依赖注入 Decorators,等等这些,我们在 Javascript 在运行是无法重写的,想象下,在不支持的浏览器里不管怎么样,你都用不了 let 这个关键字。

Babel只负责转换 syntax , 而includes,map,includes这些 API层面的则交给polyfill 这个模块单独处理。

polyfill直译是垫片的意思,处理类似 assign,includes,map,includes。某些浏览器如果没有这些方法,最直接的办法的是根据一份浏览器不兼容的表格(这个browserslist已经完成了),把对应浏览器不支持的语法全部重新写一遍,类似下面这样:

// 
  if (typeof Object.assign != 'function') {
      Object.defineProperty(Object, "assign", 
      ·····
  }
  if (!Array.prototype.includes){
     Object.defineProperty(Array.prototype, 'includes',
      ·····
  }
  if (!Array.prototype.every){
     Object.defineProperty(Array.prototype, 'every',
      ·····
  }
  .....好多好多

这种方式可以简单粗暴的解决兼容性问题, 那问题也来了,这样会导致 polyfill.js 这个包非常大。

于是大佬们想到了按需加载。用到了includes,Babel就只引入 includes 的对应的处理代码,按需加载。

这个就需要用到上面3.3.2说到的@babel/preset-env的useBuiltIns 属性了,不了解 @babel/preset-env 看下上篇 useBuiltIns 有 false,entry,usage 三个属性。

,使用entry配置时候,需要手动在src/main.js 中手动@babel/polyfill:
在这里插入图片描述
在package.json中的script配置打包命令:
在这里插入图片描述
执行npm run build后在dist文件夹下新生成一个bundle.js打包文件。该文件把@babel/polyfill里面所有内容都引入了:
在这里插入图片描述
这样子体积太大了。

@babel/preset-envuseBuiltIns改为usage,并且删除src/main.js引入@babel/polyfill的的代码:

在这里插入图片描述
执行npm run build后在dist文件夹下新生成一个bundle.js打包文件。由于usage是按需加载,所以bundle.js的文件中只用导入几个文件:
在这里插入图片描述

5.2 @babel/plugin-transform-runtime

polyfill问题是已经解决了,现在又出现了一个场景, 假设你是开发一个 npm组件的选手,你刚好用到了 Promise 方法, 按照上面的方法,写完发布到npm仓库,现在隔壁印度小哥刚好搜到你这包,下载下来了,但是他的项目里面也用到了 Promise ,但是他的Promise是自定义 一套,类似:

window.Promise  = function (){
    this.reject = ..
    this.resolve = ..
}

这个时候就傻眼了,小哥项目跑不起来了。,这个场景其实很常见,那这么办呢?

假设在开发组件的时候能报把所有的Promise 拷贝(copy)到 _Promise对象上,然后组件里都用_Promise ,就和外界做了层隔离,互不影响。这个思路我们在开发设计中也是可以学习套用的,Babel 里面针对这种场景出现了@babel/runtime, @babel/plugin-transform-runtime ,首先 执行下,这2个一个都不能少,都是必须的:

npm install --save @babel/runtime
npm install --save-dev @babel/plugin-transform-runtime

配置babel.config.json:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage"
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 2
      }
    ]
  ]
}

执行npm run build后在dist文件夹下新生成一个bundle.js打包文件。打包文件内容如下:
在这里插入图片描述

5.3 总结

  • Babel只是转换syntax层的语法
  • 所以需要@babel/polyfill来处理API兼容;又因为polyfill体积太大,所以通过presetuseBuiltIns来实现按需加载
  • 再接着为了满足npm组件开发的需要 出现了@babel/runtime来做隔离

Polyfill‌和Runtime‌的区别:

  • Polyfill‌:
    Polyfill主要用于解决浏览器不支持的ES6新特性问题。它通过向全局对象和内置对象的prototype上添加方法来实现兼容性。例如,如果运行环境中不支持Array.prototype.find方法,引入polyfill后就可以使用ES6方法来编写代码,但缺点是会造成全局空间污染‌。‌
  • Runtime‌:
    Runtime(如babel-runtime)则是将ES6代码编译成ES5代码来执行。它不会污染全局对象和内置对象的原型
04-30
### Babel JavaScript 编译工具使用指南 Babel 是一种用于将 ECMAScript 2015+ (ES6+) 转换为向后兼容版本的 JavaScript 工具链[^1]。通过这种转换,开发者可以在当前和较老的浏览器或运行环境中安全地使用最新语言特性。 #### 安装 Babel 为了使用 Babel 进行代码转换,需要先安装核心依赖项 `@babel/core` 和命令行接口 `@babel/cli`。可以通过包管理器如 npm 或 pnpm 来完成这一操作: ```bash pnpm add --save-dev @babel/core @babel/cli ``` 此命令会将必要的模块下载到项目的开发依赖中[^4]。 #### 配置文件设置 Babel 支持多种配置方式,最常见的是创建 `.babelrc` 文件或者在 `package.json` 中定义 Babel 的选项。以下是基于 `.babelrc` 的基本配置示例: ```json { "presets": ["@babel/preset-env"], "plugins": [] } ``` 在此配置中,`@babel/preset-env` 是一个预设集合,允许自动决定所需的语法转换以及 Polyfill 插入,从而适配目标环境的要求[^2]。 #### 命令行编译 一旦完成了上述准备工作,就可以利用 Babel CLI 对单个文件或多组文件执行编译任务。例如,假设有一个名为 `input.js` 的源码文件,则可通过如下指令将其转化为适合旧版浏览器使用的格式并保存至 `output.js`: ```bash npx babel input.js --out-file output.js ``` 如果项目目录中有多个待处理脚本,还可以指定整个文件夹作为输入路径,并生成对应的输出结构: ```bash npx babel src --out-dir dist ``` 以上命令将会把位于 `src` 下的所有符合条件的 JS 文件逐一经过 Babel 处理后再存放到同名布局下的 `dist` 子目录里[^4]。 #### 自定义插件扩展功能 除了内置的能力外,Babel 更强大的地方在于它的可插拔架构设计。借助社区贡献的各种第三方插件,可以进一步增强其灵活性与适应范围。比如针对特定需求编写自定义逻辑来调整 AST 表达形式等等。关于如何构建个人专属插件的具体教程,请参阅官方提供的《Babel 插件手册》[^3]^。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值