Console党福音!封装 console.log 再也不用打注释了!

32,158 阅读5分钟

背景

大家好~, 我是刚入职场不久的前端小帅,每周分享我的一丢丢想法,希望能够帮助到大家~

众所周知,console在项目开发调试中是不可或缺的,在日常工作中经常会使用到它,但使用多了之后会发现,虽然它足够短小精炼,但耐不住用的多,再加上注释指示,工作量隐约还是不小的(懒惰,因此我对它进行小小的封装,让它用起来更短更方便,分享给大家,希望能够对大家开发有所帮助

优势

  • 面向 Vue2
  • 拒绝通用的注释打印
  • 针对对象值自动序列化
  • 支持一行输出 N个变量打印
  • 支持输出data变量和局部变量
  • 支持限制特定页面输出变量
  • 更短的函数名

示例:

data() {
    return {
        joker: 'jym',
        handsomeMan: 'me',
        info: {
            job: {
                name: '专业炒粉'
            }
        }
    }
},
methods: {
    getJoker() {
        const demo = 'test'
        this.$c('joker', 'handsomeMan', 'info', 'info.job.name', { demo }) //输出语句
    }
}

输出结果:
image.png

**是的,只需要一行代码!!! **

是的,只需要一行代码!!!

(如果是原来的console.log打印 光想就已经累了......)

封装

接下来看看是如何封装的吧~

方法名再短点!

// main.js
Vue.prototype.$c = function() {...}

因为要全局使用,因此直接挂载到 Vue 的原型对象上,这样各个组件里都能通过this.$c()进行调用

传参与打印

如何访问对应Vue实例的数据

因为我们需要访问this.xxx, 获取到this指向必不可缺,因此结合this的小知识,封装的函数必须是普通函数而不是箭头函数,这样在vue实例中调用时this.$cthis会指向Vue实例自身,我们就可以访问this下的数据啦~

传参

参数肯定需要支持多个,数组冗余了 ,直接扁平化输入
再结合Vue里是通过this.xxx获取数据,可知我们传入的需要是字符串
可得步骤

  • 传入多个待打印的变量名
  • 遍历每个变量名
  • 通过 this变量名 获取到data里的值
  • 判断是否为对象,分别处理
  • 打印
  • over
// main.js
Vue.prototype.$c = function(...words) {
    words.forEach(word => {
        const val = this[word]
        if (Object.prototype.toString.call(val).slice(8, -1) === 'Object') {
            console.log(`${word} ======> `)
            console.log(JSON.stringify(val, null, 2))
            return
        }
        console.log(`${word} ======> `, val)
        return
    })
}

对于对象类型的变量,通常我们需要获取实时的对象,直接console.log(obj), 但这样获取的obj通常不是准确的值,想要获取实时的值则需要调用console.log(JSON.stringify(obj, null, 2))通过json字符串化打印实时的值

什么?JSON.stringify后面两个参数是干嘛的?

我TM直接就是一个

参数一 接收 函数或数组,用来转换过滤对象
参数二 接收 数字或字符串,用于指定缩进的空格数或缩进字符串

示例:

const obj = {
    name: 'mach',
    age: 24,
    sex: 1
}
console.log(JSON.stringify(obj, ['name', 'age']))
// {"name":"mach","age":24}

console.log(JSON.stringify(obj, ['name', 'age'], 2))
// {
//   "name": "mach",
//   "age": 24
// }

OK, 这个时候我们进行打印测试一下

//xxx.vue
data() {
    return {
        name: 1,
        age: 2,
        obj: {
            name: 'mach',
            age: 24,
            sex: 1
        }
    }
},
created() {
    this.$c('name', 'age', 'obj')
}

image.png

这时候看看效果,注释有了!变量的值也有了!

局部变量

现在能够访问Vue实例的值了,那少不了局部变量。
问题来了,访问Vue实例是应用了this指向,那局部变量该怎么获取到呢?

最核心的肯定获取是局部变量的执行上下文

呃呃呃呃呃 闭包? eval 试了一下貌似都不行

呃呃呃 , 有了,就是你了!

// xxx.vue
methods:{
    getList() {
        const joker = 'you'
    }
}
this.$c({ 'joker': joker })

高端的食材,往往只需要最朴素的烹饪方式~
忙碌了两小时之后,小师傅开始传入对象

直接用对象传变量名和变量值!

// 简写
this.$c({ joker })

瞧瞧 { joker } 同样很短小,非常适合开发,美吱吱~~

于是函数长这样,对局部变量和Vue data里的变量做区分:

  • 判断 对象 则是局部变量
  • 判断 字符串 则是vue实例数据
Vue.prototype.$c = function(...words) {
    words.forEach(word => {
        if (typeof word === 'string') {
            const val = this[word]
            if (Object.prototype.toString.call(val).slice(8, -1) === 'Object') {
                console.log(`${word} ======> `)
                console.log(JSON.stringify(val, null, 2))
                return
            }
            console.log(`${word} ======> `, val)
            return
        }
        const [name, value] = Object.entries(word)[0]
        if (Object.prototype.toString.call(value).slice(8, -1) === 'Object') {
            console.log(`${name} ======> `)
            console.log(JSON.stringify(value, null, 2))
            return
        }
        console.log(`${name} ======> `, value)
    })
}

使用

// xxx.vue
methods:{
    getList() {
        const joker = 'you'
        this.$c('name', 'age', 'obj', { joker })
    }
}

image.png

Perfect ~

指明位置及套个漂亮显眼的框

对了,因为$c的函数是定义在main.js里的,因此控制台打印会显示是出现在main.js

image.png

解决办法就是在壳里加个 this.$options.name,指明调试语句来自哪个组件
再给打印区套个漂亮的壳,便于做区分

Vue.prototype.$c = function(...words) {
    console.log(`来自${this.$options.name}-🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉`) // 新增
    words.forEach(word => {
        if (typeof word === 'string') {
            const val = this[word]
            if (Object.prototype.toString.call(val).slice(8, -1) === 'Object') {
                console.log(`${word} ======> `)
                console.log(JSON.stringify(val, null, 2))
                return
            }
            console.log(`${word} ======> `, val)
            return
        }
        const [name, value] = Object.entries(word)[0]
        if (Object.prototype.toString.call(value).slice(8, -1) === 'Object') {
            console.log(`${name} ======> `)
            console.log(JSON.stringify(value, null, 2))
            return
        }
        console.log(`${name} ======> `, value)
    })
    console.log('🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉')
}

测试打印结果:

image.png

圆满结束啦

访问嵌套属性的值

还有嘞 ~ 有人问,刚刚只是访问变量第一层,如果只想访问变量的第n层怎么办?

很简单,核心就是嵌套调用~

const val = this[word]

改成

const val = word.split('.').reduce((pre, cur) => {
    return pre[cur]
}, this)

这个时候通过 xxx.xxxx.xx 就可以访问Vue实例下数据的值啦~

data(){
    return {
        obj: {
            name: 'joker'
            age: 24
        }
    }
}
this.$c('obj.age', 'obj.name')

image.png
这里xxx.xxxx.xxxxx|xxxx|xxxxx-xxxx-xx没啥区别,就是做分割罢了,但用.好分辨点贴近开发

解决完Vue实例下数据的嵌套访问,这时候有人会问,访问局部变量的嵌套属性咋办

Emmm 好问题 , 转动我的机灵小脑袋~~~~~ 有了!!!

const obj = {
    accont: {
        xx: {
            id: 11,
            name: 'rich'
        },
        money: 10086
    }
}
const xx = obj.accont.xx   // 新增

// 调用
this.$c({xx})

高端的食材,往往只需要最朴素的烹饪方式 ~

const joker = {
    info: {
        name: 'jym',
        age: 18,
        height: 185
    }
}
const name = joker.info.name
this.$c({ joker }, { name })   

image.png

这样纸代码看起来也清晰嘛~ 嗯,针不戳

限制特定页面输出变量

假设我现在只需要path1path2的页面输出变量,关闭其他页面的输出,咋做嘞~

简简单单,咱已经拿到指向Vue实例的this,可以直接访问当前路由地址和名做一下限制即可

在函数开头加上

Vue.prototype.$c = function(...words) {
    const whitelist = ['path1', 'path2']
    const currentPath = this.$route.path

    if (!whitelist.includes(currentPath)) return
    ......
}

现在就只有path1, path2的页面下的组件可以输出啦,是不是很方便呢?

只有 Vue2 可以这么封装吗?

虽然这个封装的代码是面向 Vue2, 但很显然封装的核心对于Vue是通用的,把this换成组件实例即可,如Vue3里是this(选项式) 或者 getCurrentInstance()(组合式),至于React,就只能朴素一丢丢喏(你懂得)

代码 没写 我就不贴了

集帅们 也可以把代码打在评论区,供参考借鉴~~

总结

全部代码:

Vue.prototype.$c = function(...words) {
    const whitelist = ['path1', 'path2']
    const currentPath = this.$route.path
    if (!whitelist.includes(currentPath)) return
    
    console.log(`🎉🎉🎉🎉🎉🎉🎉🎉来自-${this.$options.name}-🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉`)
    words.forEach(word => {
        if (typeof word === 'string') {
            const val = word.split('.').reduce((pre, cur) => {
                return pre[cur]
            }, this)
            if (Object.prototype.toString.call(val).slice(8, -1) === 'Object') {
                console.log(`${word} ======> `)
                console.log(JSON.stringify(val, null, 2))
                return
            }
            console.log(`${word} ======> `, val)
            return
        }
        const [name, value] = Object.entries(word)[0]
        if (Object.prototype.toString.call(value).slice(8, -1) === 'Object') {
            console.log(`${name} ======> `)
            console.log(JSON.stringify(value, null, 2))
            return
        }
        console.log(`${name} ======> `, value)
    })
    console.log('🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉🎉')
    console.log('')
}

优势

  • 无需通用的注释打印
  • 支持限制特定页面输出变量
  • 针对对象值自动序列化
  • 支持一行输出 N个变量打印
  • 支持打印data变量和局部变量
  • 更短的函数名

总的来说,这个封装主要是我在项目console烦了,特别是加上注释和对象json序列化,于是这几天蚌埠住了,整了这个封装,总的来说我觉得效果是不错滴,一行顶之前 N 行, 不用加注释,也不需要该死的 JSON.stringify(xx, null, 2)

但是特殊打印就回归正常 console.log 哦, 没必要封装太深入

希望能够帮助到那些和我一样的console党~ (wink), 觉得有帮助可以点赞关注一下我喔~~~

评论区总结一下:

Vue2选项式用当前方案我觉得更好:

vscode配置快捷键:

"Print to console": {  
"scope": "javascript,typescript",  
"prefix": "c",  
"body": [
 console.log('')
"this.\\$c('xxxx$1')",  
"$2"  
],  
"description": "Log output to console"  
}  

this.$c()上方可以加一句console做堆栈追踪
在加上快捷键后,基本打印变量就是 c+tab,然后加变量名就可以了,不需要自己写注释,开发速度至少比console.log()快一半! 并且无论打印多少个变量代码量都只要一行!

如果是react或者Vue3组合式API的话

使用vscode的turbo console插件,这个好用些,但在选项式API里不适用。

往期奇思妙想:

《这个前端Api管理方案会更好?》
《这个前端Api管理方案会更好?(二)》