你需要掌握的babel知识都在这里

Posted QIANDXX

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你需要掌握的babel知识都在这里相关的知识,希望对你有一定的参考价值。

随着ES6以及后面版本的问世,确实给前端javascript增添了不少活力,层出不穷的高级语法不仅提升编码效率,还使得前端的编码风格越来越靠近贴近主流。然而这些高级语法在有些浏览器上却运行不了,是因为浏览器还没法编译js的高级语法导致出现兼容性问题,需要将这些高级语法编译成浏览器能够运行的低版本语法。今天讲解的babel就是来解决这个js兼容问题的。

Babel,又名Babel.js。 是一个用于web 开发,且自由开源的JavaScript 编译器、转译器。 Babel 使软件开发者能够以偏好的编程语言或风格来写作源代码,并将其利用Babel 翻译成JavaScript。 Babel 是一个常用来使用最新的JavaScript 语言特性的工具。——维基百科

babel官网

前置知识

babel编译流程

babel其实就是将源码转换成目标源码的一个过程,大致分为三步

  • parse: 通过parser把源码转成抽象ast语法树
  • transform: 循环遍历ast,通过transform的能力对其进行语法转换
  • generate: 根据转换后的源码生产目标源码

主要依赖库介绍

@babel-core:babel核心编译库,它能够进行语法、语义、词法分析,生成抽象语法树
@babel-cli:负责命令相关的功能,提供 bebel 命令
@babel/preset-env:ES6稳定语法transform插件的集合
@babel/runtime: 运行时,配合transform-runtime
@babel/plugin-transform-runtime:polyfill垫片防止污染全局变量(第三方库必须得接入)
@babel/polyfill:高级语法兼容解决方案依赖

学习babel,你主要掌握一下三个模块就差不多了:

  • 1、环境搭建,基本配置
  • 2、babel-polyfill
  • 3、babel-runtime

基本配置

搭建一个简单的项目

┌-- src
┆   └--index.js
├-- .babelrc
└-- package.json

配置scripts命令

# package.json

    ...
    "scripts": 
        "babel": "babel src/index.js --out-dir dist"
    ,
    ...


安装

开发依赖

yarn add @babel/cli @babel/core @babel/plugin-transform-runtime @babel/preset-env -D

生产依赖

yarn add @babel/polyfill @babel/runtime -S

配置.babelrc


    "presets": [
        [
            "@babel/preset-env"
        ]
    ],
    "plugins": []


到这里一个最基础版本的babel就配置好了,运行看看
src/index.js

const sum = (a, b) => a + b;

输出
dist/index.js

"use strict";

var sum = function sum(a, b) 
  return a + b;
;

证明babel已经可以将箭头函数转换为 function 函数了,大功告成!先别高兴太早,插播一段解释。。。。。。。

1、解释一下@babel/preset-env
这个是ES6稳定语法编译插件的集合,什么意思?就是除掉一小部分高级语法(无法用js完整表示)以外的ES6语法转换成低版本js的插件的集合。
举例:如果不用预设,上面src/inex.js中的箭头函数得单独安装@babel/plugin-transform-arrow-functions插件来完成转译


    "presets": [], 
    "plugins": ["@babel/plugin-transform-arrow-functions"],
    //"targets": "last 2 Chrome versions"


也是可以正常转译箭头函数,也就是说在babel中一种语法就得需要对应一个插件来转译,这里所说的@babel/preset-env预设就是很多稳定语法转译成低版本js插件的集合,目的是方便使用,减少配置成本。
2、这里再补充一下几个官方的预设
@babel/preset-react:用来编译jsx语法
@babel/preset-typescript:用来将ts文件转成js文件
@babel/preset-flow:用来将使用flow类型约束的文件转成js文件
3、既然babel是将高级语法转成低版本语法来做兼容处理,那到底要转到多低的版本呢???
@babel/preset-env会根据配置文件中的targets属性来生成插件列表后编译,在浏览器或electron中,官方还是推荐用.browserslistrc来配置目标环境。如果babel配置文件没有设置targetsignoreBrowserslistConfig,@babel/preset-env会使用默认的browserslist配置源(**> 0.5%, last 2 versions, Firefox ESR, not dead**

好了,来继续,来试试promise高级语法转译情况
src/index.js

const sum = (a, b) => a + b;
// 新的 API
Promise.resolve(100).then((data) => data);

编译后输出

"use strict";
var sum = function sum(a, b) 
  return a + b;
; // 新的 API
Promise.resolve(100).then(function (data) 
  return data;
);

发现Promsie根本就没有转译,也就是说@babel/preset-env预设没法转译promsie,怎么解决???
引入下一个知识点,@babel/polyfill,简称 “垫片”,说白了就是用这个来抹平部分高级语法的兼容问题

@babel/polyfill 垫片

@babel/polyfill其实是一个空壳,他当中包含 core-jsregenerate,真正起作用的是这两个依赖包

使用

在入口文件的顶部引入

import "@babel/polyfill";

执行编译,输出,都正常了

但是@babel/polyfill89.4kb完全引入了,而这里只需要promsie对应的垫片就OK了,造成编译出来的文件内存很大,又是一个头疼的问题来了!!!

官方出了解决方案,Babel@7.4.0版本之前,还是得用上面这种全量引入的方式,之后,可以分开引入

import "core-js/stable"; 
import "regenerator-runtime/runtime";

这也差不多,没多大效果,那有没有一种自动按需引入的解决方案呢?
要说还得看官方,这不方案就来了吗

 
    "presets": [ 
        [ 
            "@babel/preset-env", 
             
                "useBuiltIns": "usage", 
                "corejs": "3" 
             
        ] 
    ] 


配置完后,编译,输出

"use strict";

require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.promise.js");
var sum = function sum(a, b) 
  return a + b;
; // 新的 API
Promise.resolve(100).then(function (data) 
  return data;
);

完美解决!!!

到这里,如果自己的业务项目配置到此就结束了
如果是开发第三方依赖包,还得继续往下看

@babel/plugin-transform-runtime

咱们不解释为啥,先看使用了@babel/plugin-transform-runtime插件和没使用输出的代码。
src/index.js

class Test 

未使用的输出情况

"use strict";

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

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; 

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

var Test = /*#__PURE__*/_createClass(function Test() 
  _classCallCheck(this, Test);
);

使用后的输出情况

"use strict";

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

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

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

var Test = /*#__PURE__*/(0, _createClass2["default"])(function Test() 
  (0, _classCallCheck2["default"])(this, Test);
);

从两段输出代码可以看出,后一种是用变量来储存插件的方法,而且是采用抽离公共代码的helper文件导出方法的形式,而前一种则是把方法直接编译到了文件内,试想一下会出什么问题?

如果一个项目有多个地方用到了class类,转译时每个文件都要多出这段代码,造成严重的代码冗余,还有一点,直接将方法编译到文件会污染文件的全局环境,尤其是作为第三方类库提供给别人使用时,很可能被覆盖掉,应该避免不必要的冲突

@babel/runtime 和 @babel/plugin-transform-runtime这两个插件要配合使用,得同时安装


    "presets": [
        [
            "@babel/preset-env",
            
                "useBuiltIns": "usage",
                "corejs": 3
            
        ]
    ],
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            
                "absoluteRuntime": false,
                "corejs": 3,
                "helpers": true,
                "regenerator": true,
                "useESModules": false
            
        ]
    ]


结论:开发类库用polyfill + runtime,公司业务项目用polyfill就行了

完结

以上是关于你需要掌握的babel知识都在这里的主要内容,如果未能解决你的问题,请参考以下文章

寒冬也挡不住进大厂的决心,Android面试知识架构,面试需要掌握的都在这里!

Python学习教程:必须掌握的Cookie知识点都在这里了

你需要了解的HTTP知识都在这里了!

软件测试工程师需要掌握的非测试知识有哪些?---打卡第十天

你需要掌握的http知识

ES6