Skip to content

Latest commit

 

History

History
254 lines (186 loc) · 9.14 KB

5.babel.md

File metadata and controls

254 lines (186 loc) · 9.14 KB

babel

Babel到底是什么呢?Babel是一个工具链,主要用于旧浏览器或者缓解中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript;包括:语法转换、源代码转换、Polyfill实现目标缓解缺少的功能等;

Babel命令行使用

  • babel本身可以作为一个独立的工具(和postcss一样),不和webpack等构建工具配置来单独使用。如果我们希望在命令行尝试使用babel,需要安装如下库:
    • @babel/core:babel的核心代码,必须安装;
    • @babel/cli:可以让我们在命令行使用babel;
npm install @babel/cli @babel/core
  • 使用babel来处理我们的源代码:
    • src:是源文件的目录;
    • --out-dir:指定要输出的文件夹dist;
npx babel src --out-dir dist

插件的使用

  • 比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件:
npm install @babel/plugin-transform-arrow-functions -D

npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions
  • 查看转换后的结果:我们会发现const 并没有转成var
    • 这是因为plugin-transform-arrow-functions,并没有提供这样的功能;
    • 我们需要使用plugin-transform-block-scoping 来完成这样的功能;
npm install @babel/plugin-transform-block-scoping -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping,@babel/plugin-transform-arrow-functions

Babel的预设preset

  • 但是如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset):后面我们再具体来讲预设代表的含义;
  • 安装@babel/preset-env预设:
npm install @babel/preset-env -D
  • 执行如下命令:
npx babel src --out-dir dist --presets=@babel/preset-env

Babel的底层原理

  • babel是如何做到将我们的一段代码(ES6、TypeScript、React)转成另外一段代码(ES5)的呢?从一种源代码(原生语言)转换成另一种源代码(目标语言),这是什么的工作呢?就是编译器,事实上我们可以将babel看成就是一个编译器。Babel编译器的作用就是将我们的源代码,转换成浏览器可以直接识别的另外一段源代码;

  • Babel也拥有编译器的工作流程:

    • 解析阶段(Parsing)
    • 转换阶段(Transformation)
    • 生成阶段(Code Generation)
  • https://github.com/jamiebuilds/the-super-tiny-compiler

babel工作原理

babel-loader

我们可以设置一个规则,在加载js文件时,使用我们的babel:

{
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          // options: {
          // 预设
          //   presets: [
          //     ["@babel/preset-env", {
                   // 通过传递给预设的参数 targets 来设置目标浏览器,targets 的优先级比 .browserslistrc 文件高
          //       // targets: ["chrome 88"]
          //       // enmodules: true
          //     }]
          //   ]
          // 使用指定的插件
          //   // plugins: [
          //   //   "@babel/plugin-transform-arrow-functions",
          //   //   "@babel/plugin-transform-block-scoping"
          //   // ]
          // }
        }
      }
    ]
}

Stage-X的preset

在babel7之前(比如babel6中),我们会经常看到这种设置方式:

  • 它表达的含义是使用对应的babel-preset-stage-x 预设;
  • 但是从babel7开始,已经不建议使用了,建议使用preset-env来设置;
{
    presets: ['stage-3']
}

Babel的配置文件

像之前一样,我们可以将babel的配置信息放到一个独立的文件中,babel给我们提供了两种配置文件的编写:

  • babel.config.json(或者.js,.cjs,.mjs)文件;
  • .babelrc.json(或者.babelrc,.js,.cjs,.mjs)文件;

它们两个有什么区别呢?目前很多的项目都采用了多包管理的方式(babel本身、element-plus、umi等);

  • .babelrc.json:早期使用较多的配置方式,但是对于配置Monorepos项目是比较麻烦的;
  • babel.config.json(babel7):可以直接作用于Monorepos项目的子包,更加推荐;

polyfill

为什么时候会用到polyfill呢?

  • 比如我们使用了一些语法特性(例如:Promise, Generator, Symbol等以及实例方法例如Array.prototype.includes等)但是某些浏览器压根不认识这些特性,必然会报错;我们可以使用polyfill来填充或者说打一个补丁,那么就会包含该特性了;

  • babel7.4.0之前,可以使用@babel/polyfill的包,但是该包现在已经不推荐使用了:babel7.4.0之后,可以通过单独引入core-js和regenerator-runtime来完成polyfill的使用:

npm install core-js regenerator-runtime --save

我们需要在babel.config.js文件中进行配置,给preset-env配置一些属性:

  • useBuiltIns:设置以什么样的方式来使用polyfill;

  • corejs:设置corejs的版本,目前使用较多的是3.x的版本,比如我使用的是3.8.x的版本;

    • 另外corejs可以设置是否对提议阶段的特性进行支持;
    • 设置proposals属性为true即可;
  • useBuiltIns属性有三个常见的值

    • 第一个值:false 打包后的文件不使用polyfill来进行适配;并且这个时候是不需要设置corejs属性的;
    • 第二个值:usage 会根据源代码中出现的语言特性,自动检测所需要的polyfill;这样可以确保最终包里的polyfill数量的最小化,打包的包相对会小一些;可以设置corejs属性来确定使用的corejs的版本;
    • 第三个值:entry 如果我们依赖的某一个库本身使用了某些polyfill的特性,但是因为我们使用的是usage,所以之后用户浏览器可能会报错;所以,如果你担心出现这种情况,可以使用entry;并且需要在入口文件中添加`import 'core-js/stable'; import 'regenerator-runtime/runtime';这样做会根据browserslist 目标导入所有的polyfill,但是对应的包也会变大;
// babel.config.js
module.exports = {
  presets: [
    ["@babel/preset-env", {
      // false: 不用任何的polyfill相关的代码
      // usage: 代码中需要哪些polyfill, 就引用相关的api
      // entry: 手动在入口文件中导入 core-js/regenerator-runtime, 根据目标浏览器引入所有对应的polyfill
      useBuiltIns: "entry",
      corejs: 3
    }],
    ["@babel/preset-react"]
  ]
}

plugin-transform-runtime

上面使用 polyfill 时使用的属性 useBuiltIns 是传递给 preset 的参数,它会使得 polyfill 是全局影响的。链接 如果不想 polyfill 被添加到全局,使用 plugin-transform-runtime 插件。在前面我们使用的polyfill,默认情况是添加的所有特性都是全局的。如果我们正在编写一个工具库,这个工具库需要使用polyfill;别人在使用我们工具时,工具库通过polyfill添加的特性,可能会污染它们的代码;所以,当编写工具时,babel更推荐我们使用一个插件: @babel/plugin-transform-runtime来完成polyfill的功能;

useBuiltIns 和 @babel/plugin-transform-runtime 是互斥的

  • 安装@babel/plugin-transform-runtime:
npm install @babel/plugin-transform-runtime -D
  • 使用plugins来配置babel.config.js:
module.exports = {
  plugins: [
    ["@babel/plugin-transform-runtime", {
      corejs: 3
    }]
  ]
}

而且要依据安装的 corejs 库版本安装对应的另外一个包:

corejs 选项 安装对应的包
false npm install --save @babel/runtime
2 npm install --save @babel/runtime-corejs2
3 npm install --save @babel/runtime-corejs3

React的jsx支持

  • 我们编写react代码时,react使用的语法是jsx,jsx是可以直接使用babel来转换的。对react jsx代码进行处理需要如下的插件:
    • @babel/plugin-syntax-jsx
    • @babel/plugin-transform-react-jsx
    • @babel/plugin-transform-react-display-name

但是开发中,我们并不需要一个个去安装这些插件,我们依然可以使用preset来配置:

npm install @babel/preset-react -D

在 babel.config.js 中这样设置:

module.exports = {
  presets: [
    ["@babel/preset-react"]
  ]
}

TypeScript的编译

  • 在项目开发中,我们会使用TypeScript来开发,那么TypeScript代码是需要转换成JavaScript代码。 可以通过TypeScript的compiler来转换成JavaScript:
npm install typescript -D

另外TypeScript的编译配置信息我们通常会编写一个tsconfig.json文件:

tsc --init

之后我们可以运行npx tsc来编译自己的ts代码:

npx tsc
  • 如果我们希望在webpack中使用TypeScript,那么我们可以使用ts-loader来处理ts文件:配置ts-loader:
    {
        test: /\.ts$/,
        exclude: /node_modules/,
        // 本质上是依赖于typescript(typescript compiler)
        use: "ts-loader"
    }