Babel 7

Babel-7

babel是一个工具链,其主要用于将ES5以上对的版本代码转化为向下兼容的JavaScript语法。

本文是针对babel7.10.0讲解的

创建测试项目

mkdir babel-test
cd babel-test
npm init
  • babel-test根目录下创建.babelrc文件,用于配置babel。

    babel命令会从当前编译文件依次向上查找,直到找到.babelrcbabel.config.js为止。

  • 在根目录下新建source-code目录用于存放源码。

  • 在根目录下新建target-code目录用于存放编译后的代码。

@babel/core

babl核心包,其定义了代码转换的一些方法,可将源代码根据配置转换为兼容环境的目标代码。

npm install --save-dev @babel/core

几乎所有babel相关组件均依赖于它,没有该包,则无法正常执行编译操作。

@babel/cli

@babel/cli是babel提供的内建命令工具,可以在终端使用命令主动编译需要的文件。

npm i --save-dev @babel/cli

@babel/cli是babel命令工具,不适合全局安装。

@babel/node是babel对动态编译的支持,适合全局安装,会损失程序速度和性能。

他们都依赖于@babel/core

source-code目录下新建test.js文件,内容如下:

const funA = ()=>{
    let arr = ["a","b","c"];
    let isExist = arr.includes((item)=>item=="c");
    console.log(isExist)
}

funA();

new Promise((resolve)=>{
    console.log("异步操作")
    resolve();
}).then(()=>{
    console.log("异步成功")
})

转换命令

npx babel source-code/test.js --out-file target-code/out.js

source-code/test.js编译后,生成out.jstarget-code目录下。

此时你会发现out.js生成了,但是未进行编译转换。因为babel需要根据配置来确认需要转换为什么环境的代码,babel7将babel6中集成的编译功能拆分为了一个个插件,默认情况下无可用插件编译,所以无法执行编译。

@babel/plugin*

babel7是通过一个个插件来进行编译的,如果没有插件则无法进行编译转换。

例如:上面test.js中使用了=>,箭头函数的编译插件为:@babel/plugin-transform-arrow-functions

npm i --save-dev @babel/plugin-transform-arrow-functions 

修改执行命令为:

npx babel source-code/test.js --out-file target-code/out.js --plugins=@babel/plugin-transform-arrow-functions

此时你可以发现=>已经转换为了function()

当然一段代码的编译将使用到很多的插件,例如:

  • 将const,let转换为var:@babel/plugin-transform-block-scoping
  • 将class转化为function:@babel/plugin-transform-classes

详情请参考:https://www.babeljs.cn/docs/plugins

@babel/presets

presets是插件集合,我们代码中可能用到了多个需要转换的插件,如果一个一个的引入,耗时且难以维护。所以官方提出了一些插件集合来简化我们的引入操作。

常见的插件集有:

  • @babel/preset-env:ES2015、ES2016、ES2017的语法插件集

    其不支持stage-x插件的转换

  • @babel/preset-stage-0:stage-0,1,2,3都是对预设语法的转换插件,越靠前的插件,包含内容越多。

    stage-0包含了1,2,3的所有插件,另外还支持transform-do-expressions,transform-function-bind

    Babel7开始废弃了stage-x的预设集,请谨慎使用,在babel7种按需导入对应的插件即可。

  • @babel/plugin-transform-flow-strip-types:去除ts类型校验

  • @babel/preset-react:支持jsx语法的转换

  • babel-preset-minify:压缩代码

  • @babel/preset-typescript:转换ts代码

npm install --save-dev @babel/preset-env

在.babelrc中添加如下信息:

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

执行编译:

npx babel source-code/test.js --out-file target-code/out.js

可发现除了Promise,代码基本被编译了。

@babel/polyfill

@babel/polyfillcore-js2regenerator-runtime组成,用来实现对generatorasyncPromiseSymbol等函数的支持。babel默认进队对js语法进行解析,并不能转换API。

npm i --save-dev @babel/polyfill

在入口文件头部引入@babel/polyfill,即可按需加载文件中用到的api。

使用方法

修改.babelrc配置

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage", //手动引入@babel/polyfill,如果设置为entry则可在配置在entry上,自动引入
        "corejs": "3"   //指定corejs的版本,否则会有警告
      }
    ]
  ],
  "plugins": []
}

test.js头部添加import "@babel/polyfill";

执行编译后代码如下:

"use strict";

require("core-js/modules/es.array.includes");

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

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

var funA = function funA() {
  var arr = ["a", "b", "c"];
  var isExist = arr.includes(function (item) {
    return item == "c";
  });
  console.log(isExist);
};

funA();
new Promise(function (resolve) {
  console.log("异步操作");
  resolve();
}).then(function () {
  console.log("异步成功");
});

可以看到自动引入了promiseincludesto-string三个api。

@babel/runtime和@babel/plugin-transform-runtime

@babel/runtime依赖于@babel/helpersregenerator-runtime

@babel/helpers:包含了很多辅助方法,可以便于将高阶语法转化为地接语法,相当于公共函数类。例如class转换_classCallCheck

regenerator-runtime:生成器函数、asyncawait经babel编译后,regenerator-runtime模块用于提供功能实现。

@babel/runtime解决了2个问题:

  • 每个模块编译都自动引入了api类,但这会造成相同引用类多次复用而引入的文件体积变大。@babel/runtime抽离了公共方法,存放在@babel/helpers中,减少了代码体积。
  • @babel/polyfill虽按需引入了,但是会污染全局命名空间,当用户自定的方法和公共库方法重名时,就会造成冲突。
npm i --save-dev @babel/plugin-transform-runtime 
npm i --save @babel/runtime 
npm install --save @babel/runtime-corejs2

@babel/runtime需搭配@babel/plugin-transform-runtime使用。

@babel/plugin-transform-runtime的corejs默认为false,即不对polyfill进行处理,可设置为不同的版本让其支持。

执行编辑后可以发现,对polyfill引入内容添加了别名,这样就避免了全局污染。

@babel/register

@babel/register提供了动态编译,使我们的源码可以直接在生产环境运行,而不是先编译后生成环境才能执行。

npm install --save-dev @babel/register

使用时,在入口文件头部引入require("@babel/register");,即可实现动态编译,不建议使用,因为损耗生成环境性能。

stage-x

Babel7.10.0开始正式废弃了预设提案stage-x的支持需要我们手动按需在插件中依次引入。

"plugins": [
    // Stage 0
    "@babel/plugin-proposal-function-bind",                       //::修饰符转化为bind或者call
		
    // Stage 1
    "@babel/plugin-proposal-export-default-from",                   //export * form语法支持
    "@babel/plugin-proposal-logical-assignment-operators",          //||=,&&=等合并操作符的支持
    ["@babel/plugin-proposal-optional-chaining", { "loose": false }],//?.属性名 链式结构的支持
    ["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }],  //管道链接器|>的支持
    ["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }], //?? 操作符的支持
    "@babel/plugin-proposal-do-expressions",                        //do{...} 表达式的支持

    // Stage 2
    ["@babel/plugin-proposal-decorators", { "legacy": true }],    //注解修饰器的支持
    "@babel/plugin-proposal-function-sent",                       //function* generator函数别名的支持
    "@babel/plugin-proposal-export-namespace-from",               //export * as name form语法支持,即导出别名的支持
    "@babel/plugin-proposal-numeric-separator",                   //**,>>,<<等数学运算符的支持
    "@babel/plugin-proposal-throw-expressions",	                  //throw new Error(),异常抛出函数的支持

    // Stage 3
    "@babel/plugin-syntax-dynamic-import",                        //按需导入的支持,目前可以用polyfill代替API的导入
    "@babel/plugin-syntax-import-meta",                           
    ["@babel/plugin-proposal-class-properties", { "loose": false }],//class类解析
    "@babel/plugin-proposal-json-strings"                           //字符串中添加固定字符
  ]

antd样式按需加载

 "plugins": [
 		"@babel/plugin-syntax-dynamic-import", 
 		["import", { "libraryName": "antd", "libraryDirectory": "lib", "style": "css"}, "ant"],
    ["import", { "libraryName": "antd-mobile", "libraryDirectory": "lib", "style": "css"},"antd-mobile"]
 ]

@babel/plugin-proposal-object-rest-spread:支持{…a},解构语法,使用较多。

自动替换stage-x插件

npx babel-upgrade --write

运行该命令会自动更新package.json.babelrc文件,替换package.json中出现的stage-x预设集。

自定义插件

babel转换分为3个步骤:词法解析,逻辑转换,代码生成。

一个插件就是一个函数,其接受一个babel对象,返回一个包含visitor的对象,visitor就是一颗AST语法树。

新建source-code/my-plugin.js,内容如下:

module.exports = function (babel) {
    const t = babel.types;

    return {
        name: "myPlugin",
        visitor: {
            CallExpression(path) {
                const obj = path.node.callee.object     //对象
                const prop = path.node.callee.property  //属性
                const arguments = path.node.arguments   //参数
                if (t.isIdentifier(obj) && t.isIdentifier(prop) && obj.name === 'console' && prop.name === 'log') {
                    const location = `****location:row:${path.node.loc.start.line}, column:${path.node.loc.start.column}****`;
                    //为每个console.log()语句的末尾加上一个位置信息
                    arguments.push(t.stringLiteral(location))
                }
            }
        }
    }
}

这里我们定义了一个插件,为每个console.log增加了一个参数,显示打印位置。

使用

.babelrc中添加上诉插件:

 "plugins": [
    "./source-code/my-plugin.js"
  ]

执行编译,可发现console.log("异步操作") =>console.log("异步操作", "****location:row:10, column:4****")

自定义插件就是操作AST语法树,使其满足自己的需求,然后重新生成新的代码。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章