3

小蝌蚪传记:前端实用技巧,通过babel精准操作js文件

 2 years ago
source link: https://segmentfault.com/a/1190000041081789
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

小蝌蚪传记:前端实用技巧,通过babel精准操作js文件

今天是暗恋她的第90天,但我马上就要失恋了

因为组织架构变动,我要换到一个离她很远的地方

暗恋她的90天里,我一直在997

每天都在跟同类互相残杀

我厌倦了和一群老男人加班的日子

她是这段黑暗时间里,唯一的光

她曾是年会的女主持

万千男人暗恋的女神,而我只是个加班狗

她身边都是鲜花和掌声

我身边全是抠脚大汉和LSP

她每天穿着好看的衣服和名牌包包

而我只有一件粑黄色上衣,和一个装电脑的大黑包

我配不上她,但又被她深深的吸引

结婚7年来,我每天下班就回家

不抽烟不喝酒不近女色

腾讯男德第一人

现在居然对她产生了情愫

本以为我很专一,遇见她以后才明白

原来男人真的可以同时爱上好几个女人

离别之前,不知道要不要跟她表白

如果表白,她一定会说我是个好人

如果自己长的再好看点,是不是就不用自卑了

可惜没有如果,我是个完美的垃圾

就这样心烦意乱的上线了一版代码,配置文件还修改错了

直接崩出线上大bug

所有网页全部 404 not found

1000封报警邮件狂轰滥炸

boss委婉的发来1星绩效的暗示

配置文件这种东西,人工去修改,太容易受情绪影响而改错了

这些重复且易出错的操作,应该用工程化、自动化手段去解决

babel修改js配置文件实现原理

完整代码参考:github

像那些js配置文件,里面可能有很多的非配置代码,而且一次可能要修改好几个文件

比如我们在前端项目,要插入一个页面,需要修改router、menus等配置文件,还要手动拷贝页面模板等等

这些高重复机械化操作,人工修改非常容易出错

我们可以直接用babel来操作AST抽象语法树,通过工程化去精准修改。让babel去帮我们找到指定位置,并正确插入配置代码。我们在做工程化开发的时候,经常会用到babel去操作AST。

首先我们了解一下什么是AST

AST,抽象语法树(Abstract Syntax Tree)它是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构。

我们使用babel来转化和操作AST,主要分为三个步骤:解析(parser)、转换(traverse)、生成(generator)

操作AST三大阶段

如下图,如果我们想通过babel,在配置文件里面插入一段配置代码,应该怎么实现呢

解析(parser)

第一步:读取配置文件代码,并生成AST抽象语法树

let configJsData = fs.readFileSync(configJsPath, "utf8");

然后将配置文件代码生成AST抽象语法树

const parser = require("@babel/parser");

let configJsTree = parser.parse(`${configJsData}`,{
    sourceType: "module",
    plugins: [
      "jsx",
      "flow",
    ],
  });

configJsTree就是我们的AST了

加上sourceType: "module"这个配置属性,是为了让babel支持解析export和import

转换(traverse)

转换(traverse)阶段,就是要遍历整个AST抽象语法树,找到指定的位置,然后插入对应的配置代码。

代码如下:

const traverse = require("@babel/traverse").default;

traverse(configJsTree, {
    ObjectProperty(path) {
      // 插入配置文件代码
    },
  });

我们使用@babel/traverse的traverse方法进行遍历整个AST

其中ObjectProperty的作用是在遍历AST过程中,识别出所有的Object对象,因为我们是要将配置代码插入一个Object对象,所以使用的是ObjectProperty。如果要将配置插入数组中,就使用ArrayExpression

然后我们开始进行配置代码的插入,将代码

{
  key: "testPath",
  icon: HomeOutlined,
  exact: true,
}

插入如下的位置

我们需要在traverseObjectProperty进行位置的查找和代码插入

首先我们要找到key: 'home'的位置

代码如下:

traverse(configJsTree, {
    ObjectProperty(path) {
      if ( path.node.key.name === "key" && path.node.value.value === "home" ) {
        // 这就是 key: 'home'的位置
      }
    },
  });

通过path.node.key.namepath.node.value.value找到对象属性为key并且对象值为home的Object对象

找到位置后开始插入代码

traverse(configJsTree, {
    ObjectProperty(path) {
      // 搜索并识别出配置文件里key: "home" 这个object对象位置
      if ( path.node.key.name === "key" && path.node.value.value === "home" ) {
        path.parent.properties.forEach(e=>{
          if ( e.key.name === "children" ) {
           // 找到children属性
          }
        })
      }
    },
  });

通过path.parent.properties找到对象的父级,然后遍历父级下的所有属性,找到children这个属性。这就是我们要插入的位置。

接下来我们要构造要插入的数据

{
   key: "testPath",
   icon: HomeOutlined,
   exact: true,
}

构造数据的代码如下:

const t = require("@babel/types");

const newObj = t.objectExpression([
    t.objectProperty(
      t.identifier("key"),
      t.stringLiteral("testPath")
    ),
    t.objectProperty(
      t.identifier("icon"),
      t.identifier("HomeOutlined")
    ),
    t.objectProperty(
      t.identifier("exact"),
      t.booleanLiteral(true)
    ),
  ]);

可以看到用dentifier来标识对象的属性,用stringLiteral标识字符串数据,booleanLiteral标识boolean值,这些类型都可以在@babel/types查询到。

最后一步,将构造好的数据插入:

e.value.elements.push(newObj)

将所有 traverse 阶段代码汇总起来如下:

const traverse = require("@babel/traverse").default;
const t = require("@babel/types");

traverse(configJsTree, {
    ObjectProperty(path) {
      // 搜索并识别出配置文件里key: "home" 这个object对象位置
      if ( path.node.key.name === "key" && path.node.value.value === "home" ) {
        path.parent.properties.forEach(e=>{
          if ( e.key.name === "children" ) {
            const newObj = t.objectExpression([
              t.objectProperty(
                t.identifier("key"),
                t.stringLiteral("testPath")
              ),
              t.objectProperty(
                t.identifier("icon"),
                t.identifier("HomeOutlined")
              ),
              t.objectProperty(
                t.identifier("exact"),
                t.booleanLiteral(true)
              ),
            ]);
            e.value.elements.push(newObj)
          }
        })
      }
    },
  });

生成(generator)

这个阶段就是把AST抽象语法树反解,生成我们常规的代码

const generate = require("@babel/generator").default;

const result = generate(configJsTree, { jsescOption: { minimal: true } }, "").code;

fs.writeFileSync(resultPath, result);

通过@babel/generator将AST抽象代码语法树反解回原代码,jsescOption: { minimal: true }配置是为了解决中文为unicode乱码的问题。

至此咱们就完成了对js配置文件插入代码,最终结果如下:

以上就是通过babel操作AST,然后精准插入配置代码的全流程,完整代码参考:github

线上一切都稳定后

回头看了下女神

她正在照镜子涂口红

就静静坐着,都好喜欢好喜欢

她很爱笑、每周三都会去打篮球,早上10:30准时到公司、喜欢咖啡和奶茶

她的生活每天都是一首歌

而我只会敲代码,加班加到腿抽筋

据线人说,她已经有男朋友了,又高又帅又有钱

而我又老又丑、不仅穷还秃

听说她还很年轻,而我已经32

还有不到3年就35岁,我的时间不多了

怎么能留恋世间烟火呢

女人只会影响我敲代码的速度

哎。。。真该死

我还是默默地守护她、支持她、欣赏她吧

不打搅是屌丝最高级的告白

只是可惜的是

至始至终,我都不知道她叫什么

我们从没说过一句话

都是我一个人的独角戏

鲁迅说过:“我知道妳不是我的花,但能途经妳的盛放,我不胜荣幸”

。。。。。。

深夜空无一人的总部大楼

就这样默默坐在彼此的身边

就当我们也曾经在一起过吧

这次离别,可能就再也见不到了

不知道妳会不会想起我

也不知道我会不会爱上别的女孩

但是还是谢谢妳

惊艳了我每个加班的夜晚

goodbye my lover

————— yours 小蝌蚪

作者:第一名的小蝌蚪,公众号:第一名的小蝌蚪


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK