资讯专栏INFORMATION COLUMN

Babel插件开发入门示例详解

Muninn / 1860人阅读

摘要:的工作过程的处理主要过程解析转换生成。代码转换处理,处理工具插件等就是在这个阶段进行代码转换,返回新的。若感兴趣了解更多内容,插件中文开发文档提供了很多详细资料。

Babel简介

Babel是Javascript编译器,是种代码到代码的编译器,通常也叫做『转换编译器』。

Babel的工作过程

Babel的处理主要过程:解析(parse)、转换(transform)、生成(generate)。

代码解析
词法分析和语法分析构造AST。

代码转换
处理AST,处理工具、插件等就是在这个阶段进行代码转换,返回新的AST。

代码生成
遍历AST,输出代码字符串。

所以我们需要对AST有一定了解才能进行Babel插件开发。

AST

在这整个过程中,都是围绕着抽象语法树(AST)来进行的。在Javascritp中,AST,简单来说,就是一个记录着代码语法结构的Object。感兴趣的同学可到https://astexplorer.net/ 去深入体验
比如下面的代码:

import {Button} from "antd";

import Card from "antd/button/lib/index.js";

转换成AST后如下,

{
  "type": "Program",
  "start": 0,
  "end": 253,
  "body": [
    {
      "type": "ImportDeclaration",
      "start": 179,
      "end": 207,
      "specifiers": [
        {
          "type": "ImportSpecifier",
          "start": 187,
          "end": 193,
          "imported": {
            "type": "Identifier",
            "start": 187,
            "end": 193,
            "name": "Button"
          },
          "local": {
            "type": "Identifier",
            "start": 187,
            "end": 193,
            "name": "Button"
          }
        }
      ],
      "source": {
        "type": "Literal",
        "start": 200,
        "end": 206,
        "value": "antd",
        "raw": ""antd""
      }
    },
    {
      "type": "ImportDeclaration",
      "start": 209,
      "end": 253,
      "specifiers": [
        {
          "type": "ImportDefaultSpecifier",
          "start": 216,
          "end": 220,
          "local": {
            "type": "Identifier",
            "start": 216,
            "end": 220,
            "name": "Card"
          }
        }
      ],
      "source": {
        "type": "Literal",
        "start": 226,
        "end": 252,
        "value": "antd/button/lib/index.js",
        "raw": ""antd/button/lib/index.js""
      }
    }
  ],
  "sourceType": "module"
}
插件开发思路

确定我们需要处理的节点类型

处理节点

返回新的节点

简单插件结构

插件必须是一个函数,根据官方文档要求,形式如下:

module.exports = function ({ types: t }) {
    return {
        visitor: {
            ImportDeclaration(path, source){
                //todo
            },
            FunctionDeclaration(path, source){
                //todo
            },
        }    
    }
}

types来自@babel/types工具类,主要用途是在创建AST的过程中判断各种语法的类型和节点构造。

实现示例

很多同学用过 babel-plugin-import ,它帮助我们在使用一些JS类库是达到按需加载。其实,该插件帮助我们做了如下代码转换:

//from
import {Button } from "antd";

//to
import Button from "antd/es/button";
import "antd/es/button/style.css"; 

我们先看看两者的AST有何差别,以帮助我们对转换有个清晰的认识:

转换前:

 
[{
      "type": "ImportDeclaration",
      "start": 6,
      "end": 45,
      "specifiers": [
        {
          "type": "ImportSpecifier",
          "start": 14,
          "end": 20,
          "imported": {
            "type": "Identifier",
            "start": 14,
            "end": 20,
            "name": "Button"
          },
          "local": {
            "type": "Identifier",
            "start": 14,
            "end": 20,
            "name": "Button"
          }
        }
      ],
      "source": {
        "type": "Literal",
        "start": 28,
        "end": 44,
        "value": "antd/es/button",
        "raw": ""antd/es/button""
      }
    }]
    

转换后:

 [{
  "type": "ImportDeclaration",
  "start": 5,
  "end": 41,
  "specifiers": [
    {
      "type": "ImportDefaultSpecifier",
      "start": 12,
      "end": 18,
      "local": {
        "type": "Identifier",
        "start": 12,
        "end": 18,
        "name": "Button"
      }
    }
  ],
  "source": {
    "type": "Literal",
    "start": 24,
    "end": 40,
    "value": "antd/es/button",
    "raw": ""antd/es/button""
  }
},
{
  "type": "ImportDeclaration",
  "start": 46,
  "end": 76,
  "specifiers": [],
  "source": {
    "type": "Literal",
    "start": 53,
    "end": 75,
    "value": "antd/es/button/style",
    "raw": ""antd/es/button/style""
  }
}]

对比两棵树,我们应该有个大致的思路。在转换过程中,我们还需要一些参数,这些参数在配置文件(package.json或者.babelrc)中,提供了一些自定义配置,比如antd的按需加载:

["import",{libraryName:"antd",libraryDireactory:"es","style":"css"}]

现在我们开始尝试实现这个插件吧:

module.exports = function ({ types: t }) {
return {
    visitor: {
        ImportDeclaration(path, source) {
            
            //取出参数
            const { opts: { libraryName, libraryDirectory="lib", style="css" } } = source;
            //拿到老的AST节点
            let node = path.node

            if(node.source.value !== libraryName){
                return;
            }
            //创建一个数组存入新生成AST
            let newImports = [];
            //构造新节点
            path.node.specifiers.forEach(item => {
                newImports.push(t.importDeclaration([t.importDefaultSpecifier(item.local)], t.stringLiteral(`${libraryName}/${libraryDirectory}/${item.local.name}`)));
                newImports.push(t.importDeclaration([], t.stringLiteral(`${libraryName}/${libraryDirectory}/style.${style}`)))
            });
            //替换原节点
            path.replaceWithMultiple(newImports);                
        }
    }
}

}

现在,简单版本的@babel-plugin-import的babel插件我们已经完成了。
若感兴趣了解更多内容,babel插件中文开发文档提供了很多详细资料。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106485.html

相关文章

  • Babel 快速入门

    摘要:快速体验安装依赖新建文件夹,在命令行里进入该文件夹,并执行如下命令生成文件是内置的一个,可通过命令行操作来编译文件。入门为了确保转换后的代码能正常的运行,最好在代码之前引入这是一个实现了部分特性的包。参考中文网入门 简介 Babel 是一个 JavaScript 编译器,可将我们代码中的 ES6 语法转换为 ES5 的语法,使我们的代码能在不支持 ES6 语法的环境中正常运行。配合一些...

    NoraXie 评论0 收藏0
  • Babel入门插件开发

    摘要:最近的技术项目里大量用到了需要修改源文件代码的需求,也就理所当然的用到了及其插件开发。在这里我要推荐一款实现了这些标签的插件,建议在你的项目中加入这个插件并用起来,不用再艰难的书写三元运算符,会大大提升你的开发效率。具体可以参见插件手册。 最近的技术项目里大量用到了需要修改源文件代码的需求,也就理所当然的用到了Babel及其插件开发。这一系列专题我们介绍下Babel相关的知识及使用。 ...

    Jinkey 评论0 收藏0
  • 前端文档收集

    摘要:系列种优化页面加载速度的方法随笔分类中个最重要的技术点常用整理网页性能管理详解离线缓存简介系列编写高性能有趣的原生数组函数数据访问性能优化方案实现的大排序算法一怪对象常用方法函数收集数组的操作面向对象和原型继承中关键词的优雅解释浅谈系列 H5系列 10种优化页面加载速度的方法 随笔分类 - HTML5 HTML5中40个最重要的技术点 常用meta整理 网页性能管理详解 HTML5 ...

    jsbintask 评论0 收藏0
  • 前端文档收集

    摘要:系列种优化页面加载速度的方法随笔分类中个最重要的技术点常用整理网页性能管理详解离线缓存简介系列编写高性能有趣的原生数组函数数据访问性能优化方案实现的大排序算法一怪对象常用方法函数收集数组的操作面向对象和原型继承中关键词的优雅解释浅谈系列 H5系列 10种优化页面加载速度的方法 随笔分类 - HTML5 HTML5中40个最重要的技术点 常用meta整理 网页性能管理详解 HTML5 ...

    muddyway 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<