babel


1.babel介绍

小概

  • polyfill:polyfill主要抚平不同浏览器之间对js实现的差异,比如一些es6的语法,通过polyfill注入代码,使得es5也能正常运行api

  • Core-js:它是JavaScript 标准库中最流行也最常用的polyfill

    • core-js:包含所有polyfill(当然也可以仅引入部分特性的polyfill,比如:

      import 'core-js/features/array/from'; // <- at the top of your entry point
      import 'core-js/features/array/flat'; // <- at the top of your entry point
      import 'core-js/features/set';        // <- at the top of your entry point
      import 'core-js/features/promise';    // <- at the top of your entry point
      
      Array.from(new Set([1, 2, 3, 2, 1]));          // => [1, 2, 3]
      [1, [2, 3], [4, [5]]].flat(2);                 // => [1, 2, 3, 4, 5]
      Promise.resolve(32).then(x => console.log(x)); // => 32

      但是即使在这里部分引入的polyfill,依然会直接扩展到全局环境中

    • core-js-pure:不会把polyfill注入全局环境,但是在使用时需要单独引入polyfill的module

    • core-js-bundle:编译打包好的版本,包含全部的polyfill特性,适合在浏览器里面通过script直接加载,前2个版本适合放构建工具

  • regenerator-runtime:用来转换 generatorasync 函数,也是一个polyfill

  • babel:

    • 内部借助了corejs对代码添加polyfill

    • 并且支持ES6的代码转换为ES5代码(class、箭头函数、properties)

    • 支持jsx等编译转换

在babel7之前,babel专门提供了一个库叫babel/polyfill来做polyfill,在babel7之后,这个库被废弃了

babel的每一种转换处理都对应一个babel插件,但是plugin太多了,所以babel推出了presets,包含若干个plugin

自定义preset

安装完对应plugin之后,新建一个my-preset.js

module.exports = () => ({
  plugins: [
    ['@babel/plugin-transform-arrow-functions'],
    ['@babel/plugin-transform-classes', {spec: false}],
    ['@babel/plugin-transform-computed-properties'],
    ['@babel/plugin-proposal-object-rest-spread', {loose: true, useBuiltIns: true}]
  ]
});

然后在babel配置文件中导入

//babel.config.js
const presets = [
    './my-preset.js'
];
const plugins = [
];

module.exports = {presets, plugins}

webpack and babel

如果希望将ES6语法转化成ES5语法,我们可以使用babel对应的loader,配置打包得到新的js文件

npm install -D babel-loader @babel/core @babel/preset-env
  • @babel/core
    • babel的核心工具
  • @babel/preset-env
    • babel的预定义环境,包含大量实用babel plugin,还能根据browserslist进行配置,满足运行环境最低版本要求
    • 融入了corejs,并且有对应的配置项进行适配,抛弃了传统的 @babel/polyfill 直接全量引入
  • @babel-loader
    • babel在webpack中的加载器

其实还可以配置一个core-jscore-js用来使老版本的浏览器支持新版ES语法(比如老版本没有Promise,它会给你引进去coreJs库里的Promise函数),然后按需加载可以使用到的 core-js 部分

如果 @babel/core or @babel/plugin-transform-regenerator 版本 小于 7.18.0,你需要加载 regenerator runtime 来加载 core-js/stable, 如果 你用了 @babel/preset-env‘s useBuiltIns: "usage" option or @babel/plugin-transform-runtime. 则会自动加载 core-js/stable

2.配置

webpack.config.js中的配置

const path = require('path')

module.exports = {
  entry: "./src/index.ts",
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  module: {
    // 指定加载规则
    rules: [
      {
        //这里使用TypeScript
        test: /\.ts$/,// test指定规则生效的文件,以ts结尾的图片
        exclude: /node_modules/,
        use: [
          {
            loader: "babel-loader",
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    // 要兼容的目标浏览器
                    targets: {
                      "chrome": "58",
                      "ie": "8"
                    },
                    // corejs版本
                    "corejs": "3",
                    // 使用corejs的方式,按需加载
                    "useBuiltIns": "usage"
                  }
                ]
              ]
            }
          },
          // 如果使用typescript的loader,要把ts-lodaer放下方,babel-loader放上方
          // webpack从后往前执行,所以必须要先执行ts -> Js转换,再执行js兼容性的转换
          'ts-loader'
        ],
      }
    ]
  },
}
  • @babel/preset-env:

    • useBuiltIns

      • 默认false,不会有任何 polyfill 被添加进来

      • entry,需要我们手动针对项目入口文件处全局注入的core-js进行优化转换,但是只针对core-js@3版本

        import "core-js";
        //...
      • usage,按需导入,效果比 entry 好(体积小,可以看 这里

    • corejs

      • 安装完core-js后还需要在@babel-preset-env中指定其版本,如果开启useBuiltIns,默认为2.0版本,这里肯定要改成3.0+的,因为3.0core-js改进很大。

当然,我们也可以在根目录下,创建一个babel.config.js / babel.config.json / .babelrc 将babel配置信息放在一个独立的文件中

//babel.config.js
module.exports = {
    preset: {
        "@babel/preset-env"
    }
}

然后在webpack.config.js里的rules里直接use babel-loader即可

你甚至可以在package.json里面写

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

3.transform-runtime

(1)Babel 插入了辅助代码

  1. Babel 在每个文件都插入了辅助代码,使代码体积过大!

    Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend,默认情况下会被添加到每一个需要它的文件中。

    transform-runtime插件可以把这些重复的辅助代码,转换成公共的函数进行引入

  2. 我们正常给应用全局环境注入polyfill,是没啥问题的,但是如果在开发一个独立的工具库项目,不确定它将会被其它人用到什么运行环境里面,那么前面那种扩展全局环境的polyfill就不是一个很好的方式,而transform-runtime会为你创造一个沙盒环境来运行当前代码

下面的配置禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。

更多信息请查看 文档

npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
rules: [
  // 'transform-runtime' 插件告诉 Babel
  // 要引用 runtime 来代替注入。
  {
    test: /\.m?js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env'],
        plugins: ['@babel/plugin-transform-runtime']
      }
    }
  }
]

注意:如果同时开启plugin-transform-runtime和preset-env的polyfill,则会

rules: [
  {
    test: /\.m?js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: [
          '@babel/preset-env',
          {
              "targets": {
                  ios: 8,
                  android: 4.1
              },
              useBuiltIns: 'usage'
          }
        ],
        plugins: ['@babel/plugin-transform-runtime', {
            corejs: 3
        }]
      }
    }
  }
]

然后对以下进行转换

Promise.resolve().finally();

会出现

"use strict";

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

var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));

require("core-js/modules/es6.promise");

require("core-js/modules/es6.object.to-string");

require("core-js/modules/es7.promise.finally");

_promise.default.resolve().finally();

require("core-js/modules/es6.promise");var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise")); 这种重复的polyfill的存在,所以我们在开发时要关闭其中之一

options: {
      presets: ['@babel/preset-env'],
    plugins: ['@babel/plugin-transform-runtime']
}

或者

[
    "@babel/plugin-transform-runtime", {
        corejs: false,
        regenerator: false
    }
]

最后注意:

  1. 它跟preset-env提供的polyfill适用的场景是完全不同,runtime适合开发库,preset-env适合开发application
  2. runtime与preset-env的polyfill不能同时启用
  3. runtime的polyfill不判断目标运行环境(并不支持 targets 的配置)

(2)箭头函数

babel后部分浏览器仍然不支持箭头函数,是因为webpack5打包文件默认输出形式就是箭头函数,用以下配置解决即可

output: {
        filename: 'js/bundle.js',
        path: path.resolve(__dirname, 'dist'),
        environment: {
            arrowFunction: false
        }
},

(3)babel优化

影响打包效率的loader首当其冲必属 Babel 了

我们可以控制babel文件搜索范围,只转换指定文件夹下的代码,去除没必要转换的部分,比如node_modules

module.exports = {
  module: {
    rules: [
      {
        // js 文件才使用 babel
        test: /\.js$/,
        loader: 'babel-loader',
        // 只在 src 文件夹下查找
        include: [resolve('src')],
        // 不会去查找的路径
        exclude: /node_modules/
      }
    ]
  }
}

(4)babel原理

babel就是从一种源代码转换为另外一种源代码,或者把babel看成一种编译器

https://juejin.cn/post/7078482623387402271#comment

参考文章:

webpack官网

babel官网

babel详解


文章作者: Hello
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Hello !
 上一篇
js游戏框架合集 js游戏框架合集
Cocos.js概述Cocos.js概述,主要源自chatgpt: Cocos.js 是一款基于 Cocos2d-x 引擎的 JavaScript 游戏开发框架,主要用于创建跨平台的移动端游戏。它结合了 Cocos2d-x 引擎的高性能和跨
2021-03-01 Hello
下一篇 
webpack(中) webpack(中)
4.其他webpack开发环境的开发模式1. watch模式该模式下,webpack会实时侦测我们的js文件变化,在webpack依赖图的所有文件,只要有一个发生了更新,那么代码将被重新编译 但如果使用下方搭建服务,则没必要开启这个watc
2021-02-07
  目录