webpack5是如何使用babel转化代码的(1)-业务开发时的babel配置

webpack5是如何使用babel转化代码的(1)-业务开发时的babel配置,第1张

前端开发工程中,因为js代码的更新换代速度快于浏览器的更新速度,所以导致了部分旧的浏览器,比如ie系列,不支持部分js代码。为了能够在这些浏览器中运行我们的项目,需要进行兼容处理。这就是babel的产生来由。

一,新建一个基础的webpack5项目

初始化项目

npm init -y

安装webpack相关依赖

npm install webpack webpack-cli webpack-dev-server -D

新建src文件夹,并写上代码

./src/main.js

  function test(){
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        resolve(22)
      },0)
    })
  }
  async function test2(){
    const result =await test()
    console.log("----",result)
  }
  test2()

新建config文件夹,并新建webpack.config.js文件

./config/webpack.config.js

const path = require("path");
module.exports = {
  entry: path.resolve(__dirname, "../src/main.js"), // 入口文件,打包从这个文件开始
  devtool:false,
  output: {
    path: path.resolve(__dirname, "../dist"),//出口文件,打包生成的文件放置到这个文件夹下
    clean: true,
    filename: "./js/[name].[chunkhash].js"		//打包成的文件名。name取的原始文件名,chunkhash生成哈希值,这样每次打包出来不一样,避免浏览器缓存读取旧文件。
  },
  mode: "development"  //开发模式
};

在package.json的script中添加命令

"scripts": {
    "build": "webpack --config ./config/webpack.config.js  --progress --color "
 },

安装html插件

npm install html-webpack-plugin -D

新建public文件夹并创建index.html文件:

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title><%= htmlWebpackPlugin.options.title %>title>
  head>
  <body>
    <div id="app">div>
  body>
html>

配置webpack:

 const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    // ...其他配置
     plugins: [
        new HtmlWebpackPlugin({
          template: "./public/index.html", //用来做模板的html的文件路径(从项目根目录开始)
          filename: "index.html", //生成的html的名字
          title:'webpack5的项目配置',//这个就对应上文的title
          inject: "body" //打包出来的那个js文件,放置在生成的body标签内
        })
      ],
}
二,测试es6的代码打包情况

然后 运行npm run build,查看打包出来的文件:

/******/ (() => { // webpackBootstrap
var __webpack_exports__ = {};
/*!*********************!*\
  !*** ./src/main.js ***!
  \*********************/

  function test(){
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        resolve(22)
      },0)
    })
  }
  async function test2(){
    const result =await test()
    console.log("----",result)
  }
  test2()

/******/ })()
;

可以看到,es6的代码并没有转化成es5。

三,理解babel

这时候就需要引入babel。

Babel是一个工具集,主要用于将ES6版本的JavaScript代码转为ES5等向后兼容的JS代码,从而可以运行在低版本浏览器或其它环境中。

我们通常说的babel7啊,babel6啊,这个包实际上说的是@babel/core这个核心库的版本。

下面列出的是 Babel 能为你做的事情:

语法转换:将es6+转化为es5语法
补齐API:通过 Polyfill 方式在目标环境中添加缺失的特性 (通过 @babel/polyfill 模块),比如说给ie浏览器增加Promise。

babel只是提供了一个“平台”,让更多有能力的plugins入驻这个平台,是这些plugins提供了将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法的能力。

在加入plugins测试之前我们需要知道一些前置知识,babel将ECMAScript 2015+ 版本的代码分为了两种情况处理:

语法层: let、const、class、箭头函数等,这些需要在构建时进行转译,是指在语法层面上的转译api方法层:Proxy,Promise、includes、map等,这些是在全局或者Object、Array等的原型上新增的方法,它们可以由相应es5的方式重新定义

babel对这两种情况的转译是不一样的,我们需要给出相应的配置。

四,语法转换 1,引入@babel/preset-env

对于预设的选择,Babel插件实在太多,假如只配置插件数组,那我们前端工程要把ES2015,ES2016,ES2017…下的所有插件都写到配置项里,我们的Babel配置文件会非常臃肿。

preset预设就是帮我们解决这个问题的。Babel7.8官方的插件和预设目前有100多个,实际发展至今(2022-05-27),Babel官方的preset,我们实际可能会用到的其实就只有4个:

@babel/preset-env@babel/preset-flow@babel/preset-react@babel/preset-typescript

一个普通的vue工程,Babel官方的preset只需要配一个"@babel/preset-env"就可以了。所以这里我就只引入@babel/preset-env。

安装基本的babel:

npm install  babel-loader @babel/core @babel/preset-env -D

这里讲一下,为啥是这三个依赖:

1.babel-loader:因为我是用的webpack,要让webpack允许babel来处理js代码。如果不用webpack,需要安装@babel/cli这个依赖。
2.@babel/core:babel的核心库,我们平时说的babel,就是说的它,他提供一个平台,可以拔插想要的预设和插件。实际上实现代码转换的是这些预设和插件,而不是babel本身。
3.@babel/preset-env:它是babel之前一大堆预设的超集,可以对标准的es6语法进行转化。这里安装它就是让它来进行es6转成es5的。

配置对应的webpack:

 module: {
    rules: [{
        test: /\.js$/,
        use: {
          loader: "babel-loader"
        },
        exclude: /node_modules/
	}]
}

然后新建babel.config.js文件:

const presets = [
  [
    "@babel/preset-env",
    {}
  ]
];
const plugins = [];
module.exports = {
  plugins,
  presets
};
2,Babel 配置文件的理解

Babel的配置文件是Babel执行时默认会在当前目录寻找的文件,主要有.babelrc,.babelrc.js,babel.config.js和package.json。它们的配置项都是相同,作用也是一样的,只需要选择其中一种。

plugin代表插件,preset代表预设,它们分别放在plugins和presets,每个插件或预设都是一个npm包。

plugins插件数组和presets预设数组是有顺序要求的。如果两个插件或预设都要处理同一个代码片段,那么会根据插件和预设的顺序来执行。规则如下:

插件比预设先执行插件执行顺序是插件数组从前向后执行预设执行顺序是预设数组从后向前执行

每个插件是插件数组的一成员项,每个预设是预设数组的一成员项,默认情况下,成员项都是用字符串来表示的,例如"@babel/preset-env"。

如果要给插件或预设设置参数,那么成员项就不能写成字符串了,而要改写成一个数组。数组的第一项是插件或预设的名称字符串,第二项是个对象,该对象用来设置第一项代表的插件或预设的参数。例如给@babel/preset-env设置参数:

const presets = [
  [
    "@babel/preset-env",
    {
      targets: {
        chrome: "58",
        ie: "11"
      },
      useBuiltIns: "usage",
      corejs: 2 // 新版本的@babel/polyfill包含了core-js@2和core-js@3版本,所以需要声明版本,否则webpack运行时会报warning
    }
  ]
];

再次打包之后,虽然会发现代码中的箭头函数,const,let等es6语法没掉了,打包出来的js还是包含箭头函数(在打包文件的最开头,这个是因为本文使用webpack5,webpack5之后打包默认使用ecma6了)。而其中还包含Promise,map等,是因为它们属于api而不是语法,目前执行到这一步,是因为只使用了@babel/preset-env,babel只会进行语法的转化,而不会进行api的补齐

接下来讲一下,为啥@babel/preset-env我们并没有设置任何参数,打包出来的代码却已经完成了es5的转化?

如果你使用过vue或react的官方脚手架cli工具,你一定会在其package.json里看到browserslist项,下面该项配置的一个例子:

  "browserslist": [
    "> 0.1%",
    "last 2 versions",
    "ie>=9"
  ]

上面的配置含义是,目标环境是市场份额大于0.1%的浏览器并且不考虑IE8及以下的IE浏览器。Browserslist叫做目标环境配置表,除了写在package.json里,也可以单独写在工程目录下.browserslistrc文件里。我们用browserslist来指定代码最终要运行在哪些浏览器或node.js环境。Autoprefixer、postcss等就可以根据我们的browserslist,来自动判断是否要增加CSS前缀(例如’-webkit-')。我们的Babel也可以使用browserslist,如果你使用了@babel/preset-env这个预设,此时Babel就会读取browserslist的配置。

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

如下设置,则转化出来的代码,不会再有箭头函数,const,let等es6的语法:

  "browserslist": [
    "> 0.1%",
    "last 2 versions",
    "ie>=9"
  ]
2,preset-env配置target参数

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

该参数项的写法与browserslist是一样的,下面是一个例子:

const presets = [
  [
    "@babel/preset-env",
    {
      targets: {
        chrome: "58",
        ie: "9"
      }
    }
  ]
];

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

正常情况下,我们推荐使用browserslist的配置而很少单独配置@babel/preset-env的targets。因为browserslist还会有其他插件使用,这样可以保持统一。

这时候,我们打包项目出来,会发现语法层面已经完成了转化,代码在高版本的浏览器中可以正常打开,但是在低版本浏览器或者ie中打开:

这是因为目前为止,我们只做了语法转换,而Promise等api,在ie中是没有的,需要补齐。

五,API补齐 1,理解为什么需要做api补齐

虽然上文@babel/preset-env做了语法翻译,但在低版本浏览器还是没有比如Promise、数组的map等。所以不仅要使用@babel/preset-env进行ES6转ES5,还要借助 @babel/polyfill把缺失的变量或者函数补充到低版本的浏览器里。从而让低版本的浏览器也可以支持promise这些新的API。

新的API分类两类,一类是Promise、Map、Symbol、Proxy、Iterator等全局对象及其对象自身的方法,例如Object.assign,Promise.resolve;另一类是新的实例方法,例如数组实例方法[1, 4, -5, 10].find((item) => item < 0)。

如果想让ES6新的API在低版本浏览器正常运行,我们就不能只做语法转换。

在前端web工程里,最常规的做法是使用polyfill,为当前环境提供一个垫片。所谓垫片,是指垫平不同浏览器之间差异的东西。polyfill提供了全局的ES6对象以及通过修改原型链Array.prototype等实现对实例的实现。

polyfill广义上讲是为环境提供不支持的特性的一类文件或库,狭义上讲是polyfill.js文件以及@babel/polyfill这个npm包。

我们可以直接在html文件引入polyfill.js文件来作为全局环境垫片, polyfill.js 有Babel官方的 polyfill.js,也有第三方的。我们引入一个Babel官方已经构建好的polyfill脚本。

简单起见,我们通过在html里引入polyfill.js的方式。


然后npm run build,ie中打开项目,就可以发现,代码执行了,且没有报错。

补齐API的方式除了通过引入 polyfill.js 文件 ,还有通过在构建工具入口文件(例如webapck),babel配置文件等方式进行。这里讲的通过在HTML里直接引入 polyfill.js 文件 这种方式进行在现代前端工程里逐渐淘汰,很少使用了。但通过这一步,可以很明显地知道,确实是它为我们的ie构建了es6的垫片。

于是,就可以在webpack中引入polyfill.js了。

2,引入@babel/polyfill

先将cdn引入的polyfill.js去除。

这里,我先用@babel/polyfill演示一下使用。

安装@babel/polyfil,值得注意的是,因为它是需要给浏览器增加垫片的,所以它是生产需要的依赖。

 npm install --save @babel/polyfill

配置babel.config.js文件,主要是preset-env的useBuiltIns参数设置:

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

(一),preset-env的useBuiltIns为false或者不设置

useBuiltIns这个参数项主要和polyfill的行为有关。在我们没有配置该参数项或是取值为false的时候,如果项目的入口文件第一行有引入polyfill:

import '@babel/polyfill';
  function test(){
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        resolve(22)
      },0)
    })
  }
  async function test2(){
    const result =await test()
    console.log("----",result)
  }
  test2()

然后修改babel.config.js:

const presets = [[
  "@babel/preset-env",
  {
    useBuiltIns: false
  }
]];
const plugins = [];
module.exports = {
  plugins,
  presets
};

polyfill就是我们上文cdn引入一样,会全部引入到最终的代码里。

(二),preset-env的useBuiltIns为entry

useBuiltIns取值为"entry"的时候,不再全部引入,而是会根据配置的目标环境找出需要的polyfill进行部分引入。

同样的,这种方法需要在入口文件引入polyfill:

import '@babel/polyfill';
function test(){
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        resolve(22)
      },0)
    })
  }
  async function test2(){
    const result =await test()
    console.log("----",result)
  }
  test2()

然后修改babel.config.js配置:

const presets = [[
  "@babel/preset-env",
  {
    useBuiltIns: 'entry',
    corejs: 2//@babel/polyfill包含了core-js@2,需要声明下使用哪个版本,否则默认是2,并且会warning,如果要用3,则必须再安装npm install core-js@3 -S
  }
]];
const plugins = [];
module.exports = {
  plugins,
  presets
};

(注意:corejs只有在useBuiltIns取值为"entry"或"usage"的时候才会生效。)

这种方式,polyfill会考虑目标环境缺失的API模块引入到文件中。

(三),preset-env的useBuiltIns为usage

useBuiltIns取值"usage"的时候,会根据配置的目标环境找出需要的polyfill进行部分引入。

这种方式不需要我们在打包入口文件(或者webpack的entry入口项)引入polyfill,Babel发现useBuiltIns的值是"usage"后,会自动进行polyfill的引入。

入口文件:

function test(){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve(22)
    },0)
  })
}
async function test2(){
  const result =await test()
  console.log("----",result)
}
test2()

babel.config.js:

const presets = [[
  "@babel/preset-env",
  {
    useBuiltIns: 'usage',
    corejs: 3//@babel/polyfill包含了core-js@2,需要声明下使用哪个版本,否则默认是2,并且会warning,如果要用3,则必须再安装npm install core-js@3 -S
  }
]];
const plugins = [];
module.exports = {
  plugins,
  presets
};

注意,如果corejs取值为3,必须安装并引入core-js@3版本才可以,否则Babel会转换失败并提示

安装

npm install core-js@3 -S

使用useBuiltIns:"usage"后,Babel除了会考虑目标环境缺失的API模块,同时考虑我们项目代码里使用到的ES6特性。只有我们使用到的ES6特性API在目标环境缺失的时候,Babel才会引入core-js的API补齐模块。

这个时候我们就看出了’entry’与’usage’这两个参数值的区别:'entry’这种方式不会根据我们实际用到的API进行针对性引入polyfill,而’usage’可以做到。另外,在使用的时候,'entry’需要我们在项目入口处手动引入polyfill,而’usage’不需要。

需要注意的是,使用’entry’这种方式的时候,只能import polyfill一次,一般都是在入口文件。如果进行多次import,会发生错误。

可以看到,ie中代码正常运行,且无报错:

至此,引入引入@babel/polyfill实现的低版本浏览器适配告一段落。

3,不使用@babel/polyfill

从babel7.4开始,官方不推荐再使用@babel/polyfill了,因为@babel/polyfill本身其实就是两个npm包的集合:core-js与regenerator-runtime。

因此从2019年年中开始,我们的新项目都应该使用core-js和regenerator-runtime这两个包。

对比2中使用@babel/polyfill的做法,不同的地方仅仅是移除@babel/polyfill,安装下面两个依赖:

 npm install --save core-js@3 regenerator-runtime

babel.config.js的配置和上文一样即可。

注意:这里core-js@3是因为我的babel.config.js中配置的3。

打包运行,可以看到ie中照样可以:

4,这种方法的缺点 (一),修改了全局变量的原型

babel 的 polyfill 机制是,对于例如 Array.from 等静态方法,直接在 global.Array 上添加;对于例如 includes 等实例方法,直接在 global.Array.prototype 上添加。这样直接修改了全局变量的原型,有可能会带来意想不到的问题。

比如说,假设一个 npm 组件开发的的作者,开发时刚好用到了 Promise 方法, 按照上面的方法,写完发布到 npm 仓库,现在你在写业务开发时需要使用这个包,下载下来了,但是他的项目里面也用到了 Promise ,但是他的 Promise 是 自定义的 一套,和你使用的polyfill改写的不一致。这就会导致你的项目跑不起来。

(二),转义语法时,辅助函数重复引入

class 语法中,babel 自定义了 _classCallCheck这个函数来辅助;typeof 则是直接重写了一遍,自定义了 _typeof 这个函数来辅助。这些函数叫做 helpers。从上图中可以看到,helper 直接在转译后的文件里被定义了一遍。如果一个项目中有100个文件,其中每个文件都写了一个 class,那么这个项目最终打包的产物里就会存在100个 _classCallCheck 函数,他们的长相和功能一模一样,这显然不合理。会使打出来的包特别大。

六,@babel/plugin-transform-runtime提供辅助函数的自动引入功能

为了方便演示,这里采用@babel/cli的方式处理js,暂时不使用webpack。安装@babel/cli:

npm i @babel/cli -D

现在我们沿用第六节第三步的配置:

babel.config.js

const presets = [
  [
    "@babel/preset-env",
    {
      useBuiltIns: "usage",
      corejs: 3
    }
  ]
];
const plugins = [];
module.exports = {
  plugins,
  presets
};

api补齐不再采用@babel/polyfill,二是使用core-js和regenerator-runtime这两个包。

修改main.js:

class Person {
  sayname() {
    return "name";
  }
}
var john = new Person();
console.log(john);

然后npx babel ./src/main.js -o b.js查看生成的文件:

"use strict";

require("core-js/modules/es.object.define-property.js");

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

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }

var Person = /*#__PURE__*/function () {
  function Person() {
    _classCallCheck(this, Person);
  }

  _createClass(Person, [{
    key: "sayname",
    value: function sayname() {
      return "name";
    }
  }]);

  return Person;
}();

var john = new Person();
console.log(john);

可以看到转换后的代码上面增加了好几个函数声明,这就是注入的函数,我们称之为辅助函数。@babel/preset-env在做语法转换的时候,注入了这些函数声明,以便语法转换后使用。

但样这做存在一个问题。在我们正常的前端工程开发的时候,少则几十个js文件,多则上千个。如果每个文件里都使用了class类语法,那会导致每个转换后的文件上部都会注入这些相同的函数声明。这会导致我们用构建工具打包出来的包非常大。

那么怎么办?一个思路就是,我们把这些函数声明都放在一个npm包里,需要使用的时候直接从这个包里引入到我们的文件里。这样即使上千个文件,也会从相同的包里引用这些函数。通过webpack这一类的构建工具打包的时候,我们只会把使用到的npm包里的函数引入一次,这样就做到了复用,减少了体积。

@babel/runtime就是上面说的这个npm包,@babel/runtime把所有语法转换会用到的辅助函数都集成在了一起。

打开node_modules@babel\runtime\helpers\esm文件夹,就可以在这里找到所有的辅助函数,对于上文用到的_classCallCheck、_defineProperties、_createClass也在其中。

当我们把它改成手动从这里引入也行。

"use strict";

require("core-js/modules/es.object.define-property.js");

var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");
var _defineProperty = require("@babel/runtime/helpers/defineProperty");
var _createClass = require("@babel/runtime/helpers/createClass");

var Person = /*#__PURE__*/ (function () {
  function Person() {
    _classCallCheck(this, Person);
  }

  _createClass(Person, [
    {
      key: "sayname",
      value: function sayname() {
        return "name";
      }
    }
  ]);

  return Person;
})();

var john = new Person();
console.log(john);

这样就解决了代码复用和最终文件体积大的问题。不过,这么多辅助函数要一个个手动引入,很是麻烦。这个时候,Babel插件@babel/plugin-transform-runtime就可以来帮助我们自动引入对应的辅助函数。

于是就可以引入@babel/plugin-transform-runtime以及@babel/runtime,这两个插件的作用分别是:

@babel/runtime:将所有文件用到的公用辅助函数抽离出来形成一个第三方文件。
@babel/plugin-transform-runtime:自动识别哪些文件使用了哪些公用辅助函数,自动在那些文件中引入辅助函数。

如何使用呢?其实@babel/preset-env已经包含了@babel/runtime,但是我们使用时,最好还是重新安装下:

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

然后修改babel.config.js配置即可:

const presets = [
  [
    "@babel/preset-env",
    {
      useBuiltIns: "usage",
      corejs: 3
    }
  ]
];
const plugins = ["@babel/plugin-transform-runtime"];
module.exports = {
  plugins,
  presets
};

也就是插件加上@babel/plugin-transform-runtime,参数使用它的默认参数。@babel/plugin-transform-runtime这个插件其实有很多作用,但使用默认参数配置时,将只发挥它自动删除文件中的辅助函数并且从node_modules@babel\runtime\helpers\esm中引入对应的辅助函数的作用。

这时再运行:npx babel ./src/main.js -o b.js,将会得到b文件:

"use strict";

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

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

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

var Person = /*#__PURE__*/function () {
  function Person() {
    (0, _classCallCheck2.default)(this, Person);
  }

  (0, _createClass2.default)(Person, [{
    key: "sayname",
    value: function sayname() {
      return "name";
    }
  }]);
  return Person;
}();

var john = new Person();
console.log(john);

可以看到,它帮助我们自动引入了辅助函数。

至于@babel/plugin-transform-runtime关于API转换的功能,则需要参数配置之后才能开启,因为我们是业务代码的开发者,所以采用的就是polyfill补齐api的方案,于是第一个修改了全局变量原型的缺点,暂时不处理,下一篇文章具体讲一下这个。

于是到这里我们这时候就能得到,业务开发者最佳的babel配置。

七,业务开发者最佳的babel配置

在第六点时,其实已经实现了低版本浏览器的兼容,但是不是很完美,作为业务开发者而言,我们是可以接受全局变量的原型被polyfill改写的。但是为了包足够小,会想要解决第二个缺点:辅助函数重复引入的问题。

于是babel.config.js配置:

const presets = [
  [
    "@babel/preset-env",
    {
      useBuiltIns: "usage",
      corejs: 3
    }
  ]
];
const plugins = ["@babel/plugin-transform-runtime"];
module.exports = {
  plugins,
  presets
};

package.json

{
  "name": "babeltest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config ./config/webpack.config.js  --progress --color "
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.18.2",
    "@babel/plugin-transform-runtime": "^7.18.2",
    "@babel/preset-env": "^7.18.2",
    "@babel/runtime": "^7.18.3",
    "babel-loader": "^8.2.5",
    "html-webpack-plugin": "^5.5.0",
    "webpack": "^5.72.1",
    "webpack-cli": "^4.9.2",
    "webpack-dev-server": "^4.9.0"
  },
  "browserslist": [
    "> 0.1%",
    "last 2 versions",
    "ie>=9"
  ],
  "dependencies": {
    "core-js": "^3.22.7",
    "regenerator-runtime": "^0.13.9"
  }
}

webpack配置:

rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader"
        },
        exclude: /node_modules/
      }
    ]

第一步,我们是安装了@babel/core,babel-loader,@babel/preset-env,让webpack允许babel处理js代码。我们在babel.config.js中增加了@babel/preset-env预设,然后package中设置了browserslist,于是babel就会根据这个browserslist,把项目代码中的es6语法转化为es5的语法。

第二步,本来要安装@babel/polyfill的,但是babel7.4之后,它拆解为core-js@3和regenerator-runtime两个包了。在安装了这两个包之后。我们给@babel/preset-env设置两个属性,将需要用到的api按需引入,这样就能在低版本浏览器补齐Promise,map等es6的API了。

ack-cli": “^4.9.2”,
“webpack-dev-server”: “^4.9.0”
},
“browserslist”: [
“> 0.1%”,
“last 2 versions”,
“ie>=9”
],
“dependencies”: {
“core-js”: “^3.22.7”,
“regenerator-runtime”: “^0.13.9”
}
}


webpack配置:

```jsoN
rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader"
        },
        exclude: /node_modules/
      }
    ]

第一步,我们是安装了@babel/core,babel-loader,@babel/preset-env,让webpack允许babel处理js代码。我们在babel.config.js中增加了@babel/preset-env预设,然后package中设置了browserslist,于是babel就会根据这个browserslist,把项目代码中的es6语法转化为es5的语法。

第二步,本来要安装@babel/polyfill的,但是babel7.4之后,它拆解为core-js@3和regenerator-runtime两个包了。在安装了这两个包之后。我们给@babel/preset-env设置两个属性,将需要用到的api按需引入,这样就能在低版本浏览器补齐Promise,map等es6的API了。

第三步,安装@babel/runtime和@babel/plugin-transform-runtime,前者其实是babel完成语法转化过程中会用到的全部辅助函数。后者则是一个插件,它能把babel处理后的文件中的辅助函数删除,然后自动引入@babel/runtime中的辅助函数。这样辅助函数就只有一份。包就会小很多。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1324185.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存