JS脚本 - 批量给所有指定标签追加Class属性
- 前言
- 一. 脚本
- 二. 测试运行
前言
公司里我们有个应用引入了UBT埋点,记录了页面上所有的点击操作以及对应的点击按钮。但是我们看下来发现,我们需要给每个按钮加一个唯一标识做区分,并且这个ID是给UBT埋点专用的。那怎么办呢?
- 加
Id
: 不合理,因为如果某个标签已经自带Id
了,我们不太可能去覆盖它。 - 加
Class
:合理,class
属性可以加多个,也可以过滤。
一. 脚本
const fs = require('fs');
const path = require('path');
// 读取文件内容
const stack = [];
const classRegExp = /class="([^"]*)"/;
const idRegExp = /id="([^"]*)"/;
const args = process.argv.slice(2)
if (!args || args.length < 4) {console.log('参数不合法')
}const TagMapping = [{Tag: 'a',ATagPre: '<a',ATagSuffix: '</a>'
}, {Tag: 'el-button',ATagPre: '<el-button',ATagSuffix: '</el-button>'
}, {Tag: 'el-dialog',ATagPre: '<el-dialog',ATagSuffix: '</el-dialog>'
}, {Tag: 'button',ATagPre: '<button',ATagSuffix: '</button>'
}]
const currentTagRegExp = new RegExp(`(<${'a'}\\s)([^>]*)(>)`, 'i');function contentAddUbtClass(filePath, tag, classPre) {const tagInfo = TagMapping.filter(item => item.Tag === tag)[0];if (!tagInfo) {return;}const content = fs.readFileSync(filePath, 'utf8');let resultContent = ''const preLen = tagInfo.ATagPre.length;const stuffixLen = tagInfo.ATagSuffix.length;for (let i = 0; i < content.length; i++) {// a标签开始if (i + preLen < content.length && content.substring(i, i + preLen) === tagInfo.ATagPre) {stack.push(i);} else if (i + stuffixLen < content.length && content.substring(i, i + stuffixLen) === tagInfo.ATagSuffix) {if (stack.length > 0) {// 出栈,找到起始索引位置const start = stack.pop();// 栈为空的时候,才开始截取并替换,这决定了代码取的标签内容都是最外层的结构if (stack.length === 0) {// a 标签const aTagHtml = content.substring(start, i + stuffixLen);const newHtml = addClass(aTagHtml, i, tag, classPre);resultContent += newHtml;i = i + stuffixLen - 1;}}} else {// 栈为空的时候,说明这个时候还没有识别到对应标签,可以添加其他内容。若识别到了对应标签,那么对应标签包裹的内容在这里不会添加if (stack.length === 0) {resultContent += content.charAt(i);}}}fs.writeFileSync(filePath, resultContent, 'utf8');
}function addClass(content, index, tag, classPre) {// 匹配结果const classMatch = content.match(classRegExp);const idMatch = content.match(idRegExp);// class属性值let className;if (content.includes(classPre)) {return content;}if (idMatch) {// 如果匹配到了id属性,则在原有的class属性值上添加新的样式名称className = `${classPre}${tag}_` + idMatch[1];// 如果没有匹配到class属性,则在原有的文本中添加新的class属性if (classMatch) {// 追加classclassName = classMatch[1] + ` ${className}`;}} else if (classMatch) {// 如果没有匹配到id属性,但匹配到了class属性,则在原有的class属性值上添加新的样式名称className = classMatch[1] + ` ${classPre}${tag}_${index}`;} else {// 如果没有匹配到id属性和class属性,则生成一个新的class属性值className = `${classPre}${tag}_${index}`;// 在原有的文本中添加新的class属性// content = content.replace(currentClassRegExp, `<$1 class="${className}"`);}// 替换原有的class属性值return content.replace(currentTagRegExp, (match, p1, p2, p3) => {// 判断标签原本是否包含了class属性if (p2.includes('class=')) {return `${p1}${p2.replace(classRegExp, `class="${className}"`)}${p3}`;} else {return `${p1}${p2} class="${className}"${p3}`;}});
}// 遍历指定目录下的所有文件
function traverseDirectory(dirPath, extension, tag, classPre) {// 读取目录下的所有文件和子目录const files = fs.readdirSync(dirPath);// 遍历每个文件和子目录files.forEach((file) => {// 获取文件的完整路径const filePath = path.join(dirPath, file);// 判断文件是否是目录if (fs.statSync(filePath).isDirectory()) {// 如果是目录,则递归traverseDirectory(filePath, extension, tag, classPre);} else {// 如果是文件,则判断文件后缀名是否匹配指定的后缀名if (extension.includes(path.extname(filePath))) {// 如果匹配,则输出文件路径contentAddUbtClass(filePath, tag, classPre)}}});
}
traverseDirectory(args[0], args[1], args[2], args[3])
二. 测试运行
假设,我要给页面所有的a
标签,添加一个class
属性,固定的前缀是UBT_
,准备一个html
文件:
执行脚本:
node .\test.js './' '.html' 'a' 'UBT_'
node 脚本名称 [路径] [匹配的文件后缀(支持多个)] [匹配的标签名称] [增加的class前缀名]
结果如下:
大家也可以在这个脚本的基础上自行二创。