前言
上家公司的工作是偏基础架构方向的,当时主要技术栈还是vue2 + element-ui,在编写前端工程模板的时候,针对页面缓存这个模块的实现也是常用的vuex + 内置的<keep-alive>组件,开发人员在使用模板的过程中,常常会出现缓存失败的现象,后来发现是在页面中没有配置name属性造成的,所以我决定编写一个工具来帮助开发人员自动加上name属性。
loader实现
关于工具的实现,下面给出了详细代码与注释:
const parseVue = require('@vue/compiler-sfc').parse;
const { parse } = require('@babel/parser');
// const { getOptions } = require('loader-utils');
const t = require('@babel/types');
const generator = require('@babel/generator');
const traverse = require("@babel/traverse");
const convertPath = require('path');
module.exports = function nameLoader(content) {
// 获取文件路径参数
let params = new URL(this.resource).searchParams;
// 获取自定义文件name
const componentName = params.get('componentName');
// 获取参数确认是否本地测试,参数从位置为2开始获取,默认参数0为node安装目录,参数1为当前文件目录
const isDev = process.argv[2] && process.argv[2] === 'dev';
// vue-loader转化后type值有script|styles|template,只对type为script部分做转化
if (params.get('type') === 'script' || isDev) {
// 通过getOptions获取webpack中配置loader时传入的options参数,可以做一些其他定制化配置,webpack5内置了该方法(this.getOptions)
// const options = getOptions(this) || {};
// script原内容,vue-loader转换以后不需要再重新parse
const script = (isDev ? parseVue(content) : content).descriptor.script.loc.source;
// 转化成ast
const ast = parse(script, {
sourceType: 'module'
});
const resourcePath = this.resourcePath;
// 遍历ast
traverse.default(ast, {
// 访问export default节点
ExportDefaultDeclaration(path) {
// 是否有name字段
const node = path.node.declaration.properties.find(item => item.key.name === 'name');
if (!node) {
// 是否当前节点下第一个property类型的节点
let isFirst = true;
// 遍历当前ast节点,找到ObjectExpression对象,(包含.vue文件中定义的选项data,method等)
path.traverse({
ObjectExpression(childPath) {
if (isFirst) {
// 找到路径并插入name属性
childPath.traverse({
enter(childPath) {
if (isFirst) {
const basename = componentName ? componentName : convertPath.basename(resourcePath);
childPath.insertBefore(t.objectProperty(t.identifier('name'), t.stringLiteral(basename.split('.')[0])));
isFirst = false;
}
}
})
}
}
})
}
}
})
// ast2code
const { code } = generator.default(ast);
// 替换没有name字段的原内容
const newContent = content.replace(script, code);
return newContent;
} else {
return content;
}
}
上方关于Ast的操作大家可以把自己的代码拷贝到这个网站 astexplorer.net/ 转化成Ast结构,找到自己想要的属性的位置再进行相关操作。
大家感兴趣的话也可以npm install vue-name-loader,玩一玩儿。