babel做兼容处理 到底怎么使用?

本文详细介绍了如何使用Babel进行兼容处理,包括直接引入polyfill、@babel/preset-env按需引入以及@babel/plugin-transform-runtime的优化。通过案例分析了配置的效果,如按需引入显著减少了包体积,而transform-runtime则能避免全局污染并优化体积。

1.背景

  • 日常项目开发中总是避免不了对低版本浏览器做一些兼容处理,最常见的手段就是结合编译工具使用babel来处理一些语法的兼容,但是每次使用的时候其实Babel的配置和使用到的相关库总是云里雾里,网上的各种推荐方案眼花缭乱不知道到底应该怎么选择。
  • 今天抱着学习的心态,和大家一起好好梳理一下

2.准备工作

  • 考虑到大多数项目都在使用webpack,今天就用webpack来学习验证babel
  • 目录结构
    在这里插入图片描述
  • 简单的配置
const path = require('path');
module.exports = {
  entry: './src/main',
  mode: 'development',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/,
      }
    ]
  }
};
  • 需要用的的依赖,先混个眼熟 core-jsregenerator-runtime@babel/preset-env@babel/runtime@babel/runtime-corejs3@babel/plugin-transform-runtime

3.配置bable

我们就以promise为例,看一下打包后补丁的效果

  • main.js
const a = 10;
const b = 11;

let add = (a, b) => {
  return Promise.resolve(a + b);
}
add(a, b);
  • 没有使用babel时,可以看到没有引入任何其他模块,语法也没有做转换
  • bundle.js的结果
    在这里插入图片描述
  • 包体积
    在这里插入图片描述

3.1 直接引入polyfill

  • 安装core-js regenerator-runtime
  • 配置babel.config,js
module.exports = {
  presets: [
    ["@babel/preset-env", {
      useBuiltIns: false // or "entry" "usage" false 
    }]
  ]
}
  • main.js中引入对应补丁
import "core-js/stable";
import "regenerator-runtime/runtime";
const a = 10;
const b = 11;

let add = (a, b) => {
  return Promise.resolve(a + b);
}
add(a, b);
  • bundle.js,可以看到是存在promise的polyfill的
  • 在这里插入图片描述
  • 因为是全局引入包体积明显变大,948k
    在这里插入图片描述
  • 可能有的同学看到过直接引入@babel/polyfill
  • 其实是在 babel7.4.0 之前,可以直接安装 @babel/polyfill 来转换 API,但是在 7.4.0 之后,就会提示让我们分开引入 core-js/stableregenerator-runtime/runtime了。
  • core-js:是整个 core-js 的核心,提供了基础的垫片能力,但是直接使用 core-js 会污染全局命名空间和对象原型
  • regenerator-runtime/runtime:主要用来兼容async generator 等语法

3.2 @babel/preset-env按需引入

  • 首先呢我们先看一下@babel/preset-env有哪些选项

targets

  • targets 可以指定项目的运行环境。如果没有配置 targets,那么 @babel/preset-env 会接着寻找项目中的 browserslist 配置,browserslist 配置只会控制语法的目标环境。如果 targetsbrowserslist 都没有,那么 @babel/preset-env 就会全量处理语法和 API。
  • browserslist 具体配置

useBuiltIns

  • useBuiltIns 决定了 @babel/preset-env 该如何处理 polyfill。可选值有:“usage” 、“entry” 、和 false, 默认为 false。
    • false,polyfill 就不会被按需处理会被全部引入。
    • entry,我们配置了targets,entry就是针对目标环境的全部,所以是对配置目标环境引入的所有 polyfill 扩展包,会自动将import “core-js/stable” 和 import “regenerator-runtime/runtime” 转换为目标环境的按需引入。
    • usage,则不需要手动导入 polyfill,babel 检测出此配置会自动进行 polyfill 的引入。

3.2.1 全局引入

  • 安装 @babel/preset-env
  • 配置babel.config.js 首先看一下全局引入
module.exports = {
  presets: [
    ["@babel/preset-env", {
      targets: {
        browsers: [
          'Android >= 4.4',
          'iOS >= 9.0',
        ],
      },
      useBuiltIns: "entry", // or "entry" "usage"
      corejs: 3
    }]
  ]
}
  • main.js 全局引入,不然useBuiltIns: "entry"也不生效
import "core-js/stable";
import "regenerator-runtime/runtime";
const a = 10;
const b = 11;

let add = (a, b) => {
  return Promise.resolve(a + b);
}
add(a, b);
  • 我们可以和之前的全局引入打包结果对比一下948k=>822k
    在这里插入图片描述

3.2.2 按需引入

  • 接下来我们看一下按需引入
  • 修改babel.config.js 使用 usage
module.exports = {
  presets: [
    ["@babel/preset-env", {
      targets: {
        browsers: [
          'Android >= 4.4',
          'iOS >= 9.0',
        ],
      },
      useBuiltIns: "usage", // or "entry" "usage"
      corejs: 3
    }]
  ]
}
  • main.js中不需要全局引入polyfiill
const a = 10;
const b = 11;

let add = (a, b) => {
  return Promise.resolve(a + b);
}
add(a, b);
  • 打包结果,包体积又从822k => 154k,明显减少
    在这里插入图片描述

3.3 @babel/plugin-transform-runtime进行优化

@babel/plugin-transform-runtime这个插件呢主要有两个作用

  1. 自动移除语法转换后内联的辅助函数,替换为@babel/runtime/helpers里的辅助函数,以节省代码体积
  2. 就是对 API 进行转换的时候,避免污染全局变量。

下面我们针对这两个功能具体看看效果

  1. 安装 @babel/plugin-transform-runtime@babel/runtime@babel/runtime-corejs3

3.3.1 优化体积

  1. 为了方便看效果,我们新建三个文件
    在这里插入图片描述
  • 三个文件都相同,就默认导出一个类
class A {
  a = 1
}
export default A;
  • 在入口文件main.js中引入
import A from './a.js';
import B from './b.js';
import C from './c.js';

const a = 10;
const b = 11;

let add = (a, b) => {
  console.log(A, B, C);
  return Promise.resolve(a + b);
}
add(a, b);
  • 打包一下看看大小,没有配置插件时270k
    在这里插入图片描述
  • 配置 babel.config.js 使用@babel/plugin-transform-runtime
module.exports = {
  presets: [
    ["@babel/preset-env", {
      targets: {
        browsers: [
          'Android >= 4.4',
          'iOS >= 9.0',
        ],
      },
      useBuiltIns: "usage", // or "entry"
      corejs: 3
    }]
  ],
  plugins: [
    ['@babel/plugin-transform-runtime']
  ]
}
  • 再次打包可以看到,270k => 166k,体积明显减小
    在这里插入图片描述
  • 怎么理解这个插件减小体积的功能呢?
  • Babel 在转译时,有时候会使用一些辅助的函数来帮忙,比如我们需要转译 class 类,就会在文件中注入_classCallCheck 这个函数来辅助转换,如果每个文件都有相同的语法,那每个文件都会注入相同的一份辅助函数;
  • 我们使用 @babel/plugin-transform-runtime 自动将需要引入的 helpers 函数替换为从 @babel/runtime 中的引用。
  • 那么配合webpack等打包工具,最终相同的引用只都公用一份,从而减小了体积。

3.3.2 避免全局污染

  • 开头说到@babel/plugin-transform-runtime 还有另一个关键的作用就是对 API 进行转换的时候,避免污染全局变量,但是看截图中引入的对应polyfill包还是从core-js中引用的,和没有使用插件时一样,似并没有改变。

  • 其实这个插件是有一些选项的,具体可以查看官网 https://babeljs.io/docs/en/babel-plugin-transform-runtime/#options

  • 我们上面的配置的默认选项是这样的

 ...
  // plugins: [
  //   ['@babel/plugin-transform-runtime']
  // ]
  plugins: [
    [
      '@babel/plugin-transform-runtime',
      {  // 默认配置
        helpers: true,
        corejs: false,
        regenerator: true,
        useESModules: false,
        absoluteRuntime: false
      }
    ]
  ]
}
  • 可以看到 corejs: false,这个参数就是用来设置是否做API转换以避免污染全局环境的,我们开启看看效果
  plugins: [
    ['@babel/plugin-transform-runtime', {
        "helpers": true,
        "corejs": 3 // 可选值为 2 3 false
    }]
  ]
  • 引入的polyfill的包由原来的core-js => core-js-pure @babel/runtime-cojs3
    在这里插入图片描述
  • 是怎么避免的全局污染呢?

core-js@3其实分为一下几个模块:

  • core-js:是整个 core-js 的核心,提供了基础的垫片能力,但是直接使用 core-js 会污染全局命名空间和对象原型;
  • core-js-pure:core-js-pure 提供了独立的命名空间,不污染全局变量;
  • core-js-compact:根据 Browserslist 维护了不同宿主环境、不同版本下对应需要支持特性的集合;
  • core-js-builder:结合 core-js-compact 以及 core-js,并利用 webpack 能力,根据需求打包出 core-js 的 core-js-bundle
  • 插件可以将 core-js 中 API 的 polyfill 直接修改原型改为从 @babel/runtime-corejs3中获取,避免了对全局变量和原型的污染;

  • 当开启了corejs之后呢,由于@babel/plugin-transform-runtime可以帮我们引入对应的polyfill,所以我们也可以不再使用@babel/preset-env中添加polyfill的能力了

  • 不过API转换主要是给开发JS库或npm包等的人用的,我们的前端工程一般仍然使用polyfill补齐API就可以了;

小小总结一下,一般项目开发就是使用@babel/plugin-transform-runtime默认配置 + @babel/preset-env 按需引入 就可以啦

如有不对的地方,请大家及时指出,避免影响其他同学;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木木林_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值