likes
comments
collection
share

0.1秒打印出console.log, 而且是全部你想要的数据难道不香吗?

作者站长头像
站长
· 阅读数 6

身为一个程序员, 你肯定每天都在打console.log, 假设一天打印100个console.log, 每个耗时2秒. 那我跟你探讨个方法, 可以让时间降至0.1秒. 那1天可以节省多少时间. 而且自己打印的console, 各种信息不全. 再加上现在vue3语法中ref对数据的包装, 单纯console.log已经看不到数据了, 必须加toRaw, 或者手动打开才能看到数据. 这其中浪费的时间和让人抓狂的感觉, 你有过吗? 反正强迫症的我, 不能忍.

1. 基础功能

话不多说, 步入正题. 先思考个问题, 如果是你, 你如何打印这段代码?

const currentVar = ref('当前变量')

如果是一个正常的操作会有以下几步

  1. 选中currentVal
  2. 复制
  3. cmd+回车到下一行
  4. 输入 console.log();
  5. 拷贝

以上打印所暴漏的问题

  1. 步骤繁琐
  2. 要输入的代码过多, 耗时且费力还容易写错
  3. 无法直接在控制台上看到数据, 需要点开多个层级才能看到里面的proxy
  4. 代码位置不清晰, 不知道当前代码在哪个文件
  5. 代码行数不清楚, 不知道当前代码在哪一行
  6. 无法区分, 具体是哪个变量值. 比如, 如果是两个console, 我怎么知道哪个是var1, 哪个是var2?
  7. 打印的代码太普通, 无法做到醒目. 试问如果是一个超大文件, 里边有1000行的console, 你怎么快速找出你打印的那一行console?
  8. 如果是obj对象数据, 不能直接直观的看到的看到里边的数据, 必须一层层点开, 很繁琐
  9. 如果console.log过多, 手动一个个删除非常麻烦, 而且容易出错
  10. ...

针对以上问题的痛点, 我做了以下的的优化

  1. 提供全局方法, 分别打印基础变量和ref变量, 并自动解析ref值和打印ref变量(利用isRef, unref, toRaw)
  2. 判断传入值的类型, 如果是对象类型, 做对象类型的打印, 否则做基础数据类型打印
  3. 提供打印变色, 字体变大, 显示当前打印所在文件和行, vscode的代码片段功能可以提供这些能力
  4. ...

一步步来, 先说下我的整体思路

  1. 创建公共方法log, 在公共方法里, 传递三个变量, 分别是要打印的变量名, 变量值, 以及其他所有你想要显示在控制台上的信息
  2. 在公共方法里, 通过isRef方法, 判断是否是ref值. 如果是ref值, 使用unref方法, 自动解析, 那么你传值的时候, 加不加.value都可以, 然后利用toRaw, 打印出值; 如果是非ref值, 则直接打印纸. 注意, 两者因为只是打印变量不一样, 所以又抽出去了个公共方法_log
  3. 因为想真的对象类型, 做特殊打印, 所以又加了个对象判断, 如果是对象, 打印成JSON.stringify解析后的值
  4. 在main.js中引入并挂载到全局
  5. 然后直接使用 proxy.log(`obj`, obj)

实现代码

  1. src/utils/gFunc.js
import { isRef, toRaw, unref } from 'vue'
export function log(variableStr, variable) {
  if (isRef(variable)) {
    let unrefVariable = unref(variable)
    _log(toRaw(unrefVariable))
  } else {
    _log(variable)
  }
  function _log(consoleData) {
    if (judgeType(consoleData) === 'object') {
      console.log(
        `%c${variableStr}`,
        'background:#fff; color: blue;font-size: 1.2em',
        JSON.stringify(consoleData, null, '\t'),
      )
    } else {
      console.log(
        `%c${variableStr}`,
        'background:#fff; color: blue;font-size: 1.2em',
        consoleData,
      )
    }
  }
  function judgeType(type) {
    if (typeof type === 'object') {
      const objType = Object.prototype.toString
        .call(type)
        .slice(8, -1)
        .toLowerCase()
      return objType
    } else {
      return typeof type
    }
  }
}
  1. 在main.js中引入
import { log } from '@/utils/gFunc.js'
app.config.globalProperties.log = log;
  1. 在任意.vue文件中测试代码是否能正常打印
<script setup lang="ts">
import { ref, getCurrentInstance, toRaw } from 'vue'
const { proxy } = getCurrentInstance()
let o1 = {
  name: 'andy',
  arr: {
    age: 18,
    height: 1.88,
  },
}
proxy.log(`o1`, o1)
let a1 = [1, 2, 3, [4.1, 4.2]]
proxy.log(`a1`, a1)

let s1 = '生当作人杰, 死亦为鬼雄'
proxy.log(`s1`, s1)
let n1 = null
proxy.log(`n1`, n1)
let b1 = true
proxy.log(`b1`, b1)
let u1 = undefined
proxy.log(`u1`, u1)

let or1 = ref({
  name: 'andy',
  arr: {
    age: 18,
    height: 1.88,
  },
})
proxy.log(`or1`, or1)
let ar1 = ref([1, 2, 3, [4.1, 4.2]])
proxy.log(`ar1`, ar1)

let sr1 = ref('生当作人杰, 死亦为鬼雄')
proxy.log(`sr1`, sr1)
let nr1 = ref(null)
proxy.log(`nr1`, nr1)
let br1 = ref(true)
proxy.log(`br1`, br1)
let ur1 = ref(undefined)
proxy.log(`ur1`, ur1)
</script>
  1. 看结果

0.1秒打印出console.log, 而且是全部你想要的数据难道不香吗?

2. 进阶功能

如果只是以上一个公共函数, 还很难实现0.1秒就打印console, 虽然以上的东西已经可以省去很大一部分时间, 但是我们想要实现的是最优解, 所以要更近一步实现进阶功能, 进阶功能需要用到一个插件和snippet功能, 我所使用的编辑器是vscode, 不同编辑器之间的用法可能有出入, 但是实现逻辑, 都是差不多的, 如果你用的是其他编辑器, 请自行修改对应的参数

注意:

  1. 我的电脑是mac, 如果是windows, 对应的快捷键会有部分出入, 主要是mac的command对应的是windows的ctrl, mad的ctrl对应windows的alt
  2. 我认为下面的快捷键作为, 最常用的快捷键, 设为ctrl+z, ctrl+x, ctrl+c这种离手最近的位置最好, 不过我那几个快捷键已经被占用了, 所以用了下面的. 以下快捷键, 可以根据个人习惯去调整

进阶思路 设想一下, 如果我想打印一个变量, 那么一个字一个字的去敲, 绝对不是最佳方案. 不卖关子了, 最佳方案, 我认为是 选中这个变量, 然后直接在下一行打印这个变量, 并把你想要输出的信息都在下一行显示出来. 或者直接在任何一行通过快捷键, 把你想要写的带有各种打印信息的代码, 直接写入在编辑器中. 那么这里边就有几个特别重要的点需要大家考虑

  1. 快捷键的方案实现
  2. 想要打印的数据, 如何直接写入编辑器中
  3. 如何获取想要打印的数据, 比如当前文件夹, 当前第几行, 当前打印的变量值

下面说实现方案

2.1 通过快捷键ctrl+s, 将完整数据打印在页面中

实现步骤

  1. 在vscode中, 按cmd+shift+p打开搜索文件的窗口, 输入>open keyboard shortcuts, 回车进入快捷键keybindings.json文件
  2. 插入快捷键, 解释下以下代码, 将剪切版的值作为初始化变量插入到下面这个代码片段中, 并且将代码行数和相对路径作为第三个参数, 放在proxy.log这个函数里
{
  "key": "ctrl+s", // 打印proxy.log
  "command": "editor.action.insertSnippet",
  "when": "editorTextFocus",
  "args": {
    "snippet": "proxy.log(`${1:${CLIPBOARD}}`, ${1:${CLIPBOARD}}, \"${TM_LINE_NUMBER}行 ${RELATIVE_FILEPATH}\");"
  }
},
  1. 简单修改下log函数, 注意: 我只写了修改后的代码, 其实就是加了个 参数, 这是可以适配之前的函数
log(variableStr, variable) 变味了 log(variableStr, variable, otherInfo = '')

 console.log(
        `%c${variableStr}`  变为了     console.log(
        `%c${variableStr} ${otherInfo}`
  1. 页面中测试, 按下 ctrl + s, 打印下面的数据
const str = ref(11)
proxy.log(`str`, str, '4行 src/views/test/t3.vue')

2.2 选中快捷键打印的实现方案(ctrl+a), 也是我工作中最常用的方案

假如你想打印一个变量在他的下一行, 我们可以想象的操作是, 先选中变量, 然后复制, 然后按cmd+回车, 然后使用上一步封装的ctrl+s快捷键将其打印在 这一行. 我们可以想一下, 选中变量后, 复制, cmd+回车, 然后写入代码这些操作, 是不是都可以通过工具去实现呢? 那么最终的方案是我们只需要选中变量, 然后快捷键, 就能一键写入代码了. 2步操作就能打印, 我想不到更精简的操作了. 除非是能自动选中, 可惜我还没找到这个方案. 这个功能依赖插件 multi-command, 需要先安装

实现步骤

  1. 在vscode中, 按cmd+shift+p打开搜索文件的窗口, 输入>open keyboard shortcuts, 回车进入快捷键keybindings.json文件
  2. 插入快捷键, 解释下以下代码, 执行multiCommand里的命令, 这个命令是addVue3Console
{
  "key": "ctrl+a", // 快速打印console
  "command": "extension.multiCommand.execute",
  "args": {
    "command": "multiCommand.addVue3Console"
  },
  "when": "editorTextFocus"
},
  1. 在vscode中, 按cmd+shift+p打开搜索文件的窗口, 输入>open user setting, 回车进入快捷键打开用户设置(JSON)文件
  2. 插入代码片段, 解释下代码. 命令addVue3Console, 会依次执行以下命令, 首先copy选中的值, 然后插入下一行, 然后写入代码片段, 注意, 这里, src/views我有进行replace替换, 因为大部分文件都在这个文件夹下, 不用写也知道, 所以替换掉省的看着难受
{
  "command": "multiCommand.addVue3Console",
  "sequence": [
    {
      "command": "execCopy",
      "when": "editorFocus && textInputFocus && terminalTextSelected"
    },
    {
      "command": "editor.action.insertLineAfter",
      "when": "editorTextFocus && !editorReadonly && terminalTextSelected"
    },
    {
      "command": "editor.action.insertSnippet",
      "when": "!editorTextFocus",
      "args": {
        "snippet": "proxy.log(`${1:${CLIPBOARD}}`, ${1:${CLIPBOARD}}, \"${TM_LINE_NUMBER}行 ${RELATIVE_FILEPATH/src\\/views\\///}\");"
      }
    },
  ]
},

2.3 使用代码提示功能快速打印

有时候, 不想用快捷键咋办? 虽然我还是建议使用快捷键, 但是snippets又不光只支持快捷键. 代码提示功能也很好用很强大啊, 下面写一个代码片段, 实现以上打印功能, 我们给这个快捷键触发的字符串 设为cc, 按下cc就能直接打印代码片段, 有一个好用的生成快捷键地址 快捷键转换地址

  1. 在vscode中, 按cmd+shift+p打开搜索文件的窗口, 输入>config user snippets, 回车进入snippets: Config User Snippets文件, 选一个文件进去配置代码片段
  2. 插入代码片段
"cc": {
  "scope": "javascript,typescript",
  "prefix": "cc简写",
  "body": [
      "proxy.log(`${1:${CLIPBOARD}}`, ${1:${CLIPBOARD}}, \"${TM_LINE_NUMBER}行 ${RELATIVE_FILEPATH/src\\/views\\///}\");"
  ],
},

大功告成,下面看最终测试代码

<script setup lang="ts">
import { ref, getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
let o1 = {
  name: 'andy',
  arr: {
    age: 18,
    height: 1.88,
  },
}
proxy.log(`o1`, o1, '7行 test/t3.vue')
let a1 = [1, 2, 3, [4.1, 4.2]]
proxy.log(`a1`, a1, '15行 test/t3.vue')

let s1 = '生当作人杰, 死亦为鬼雄'
proxy.log(`s1`, s1, '19行 test/t3.vue')
let n1 = null
proxy.log(`n1`, n1, '21行 test/t3.vue')
let b1 = true
proxy.log(`b1`, b1, '23行 test/t3.vue')
let u1 = undefined
proxy.log(`b1`, b1, '25行 test/t3.vue')

let or1 = ref({
  name: 'andy',
  arr: {
    age: 18,
    height: 1.88,
  },
})
proxy.log(`or1`, or1, '28行 test/t3.vue')
let ar1 = ref([1, 2, 3, [4.1, 4.2]])
proxy.log(`ar1`, ar1, '36行 test/t3.vue')

let sr1 = ref('生当作人杰, 死亦为鬼雄')
proxy.log(`sr1`, sr1, '39行 test/t3.vue')
let nr1 = ref(null)
proxy.log(`nr1`, nr1, '41行 test/t3.vue')
let br1 = ref(true)
proxy.log(`br1`, br1, '43行 test/t3.vue')
let ur1 = ref(undefined)
proxy.log(`ur1`, ur1, '45行 test/t3.vue')
</script>

0.1秒打印出console.log, 而且是全部你想要的数据难道不香吗?

3. 拓展

3.1 建议将log函数放在组件库中

如果在各个前端项目中, 每次写, 每次引的很麻烦, 建议将这个函数放在现有的前端组件库中. 比如我们公司我就新建了个前端组件库, 里边我把函数都暴漏出去了. 那我每次只需要引入组件库, 然后把组件库里暴漏的函数, 在遍历挂载到项目全局中使用即可, 我以我在组件库中的使用为例

具体步骤

  1. 在个人组件库中oeos-v3-components的packages/utils文件夹下, 新建gFunc.js, 把公共函数都写在这里.
  2. 在packages/index.js中, 引入并暴漏所有gFunc.js下的函数
// 注意这里, registerDirectives, finalComps是对应的 自定义指令和组件, 因为不涉及这块, 所以没有写,  只是想告诉大家, 在package/index下, 我也暴漏了组件和自定义指令, 而不是单独把函数暴漏了出去
const install = (app) => {
  registerDirectives(app)
  finalComps.forEach((v, i) => {
    app.component(v, comps[i])
  })
}
export { utils }
export * from './utils/gFunc.js'
export default {
  finalComps,
  install,
  utils,
}
  1. 在前端项目中引入并挂载全局, main.js中写以下代码
import App from './App.vue'
const app = createApp(App)
import oeosV3Components, { utils } from 'oeos-v3-components'
// 将oeos-v3-components下的公共函数赋值到全局
Object.keys(utils).forEach((v) => {
  app.config.globalProperties[v] = utils[v]
})
app.use(oeosV3Components)
  1. 在.vue文件中使用 proxy.log(str, str, "5行 src/views/test/t3.vue");
  2. 在js文件中使用
import { log } from 'oeos-v3-components'
log(`11`, 11, "2行 test/e1.js"); 

3.2 一键删除 console.log 和 debugger 和 proxy.log

如果一个文件中, 有很多console.log, proxy.log, debugger. 如果手动清理, 势必会非常麻烦, 而且容易出错. 那么用软件清理就没这些问题了. 推荐Keyboard Maestro Editor. 这在国内用的人不多, 原理就是, 快捷键打开vscode中的替换, 然后清空里边的数据, 然后写入匹配console.log的正则, 在快捷键替换; 然后再替换proxy.log, 依次类推. 主要是这个正则[\n\s\r\t\/]*console.log\(([^)]|\\n)*\).*, 放个图吧, 只是我个人这么用. 我想实现我上诉所说的功能, 所以用了这个软件, 我个人用的比较多. 如果有更通用的方案, 也可以讨论下

0.1秒打印出console.log, 而且是全部你想要的数据难道不香吗?

4. 题外话.

虽然这些功能, 看似没有太多复杂的东西. 但是却是我好几年, 不断总结琢磨出来的. 我有看技术类视频的习惯, 不过大部分的视频, 都是讲的前端知识点, 对这些我认为可以提高个人开发效率的功能, 讲的比较少. 所以很多地方, 都是我针对个人的痛点, 去想办法解决的, 思路不见得是最好, 但是确是我目前能想到的比较好的解决方案了. 如果有更好的解决思路, 可以一起探讨