通过前两篇文章的介绍,大家已经了解了CreateRetrieve,我们接着介绍UpdateRemove操作。Update操作通常配合Create来完成。我们这篇文章主要介绍几个常用的NodePath`APIreplaceinsertremove`。具体也可以看babel-handbook中的Manipulation章节

replaceWith 使用新的节点进行替换

将加法运算替换成乘法

const code = `const c = a + b`
const ast = babylon.parse(code)

traverse(ast, {
  BinaryExpression(path) {
    // 注意这里要有判断,否则会无限进入`BinaryExpression`
    // https://stackoverflow.com/questions/37539432/babel-maximum-call-stack-size-exceeded-while-using-path-replacewith
    if (path.node.operator === '+') {
      path.replaceWith(t.binaryExpression('*', path.node.left, path.node.right))
    }
  }
})

console.log(generate(ast, {}, code).code) // const c = a * b;

this.count替换为this.data.count

  转换前后的AST展示如下图:

图this.count

  我们需要做的是,找到符合this.countThisExpression,然后把它替换为this.data

const code = `this.count`
const ast = babylon.parse(code)

traverse(ast, {
  MemberExpression(path) {
    if (
      t.isThisExpression(path.node.object) &&
      t.isIdentifier(path.node.property, {
        name: 'count'
      })
    ) {
      path
        .get('object')    // 获取`ThisExpresssion`
        .replaceWith(
          t.memberExpression(t.thisExpression(), t.identifier('data'))
        )
    }
  }
})
console.log(generate(ast, {}, code).code) // this.data.count;

replaceWithSourceString 直接使用代码替换

  上个例子中将this.count替换为this.data.count的部分,通过t.memberExpression可以构造node。更简单的操作可以直接使用replaceWithSourceString,个人觉得这个API很好用。

path.get('object').replaceWithSourceString('this.data')

插入操作

  插入是树操作的一种常见操作。子节点是个Array,前、中、后各种位置都可以插入新节点。下面来介绍下pushContainerunshiftContainerinsertBeforeinsertAfter操作。

  这里以给obj对象新增一个属性myprop: 'hello my property'为例:

const code = `
const obj = {
  count: 0,
  message: 'hello world'
}
`
const ast = babylon.parse(code)

const property = t.objectProperty(
  t.identifier('myprop'),
  t.stringLiteral('hello my property')
)

pushContainer 父节点的操作

  父节点为子节点Array插入一个node

traverse(ast, {
  ObjectExpression(path) {
    path.pushContainer('properties', property)
  }
})

insertAfter 兄弟节点的操作

  insertAfter也可以完成上述操作,需要找到message属性,然后在后面插入node就搞定啦

traverse(ast, {
  ObjectProperty(path) {
    if (
      t.isIdentifier(path.node.key, {
        name: 'message'
      })
    ) {
      path.insertAfter(property)
    }
  }
})

  unshiftContainerinsertBefore与上面两个相对应,这里不再举例了,大家可以自己试一试。

  因为properties是个数组,因此,我们可以直接使用数组操作

traverse(ast, {
  ObjectExpression(path) {
    // path.pushContainer('properties', property)
    path.node.properties.push(property)
  }
})

Remove 自我毁灭

  Remove方法极为简单,找到要删除的NodePath,执行Remove就结束了。如上述代码,我们要删除message属性,代码如下:

traverse(ast, {
  ObjectProperty(path) {
    if (
      t.isIdentifier(path.node.key, {
        name: 'message'
      })
    ) {
      path.remove()
    }
  }
})

到目前为止,AST的CURD我们都介绍完了,下面一篇文章以vue小程序为例,我们来实战一波。