javascript 元编程之-代码修改代码
引言
重构代码是个体力活,特别是在确定重构方案后,剩下就是按方案调整代码,然后进行测试。
如何有好又快的调整到位代码,这是件不容易的事。
简单的代码,可以使用正则表达示进行,如果稍微复杂一点代码,正则表达示就无能为力了。
如我上一篇中讲的修改方法javascript 元编程之 method_missing,我可以在工程中修改一处,如果要修改 50 次接口呢? 我得想想这次重构值不值得了。
还能不能让我随心所欲的写代码了。。。
于是我找到了 jscodeshift,让我能用代码重构我的代码的工具。
jscodeshift 让我有了用代码操作代码的工具。
实现
实例我就使用我上篇文章中的 method_missing 项目,我想把我项目中的接口调用(用url),都调整成 method_missing 调用方式。
如下:
// old
$.ajax('/api/web/project/content/target/mod', {
type: 'post',
dataType: 'json',
data: {
tid: it.targetId,
targetImgId: img.objectId,
pid: pid
}
}).done(function (data, status, jqXhr) {
self.hideLoading();
if (data && data.code === 200 && data.data) {
it.target = data.data
// self.paginationConf.onChange()
}
}).fail(() => {
}).always(() => {
})
// new
this.$api.update_project_content_target_mod({
tid: it.targetId,
targetImgId: img.objectId,
pid: pid
}).then(function (data, status, jqXhr) {
self.hideLoading();
if (data && data.code === 200 && data.data) {
it.target = data.data
// self.paginationConf.onChange()
}
}).catch(() => {
}).finally(() => {
})
可以看出新的代码中,把老代码中的 $.ajax 换成了 this.$api,然后用参数生成调用方法名。
最后不仅调整了参数的个数,还调整了参数的值。
想想正则怎么写?
正则应该无能为力。
这时 jscodeshift 就上场了,它能把代码解析成 ast , 然后我们修改 ast,ast 再变成代码。
我的代码如下:
const adapt = require('vue-jscodeshift-adapter');
// 修改 $.ajax
function transform(fileInfo, api) {
const j = api.jscodeshift
return j(fileInfo.source)
.find(j.CallExpression, { callee: { object: { name: '$' }, property: { name: 'ajax' } } })
.replaceWith(p => {
const url = p.node.arguments[0]
const setting = p.node.arguments[1]
const urlStr = url.value.replace('/api/web/', '').split('/')
let method = 'get'
let newSetting
setting.properties.forEach(i => {
if (i.key.name.toLowerCase() === 'type') {
if (i.value.value.toLowerCase() === 'post') {
method = 'update'
}
} else if (i.key.name.toLowerCase() === 'data') {
newSetting = i.value
}
});
urlStr.unshift(method);
return j.callExpression(
j.memberExpression(
j.memberExpression(j.identifier('this'), j.identifier('$api'), false),
j.identifier(urlStr.join('_')), false
), [newSetting])
})
.toSource();
}
// 修改 done
function transform1(fileInfo, api) {
const j = api.jscodeshift
return j(fileInfo.source)
.find(j.MemberExpression, { property: { name: 'done' } })
.replaceWith(p => {
p.node.property.name = 'then'
return p.node
})
.toSource();
}
// 修改 fail
function transform2(fileInfo, api) {
const j = api.jscodeshift
return j(fileInfo.source)
.find(j.MemberExpression, { property: { name: 'fail' } })
.replaceWith(p => {
p.node.property.name = 'catch'
return p.node
})
.toSource();
}
// 修改 always
function transform3(fileInfo, api) {
const j = api.jscodeshift
return j(fileInfo.source)
.find(j.MemberExpression, { property: { name: 'always' } })
.replaceWith(p => {
p.node.property.name = 'finally'
return p.node
})
.toSource();
}
module.exports = adapt(transform);
// module.exports = adapt(transform1);
// module.exports = adapt(transform2);
// module.exports = adapt(transform3);
如何使用
# 使用 bash 循环调用
jscodeshift -t src/myTransforms.js filepath
因为我的代码是 vue 的单文件模块,所以我用的了一个第三方插件 vue-jscodeshift-adapter。
总结
jscodeshift 对于大型工程是重构是很有帮助的。可以让你快速改写大量的代码,而且方便测试。