Babel

Babel 可以干什么:

  • 语法转换
  • 通过 Polyfill 方式在目标环境中添加缺失的功能
  • 源码转换(codemods

项目中常用的Babel配置

首先,webpack 中 loader 的本质就是一个函数,接受我们的源代码作为入参同时返回新的内容。

我们日常使用的 babel 相关配置主要涉及以下三个相关插件:

  1. babel-loader:本质就是一个函数,我们匹配到对应的文件交给 babel-loader

  2. babel-core:babel-loader 是识别匹配文件和接受对应参数的函数。 babel 在编译代码过程中核心的库就是 @babel/core 这个库。他可以将我们的代码进行词法分析--语法分析--语义分析过程从而生成AST抽象语法树,从而对于"这棵树"的操作之后再通过编译称为新的代码。

  3. babel-preset-env:告诉 babel 需要以为什么样的规则进行代码转义。

PresetPlugin

Preset 就是一些 Plugin 组成的合集,可以将 Preset 理解成一些 Plugin 整合成的一个包。

例如,@babel/preset-env 是一个非常常用的 Preset,它根据目标环境和配置的浏览器列表,自动选择和加载所需的插件,使得开发者无需手动选择和配置大量的插件。

babel-preset-env

将高版本 JavaScript 代码进行转译,根据内置的规则转译成为低版本的JavaScript 代码

仅仅针对语法阶段的转译 ,比如转译箭头函数,const/let 语法。针对一些Api或者Es 6内置模块的 polyfillpreset-env 是无法进行转译的。

常见的 Plugin 其实大多数都集成在了 babel-preset-env 中。

@babel/preset-env 不会包含任何低于 Stage 3 的 JavaScript 语法提案 。如果需要兼容低于 Stage 3 阶段的语法则需要额外引入对应的 Plugin 进行兼容。

ECMAScript(简称 ES)是 JavaScript 的标准化版本,它的新特性通常会经过一个由 TC39(ECMA 技术委员会)管理的提案过程,分为五个阶段,分别是 Stage 0 到 Stage 4。这些阶段代表了新特性从最初的提议到最终成为标准的过程。 具体各个阶段的含义如下:

  1. Stage 0 - Strawman(草案):此阶段是为了提出初步想法而设立的,通常由个人或小组提出,而不是由 TC39 委员会批准的提案。这些提案可能只是一个想法的草图,并不具有实际应用性。
  2. Stage 1 - Proposal(提案):在这个阶段,提案已经详细说明了设计、语法和语义,并且经过了初步讨论。该阶段要求提供详细的说明文档,以及实现和用户反馈等。
  3. Stage 2 - Draft(草案):在这个阶段,提案已经完整地描述了其语法和语义,并且有了初步的实现。这意味着提案已经成为一个可供实际使用的草案。
  4. Stage 3 - Candidate(候选):在这个阶段,提案已经基本完成,所有方面都已经得到了充分的讨论和评审,并且已经有了至少一个实现。这意味着提案已经足够成熟,可以供开发者使用和提供反馈。
  5. Stage 4 - Finished(完成):在这个阶段,提案已经通过了所有的评审流程,并且已经被加入到了 ECMAScript 的标准中。这意味着该特性已经成为了 JavaScript 的一部分,并且可以在任何支持该版本标准的环境中使用。

因此,当说一个特性处于 Stage 3 时,意味着它已经相对成熟,具有可用性,并且很可能会最终成为 ECMAScript 标准的一部分。

babel-preset-react

将 React 中的 jsx 进行转译。

babel-preset-typescript

对于 TypeScript 代码,有两种方式去编译 TypeScript 代码成为JavaScript代码

  1. 使用 tsc 命令,结合 cli 命令行参数方式或者 tsconfig 配置文件进行编译 ts 代码。
  2. 使用 babel ,通过 babel-preset-typescript 代码进行编译 ts 代码。

Babel 的 polyfill

首先我们来理清楚这三个概念:

  1. 最新 ES 语法,比如:箭头函数,let/const
  2. 最新 ES Api ,比如 Promise
  3. 最新 ES 实例/静态方法,比如 String.prototype.include

babel-prest-env 仅仅只会转化最新的 es 语法 ,并不会转化对应的 Api 和实例方法,比如说 ES 6 中的 Array.from 静态方法。 babel 是不会转译这个方法的,如果想在低版本浏览器中识别并且运行 Array.from 方法达到我们的预期就需要额外引入 polyfill 进行在 Array 上添加实现这个方法。

整体而言就是,语法层面的转化 preset-env 完全可以胜任。但是一些内置方法模块,仅仅通过 preset-env 的语法转化是无法进行识别转化的 ,所以就需要一系列类似"垫片"的工具进行补充实现这部分内容的低版本代码实现。这就是所谓的 polyfill 的作用。

针对于 polyfill 方法的内容,babel 涉及如下:

  1. @babel/polyfill:(从 Babel 7.4.0 版本开始,这个软件包已经不建议使用了,建议直接包含 core-js/stable 。)通过往全局对象上添加属性以及直接修改内置对象的Prototype上添加方法实现 polyfill 。比如说我们需要支持String.prototype.include,在引入 babelPolyfill 这个包之后,它会在全局 String 的原型对象上添加 include 方法从而支持我们的Js Api。这种方式本质上是往全局对象/内置对象上挂载属性,所以这种方式难免会造成全局污染

    useBuiltIns 决定了如何在 preset-env 中使用 @babel/polyfill。

    js 复制代码
    {
        "presets": [
            ["@babel/preset-env", {
                "useBuiltIns": false
            }]
        ]
    }

    他有三个可选值:

    1. false:默认值。它表示仅仅会转化最新的ES语法,并不会转化任何Api和方法。
    2. entry :需要我们在项目入口文件中手动引入一次core-js: import "core-js/stable";,它会根据我们配置的浏览器兼容性列表(browserList)然后全量引入不兼容的polyfill 。也就是说比如我们代码中仅仅使用了Array.from这个方法。但是polyfill并不仅仅会引入Array.from,同时也会引入Promise、Array.prototype.include等其他并未使用到的方法。这就会造成包中引入的体积太大了
    3. usage :会根据配置的浏览器兼容,以及代码中 使用到的Api 进行引入polyfill按需添加 。如果我们存在很多个模块,那么无疑会多出很多冗余代码(import语法)
  2. @babel/runtime:@babel/polyfill是存在污染全局变量的副作用,在实现polyfill时Babel还提供了另外一种方式去让我们实现这功能,那就是@babel/runtime。

    1. 简单来讲,@babel/runtime更像是一种按需加载的解决方案,比如哪里需要使用到Promise,@babel/runtime就会在他的文件顶部添加import promise from 'babel-runtime/core-js/promise'

    2. preset-env的useBuintIns配置项,我们的polyfill是preset-env帮我们智能引入。而babel-runtime则会将引入方式由智能完全交由我们自己,我们需要什么自己引入什么。

    存在如下问题:

    • babel-runtime无法做到智能化分析,需要我们手动引入。
    • babel-runtime编译过程中会重复生成冗余代码(一些工具函数)。
  3. @babel/plugin-transform-runtime:和run-time配合使用,解决上述我们提到的run-time存在的问题而提出的插件。

    • @babel/plugin-transform-runtime插件会智能化的分析我们的项目中所使用到需要转译的js代码,从而实现模块化从babel-runtime中引入所需的polyfill实现。
    • @babel/plugin-transform-runtime插件提供了一个helpers参数。开启后可以将上边提到编译阶段重复的工具函数代码转化称为require语句。此时,这些工具函数就不会重复的出现在使用中的模块中了。

两者的选择:

  • 如果项目对打包体积要求很高,或者想要避免全局污染,推荐使用 @babel/plugin-transform-runtime + @babel/runtime。
  • 如果想要快速方便地启用所有可能需要的 polyfills,并且不需要担心打包体积,可以使用 useBuiltIns + @babel/polyfill。

参考

juejin.cn/post/702523...

相关推荐
彭世瑜12 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund40413 分钟前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish14 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five15 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序15 分钟前
vue3 封装request请求
java·前端·typescript·vue
临枫54116 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
前端每日三省17 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
小刺猬_98518 分钟前
(超详细)数组方法 ——— splice( )
前端·javascript·typescript
渊兮兮19 分钟前
Vue3 + TypeScript +动画,实现动态登陆页面
前端·javascript·css·typescript·动画
鑫宝Code19 分钟前
【TS】TypeScript中的接口(Interface):对象类型的强大工具
前端·javascript·typescript