babel插件的相关知识

时间: 2019-09-24阅读: 479标签: 插件

一、babel介绍

Babel 是 JavaScript 编译器,更确切地说是源码到源码的编译器,通常也叫做“转换编译器(transpiler)”。 意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,然后返回给你新生成的代码。在这个源码到源码的转换过程当中,抽象语法树,即AST,起到了重要的作用。


二、抽象语法树(AST)

抽象语法树(Abstract Syntax Tree)也称为AST语法树,指的是源代码语法所对应的树状结构。也就是说,一种编程语言的源代码,通过构建语法树的形式将源代码中的语句映射到树中的每一个节点上。和我们的常说的虚拟DOM有点像,虚拟dom是用json的格式把DOM结构抽象成js对象,用于描述DOM的结构,每个节点的类型、属性等等,类似的AST是把js的源代码抽象为js对象的格式,以方便描述这段代码的语法。程序代码本身被映射成为一棵语法树,通过操纵语法树,我们能够精准的获得程序代码中的每一个精确的节点。例如声明语句,赋值语句等。


三、babel的处理步骤

Babel 的三个主要处理步骤分别是: 解析(parse)转换(transform)生成(generate)

解析步骤接收代码并输出 AST

转换步骤接收 AST 并对其进行遍历,在此过程中对节点进行添加、更新及移除等操作。 这是 Babel 或是其他编译器中最复杂的过程,同时也是插件将要介入工作的部分。在babel-loader中有两种方式可以配置babel插件,我们经常会配置:

rules: [
  {
    test: /\.(js|jsx)$/,
    loader: 'babel-loader',
    exclude: /node_modules/,
    options: {
      presets: ["env", 'stage-0'],
      plugins: [
        ["extract", { "library": "lodash" }],
        ["transform-runtime", {}]
      ]
    },
  }
]

注意:plugins 的插件使用顺序是顺序的,而 preset 则是逆序的。所以上面的执行方式是extract>transform-runtime>stage-0>env

代码生成步骤把最终(经过一系列转换之后)的 AST 转换成字符串形式的代码,同时还会创建源码映射(source maps)。代码生成比较简单,就是深度优先遍历整个 AST,然后构建可以表示转换后代码的字符串。

有了以上的基础知识之后我们看看babel插件是怎么编写的。


四、编写一个简单的babel插件

1、插件格式

先从一个接收了当前babel对象作为参数的function开始。

export default function(babel) {
  // plugin contents
}

我们经常会这样写:

export default function({ types: t }) {
    //
}

接着返回一个对象,其 visitor 属性是这个插件的主要访问者。

export default function({ types: t }) {
  return {
    visitor: {
      // visitor contents
    }
  };
};

Visitor 中的每个函数接收2个参数:path 和 state;

export default function({ types: t }) {
  return {
    visitor: {
      Identifier(path, state) {},
      ASTNodeTypeHere(path, state) {}
    }
  };
};

Path 是表示两个节点之间连接的对象。表示AST中节点之间的相互关联关系。例如,如果有下面这样一个节点及其子节点︰

{
  type: "FunctionDeclaration",
  id: {
    type: "Identifier",
    name: "square"
  },
  ...
}

将子节点 Identifier 表示为一个路径(Path)的话,看起来是这样的:

{
  "parent": {
    "type": "FunctionDeclaration",
    "id": {...},
    ....
  },
  "node": {
    "type": "Identifier",
    "name": "square"
  }
}

我们发现path除了含有节点之间的关系之外,同时它还包含关于该路径的其他元数据

{
  "parent": {...},
  "node": {...},
  "hub": {...},
  "contexts": [],
  "data": {},
  "shouldSkip": false,
  "shouldStop": false,
  "removed": false,
  "state": null,
  "opts": null,
  "skipKeys": null,
  "parentPath": null,
  "context": null,
  "container": null,
  "listKey": null,
  "inList": false,
  "parentKey": null,
  "key": null,
  "scope": null,
  "type": null,
  "typeAnnotation": null
}

2、写一个简单的插件

我们写一个简单的插件,把所有定义变量名为a的换成b, 先从astexplorer看下var a = 1的 AST

{
  "type": "Program",
  "start": 0,
  "end": 10,
  "body": [
    {
      "type": "variableDeclaration",
      "start": 0,
      "end": 9,
      "declarations": [
        {
          "type": "VariableDeclarator",
          "start": 4,
          "end": 9,
          "id": {
            "type": "Identifier",
            "start": 4,
            "end": 5,
            "name": "a"
          },
          "init": {
            "type": "Literal",
            "start": 8,
            "end": 9,
            "value": 1,
            "raw": "1"
          }
        }
      ],
      "kind": "var"
    }
  ],
  "sourceType": "module"
}

从上面可以看出我们要找的节点类型是VariableDeclarator,以下是我们写出的插件代码:

export default function({ types: t }) {
  return {
    visitor: {
      VariableDeclarator(path, state) {
        if (path.node.id.name == 'a') {
          path.node.id = t.identifier('b')
        }
      }
    }
  }
}

我们要把id属性是 a 的替换成 b 就好了。但是这里不能直接path.node.id.name = 'b'。如果操作的是object,就没问题,但是这里是 AST 语法树,所以想改变某个值,就是用对应的 AST 来替换,现在我们用新的标识符来替换这个属性。

我们用@babel/core中的transform方法对我们写的插件进行测试一下:

import * as babel from '@babel/core';
const c = `var a = 1`;


const { code } = babel.transform(c, {
  plugins: [
    function({ types: t }) {
      return {
        visitor: {
          VariableDeclarator(path) {
            if (path.node.id.name == 'a') {
              path.node.id = t.identifier('b')
            }
          }
        }
      }
    }
  ]
})


console.log(code); // var b =

3、实现一个简单的按需打包的功能

例如我们要实现把import { Button } from 'antd'转成import Button from 'antd/lib/button'

通过对比 AST 发现,specifiers里的type和source不同。

// import { Button } from 'antd'
"specifiers": [
    {
        "type": "ImportSpecifier",
        ...
    }
],
"source": {
  "type": "Literal",
  "start": 23,
  "end": 29,
  "value": "antd",
  "raw": "'antd'"
}

 

// import Button from 'antd/lib/button'
"specifiers": [
    {
        "type": "ImportDefaultSpecifier",
        ...
    }
],
"source": {
  "type": "Literal",
  "start": 19,
  "end": 36,
  "value": "antd/lib/button",
  "raw": "'antd/lib/button'"
}

 

import * as babel from '@babel/core';
const c = `import { Button } from 'antd'`;


const { code } = babel.transform(c, {
  plugins: [
    function({ types: t }) {
      return {
        visitor: {
          ImportDeclaration(path) {
            const { node: { specifiers, source } } = path;
            if (!t.isImportDefaultSpecifier(specifiers[0])) { // 对 specifiers 进行判断
              const newImport = specifiers.map(specifier => (
                t.importDeclaration(
                  [t.ImportDefaultSpecifier(specifier.local)],
                  t.stringLiteral(`${source.value}/lib/${specifier.local.name}`)
                )
              ))
              path.replaceWithMultiple(newImport)
            }
          }
        }
      }
    }
  ]
})
console.log(code); // import Button from "antd/lib/Button"


五、总结

我们介绍了babel的功能、babel工作的三个阶段以及怎么写一个简单的插件,写插件主要用到了transform这个函数,插件的的函数返回一个对象,visitor是这个插件的访问者,通过它来访问AST,在visitor里定义一些函数做相关的操作。如果想了解更多有关babel插件的知识可访问babel插件手册

站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

2.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/6115

关闭

提升工作效率的Chrome插件推荐

推荐几个我觉得非常不错的 Chrome 插件,都是我非常常用的。Postman、Octotree、OneTab、Momentum、掘金、Night Eye、一键管理所有扩展

揭秘webpack plugin

Plugin(插件) 是 webpack 生态的的一个关键部分。它为社区提供了一种强大的方法来扩展 webpack 和开发 webpack 的编译过程。这篇文章将尝试探索 webpack plugin

前端最常用的vscode插件集

在前端开发中,使用Visual Studio Code有哪些你常用的插件?推荐几个自己喜欢的,不带链接,自己搜索安装吧。这些都是比较实用、前端必备的插件集

chrome插件开发

上手调试:打开chrome://extensions/ 开启开发者模式,选择加载已解压的扩展程序,选中自己的项目文件即可上手调试;引入vue 会出现 安全性问题,需要在manifest.json中设置

Vue中过滤器及自定义插件

想不出来,把官方的拿过来Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)

sublime安装插件

安装Sublime text 2插件很方便,可以直接下载安装包解压缩到Packages目录,也可以安装package control组件,然后直接在线安装

10个VSCode高效开发插件

本文介绍了目前前端开发最受欢迎的开发工具 VSCode 必装的 10 个开发插件,用于大大提高软件开发的效率。VSCode(Visual Studio Code)是由微软研发的一款免费、开源的跨平台文本(代码)编辑器,算是目前前端开发几乎完美的软件开发工具

开发一个Vue插件

Vue 项目开发过程中,经常用到插件,比如原生插件 vue-router、vuex,还有 element-ui 提供的 notify、message 等等。这些插件让我们的开发变得更简单更高效。那么 Vue 插件是怎么开发的呢?如何自己开发一个 Vue 插件然后打包发布到npm?

原生 js 实现一个有动画效果的进度条插件 progress

一个用于装载进度条内容的 div (且叫做 container)。然后在 container 里面动态生成三个元素,一个是做为背景的 div (且叫做 progress),一个是做为显示进度的 div (且叫做 bar),还有一个是显示文字的 span (且叫做 text)。

对于前端开发,整理推荐好用的chrome插件或应用

向web前端开发者整理提供的chrome插件或应用:比如Postman、JSON Viewer、Page Ruler 、ChromeADB 等等

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!