Babylon-AST初探査-実戦
7997 ワード
以前の3つの文章の紹介を経て、
次の例のコアコードは依然として最も簡単な
本明細書のいくつかの操作を経て、最終的なウィジェットコードは以下の通りです.
注意:、私たちが前に紹介したのと一致して、上述の変換を完成するために、入力と出力をすべてAST explorerに入れて、その前後の構造の対比を見ます.
文章の最初に示した2つのコードを比較し、変換を実現するには、次の手順が必要です. を削除する. を削除する.は、すべての のみを回転する.変更 をトリガする.
次はこの手順に従って、一歩一歩変換を完了し、一歩一歩コードの変化を見ると達成感があると思います.
このステップでは、元の
しかし、このコードの可読性とロバスト性は基本的に0でしょう.それは私たちが書いた
プログラム出力:
ここでは構造が固定されているため、
プログラム出力:
このステップでは、まず
これは私たちの転化方法に合わないだろう.もちろん、値を求めることで最終的なオブジェクトを得ることができますが、ここにも欠陥があります.もう1つの考え方は、他のメンバー関数を遍歴し、除外法を使用することです.
要するに、
プログラム出力:
データ属性を
このプログラムの出力:
変更
挿入の場所を見つけたら、挿入する関数を構築します.このシリーズの最初の記事で紹介した(挿入するコードの位置を見つけるには、まず付与操作かどうかを判断し、もしそうであれば を見つける.新規挿入するノード 挿入ノード
プログラム出力:
以上は私たちの実戦紹介です.こちらは
AST
のCRUD
はすでに完成した.以下は主にvue
から
に移行する過程で使用する重要な技術の一部を通じて実戦する.次の例のコアコードは依然として最も簡単な
vue
例である.const babylon = require('babylon')
const t = require('@babel/types')
const generate = require('@babel/generator').default
const traverse = require('@babel/traverse').default
const code = `
export default {
data() {
return {
message: 'hello vue',
count: 0
}
},
methods: {
add() {
++this.count
},
minus() {
--this.count
}
}
}
`
const ast = babylon.parse(code, {
sourceType: 'module',
plugins: ['flow']
})
本明細書のいくつかの操作を経て、最終的なウィジェットコードは以下の通りです.
Page({
data: (() => {
return {
message: 'hello vue',
count: 0
}
})(),
add() {
++this.data.count
this.setData({
count: this.data.count
})
},
minus() {
--this.data.count
this.setData({
count: this.data.count
})
}
})
注意:、私たちが前に紹介したのと一致して、上述の変換を完成するために、入力と出力をすべてAST explorerに入れて、その前後の構造の対比を見ます.
vue
コード回転ウィジェット文章の最初に示した2つのコードを比較し、変換を実現するには、次の手順が必要です.
data
関数をdata
属性に変換し、data
関数methods
の属性を抽出し、data
と同じレベルに配置し、methods
もthis.[data member]
をthis.data.[data member]
に変換する.ここではdata
の属性this.data
の下にthis.setData
を挿入するデータ変更次はこの手順に従って、一歩一歩変換を完了し、一歩一歩コードの変化を見ると達成感があると思います.
data
属性の生成このステップでは、元の
data
関数のreturn
のオブジェクトを抽出します.AST explorerと組み合わせると、このパスを簡単に見つけることができます.const dataObject = ast.program.body[0].declaration.properties[0].body.body[0].argument
console.log(dataObject)
しかし、このコードの可読性とロバスト性は基本的に0でしょう.それは私たちが書いた
data
関数に強く依存しているのが最初の属性です.ここでは主にtraverse
を使用してノードにアクセスします.traverse(ast, {
ObjectMethod(path) {
if (path.node.key.name === 'data') {
// BlockStatement, data
let blockStatement = null
path.traverse({ // traverse
BlockStatement(p) {
blockStatement = p.node
}
})
// blockStatement ArrowFunctionExpression
const arrowFunctionExpression = t.arrowFunctionExpression([], blockStatement)
// CallExpression
const callExpression = t.callExpression(arrowFunctionExpression, [])
// data property
const dataProperty = t.objectProperty(t.identifier('data'), callExpression)
// data
path.insertAfter(dataProperty)
// data
path.remove()
// console.log(arrowFunctionExpression)
}
}
})
console.log(generate(ast, {}, code).code)
プログラム出力:
export default {
data: (() => {
return {
message: 'hello vue',
count: 0
};
})(),
methods: {
add() {
++this.count;
},
minus() {
--this.count;
}
}
};
methods
の属性を1レベル上げるここでは構造が固定されているため、
methods
の属性はtraverse
を採用していない.traverse(ast, {
ObjectProperty(path) {
if (path.node.key.name === 'methods') {
// methods
path.node.value.properties.forEach(property => {
path.insertAfter(property)
})
// methods
path.remove()
}
}
})
プログラム出力:
export default {
data: (() => {
return {
message: 'hello vue',
count: 0
};
})(),
minus() {
--this.count;
},
add() {
++this.count;
}
};
this.member
からthis.data.member
へこのステップでは、まず
data
属性からデータ属性を抽出します.これはdata
の関数に依存していますが、どのように書かれていますか. data: (() => {
const obj = {}
obj.message = 'hello vue'
obj.count = 0
return obj
})(),
これは私たちの転化方法に合わないだろう.もちろん、値を求めることで最終的なオブジェクトを得ることができますが、ここにも欠陥があります.もう1つの考え方は、他のメンバー関数を遍歴し、除外法を使用することです.
要するに、
this.data
のプロパティを取得する方法が必要です.本明細書では、data
のreturn
メソッドによって、コードの例で引き続き取得する.// `this.data`
const datas = []
traverse(ast, {
ObjectProperty(path) {
if (path.node.key.name === 'data') {
path.traverse({
ReturnStatement(path) {
path.traverse({
ObjectProperty(path) {
datas.push(path.node.key.name)
path.skip()
}
})
path.skip()
}
})
}
path.skip()
}
})
console.log(datas)
プログラム出力:
[ 'message', 'count' ]
データ属性を
this.data.
に変更traverse(ast, {
MemberExpression(path) {
if (path.node.object.type === 'ThisExpression' && datas.includes(path.node.property.name)) {
path.get('object').replaceWithSourceString('this.data')
}
}
})
このプログラムの出力:
export default {
data: (() => {
return {
message: 'hello vue',
count: 0
};
})(),
minus() {
--this.data.count;
},
add() {
++this.data.count;
}
};
this.setData
メソッドの追加変更
this.data
の下にthis.setData
を挿入するには、まずその挿入位置、すなわちthis.data
の親ノードを見つけなければならないので、これが私たちの最初の操作です.(MemberExpression
は前のステップです.このステップのpathは前のステップと同じですから)traverse(ast, {
MemberExpression(path) {
if (path.node.object.type === 'ThisExpression' && datas.includes(path.node.property.name)) {
path.get('object').replaceWithSourceString('this.data')
}
}
const expressionStatement = path.findParent((parent) =>
parent.isExpressionStatement()
)
})
挿入の場所を見つけたら、挿入する関数を構築します.このシリーズの最初の記事で紹介した(
Create
)[https://summerrouxin.github.i...]操作、忘れたのは復習してもいいですよ.次は直接コードをつけます.このコードは必ずAST explorerhとbabel-types
のAPI
と照らし合わせて、外から内への層の対照を見つけなければなりません.このコードの論理は次のようになります.this.member
の親ノードtraverse(ast, {
MemberExpression(path) {
if (path.node.object.type === 'ThisExpression' && datas.includes(path.node.property.name)) {
path.get('object').replaceWithSourceString('this.data')
//
if(
(t.isAssignmentExpression(path.parentPath) && path.parentPath.get('left') === path) ||
t.isUpdateExpression(path.parentPath)
) {
// findParent
const expressionStatement = path.findParent((parent) =>
parent.isExpressionStatement()
)
// create
if(expressionStatement) {
const finalExpStatement =
t.expressionStatement(
t.callExpression(
t.memberExpression(t.thisExpression(), t.identifier('setData')),
[t.objectExpression([t.objectProperty(
t.identifier(propertyName), t.identifier(`this.data.${propertyName}`)
)])]
)
)
expressionStatement.insertAfter(finalExpStatement)
}
}
}
}
})
プログラム出力:
export default {
data: (() => {
return {
message: 'hello vue',
count: 0
};
})(),
minus() {
--this.count;
this.setData({
count: this.data.count
})
},
add() {
++this.count;
this.setData({
count: this.data.count
})
}
};
以上は私たちの実戦紹介です.こちらは
vue
から
へのコードの一部にすぎません.後で他のモジュールの紹介を続けることも考えられます.