怎么满屏的console.log(...)前言 最近接手了一个已经维护了很长时间的后台管理系统,这个项目依旧使用的是 V
前言
最近接手了一个已经维护了很长时间的后台管理系统,这个项目依旧使用的是 Vue 2。虽然开发上的问题不多,但还是需要做一些本地性的修改和维护。想到这,摸鱼的时间好像又多了几分。 然而,当我打开项目并启动后,发现控制台的打印信息堆积成了一整页。这可不行!不考虑内存泄漏或其他问题,光是调试时,想要在这些杂乱的信息中找到自己的打印内容都十分困难。虽然可以通过在打印时添加特殊符号来区分,但这样做显然很麻烦。
初步想法
恰逢最近看来一些 Webpack 的一些东西(八股文),了解到 loader 和 plugins 的概念。
在 Webpack 中,Loader
是一种转换工具,它的主要功能是对模块的源代码进行转换处理。简而言之,Loader 本质上是一个函数,它接受源代码作为输入,经过处理后返回转换后的代码。Webpack 在处理文件时,会使用 Loader 对文件进行预处理。
相比于 Loader
,Plugins
的功能更为强大和灵活。Plugins 是 Webpack 插件系统的一部分,几乎可以用来处理任何构建过程中的操作。Plugins 运行在 Webpack 的整个生命周期阶段,从构建过程的开始到结束,可以对构建过程进行深度的定制和优化。
所有我想是否可以写一个plugins的插件,来自动修改 console.log
语句,使其包含一个特殊的符号。这不仅有助于调试,还能帮助我们在控制台中快速识别出特定的日志信息。
初步实现
定义一个名为log
的类,这个类接受一个特殊符号作为参数。正则表达式 reg
用于匹配所有 console.log(...)
语句。Object.keys(compilation.assets)
包含了所有的构建产物文件,使用 forEach 遍历每一个文件并进行修改返回。
在 Plugin 内进行调用时,apply
方法会被 Webpack Compiler 调用,并且在整个编译生命周期都可以访问 Compiler 对象。
class log {
constructor(icon) {
this.icon = icon;
}
apply(compiler) {
const _that = this;
const reg = /(console\.log\([^)]*\))/g;
compiler.hooks.emit.tap("log", (compilation) => {
Object.keys(compilation.assets).forEach((item) => {
let content = compilation.assets[item].source();
// 确保 content 是字符串
if (Buffer.isBuffer(content)) {
content = content.toString();
}
content = content.replace(reg, function (match, p1) {
console.log(match, "!!!!!");
return match.slice(0, -1) + `,'${_that.icon}')`;
});
// 更新构建产物对象
compilation.assets[item] = {
source: () => content,
size: () => content.length,
};
});
});
}
}
module.exports = log;
我们可以在 plugins
配置中实例化 log
插件,并传入图标参数:
plugins: [
new log('🚀🚀🚀') // 在这里实例化 log 插件,并传入图标
]
这样就是实现了初步的效果,如下:
这样所有的console.log(...)
就会带上你所传入的特殊符号,这样的话其实是没什么太大的用处的,所以下一步需要实现这样的效果
效果进阶
上面的初步实现只是一个简单的效果,但我们还需要考虑一些更复杂的情况。比如,当 console.log
打印多个变量或者是函数时,我们需要进一步处理。
首先,如果 source
是函数类型,则需要执行函数以获取字符串。
// 如果是函数,执行以获取字符串
if (typeof source === "function") {
source = source();
}
函数的问题解决了的话我们要解决的就是多个变量打印的问题,
if (typeof source === "string") {
// 使用正则表达式替换源代码中的 console.log() 语句
source = source.replace(reg, function (match, p1) {
const args = match.slice(12, -1);
const newArgs = args
.split(",")
.map((item) => {
return _that.customLog(item);
})
.join(",");
return `console.log(${newArgs},'${_that.icon}')`;
});
// 更新构建产物对象
compilation.assets[item] = {
source: () => source, // 返回函数
size: () => source.length,
};
}
这里,我们对 console.log
内的所有变量进行分割,遍历处理后,再将其组装回 console.log()
内。customLog 的实现如下
customLog(variableValue) {
if(variableValue.includes("'") || variableValue.includes('"')){
return variableValue
}else{
let str = variableValue.toString();
return `'${str}=',${variableValue}`
}
}
这里接受一个到传入来的变量,判断是否是字符串,如果直接打印的字符串的话直接返回就可以了,如果不是的话,那么就进行分割遍历结果就会如下了
结尾
总体代码如下
class log {
constructor(icon) {
this.icon = icon;
}
apply(compiler) {
const _that = this;
// 正则表达console.log()
const reg = /(console\.log\((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*\))/g;
compiler.hooks.emit.tap("log", (compilation) => {
// 遍历所有的构建产物
Object.keys(compilation.assets).forEach((item) => {
// 获取当前构建产物的源代码
let source = compilation.assets[item].source();
// 如果是函数,执行以获取字符串
if (typeof source === "function") {
source = source();
}
// 检查source是否是字符串
if (typeof source === "string") {
// 使用正则表达式替换源代码中的 console.log() 语句
source = source.replace(reg, function (match, p1) {
const args = match.slice(12, -1);
const newArgs = args
.split(",")
.map((item) => {
return _that.customLog(item);
})
.join(",");
return `console.log(${newArgs},'${_that.icon}')`;
});
// 更新构建产物对象
compilation.assets[item] = {
source: () => source, // 返回函数
size: () => source.length,
};
} else {
console.log(source);
console.error("Error: Compilation asset source is not a string.");
}
});
});
}
customLog(variableValue) {
if (variableValue.includes("'") || variableValue.includes('"')) {
return variableValue;
} else {
let str = variableValue.toString();
return `'${str}=',${variableValue}`;
}
}
}
module.exports = log;
其实这个主要是为了学习如何编写一款插件,因为我也是在学习中,所以很多地方可能会存在问题。有兴趣的可以自己拓展学习一些,例如不对node_model下的进行处理,或者是在正式环境打包的时候删除掉 console.log()。主要是为了熟悉一下plugins的插件
转载自:https://juejin.cn/post/7405420213469085748