5分钟教你使用 console.log 输出五彩斑斓的黑

45,589 阅读4分钟

掘金年度人气作者

阅读本文预计需要 5 分钟,今天是非工作日,说好不更21天挑战手写前端框架系列。但是一周养成的写作习惯,今天不写点东西,就不是很舒服(太装啦)。所以有了这篇文章。

前言

chalk-malita.jpg

console.log 对一个前端人,那是在熟悉不过了。我们最经常使用它在控制台输出信息然后进行代码调试,你肯定会发现输出的信息的颜色永远是黑色,当然还有一种我们最不喜欢的颜色 -- 红色,一旦出现了就意味BUG出现了。本文教你如何用 console 在控制台输出五颜六色的信息。

console.log 输出单色的信息

实现起来非常简单,就是 console 能够解析占位符,总的就支持 5 个占位符 %o%s%s%f%c

其中 %c 指令将 CSS 样式应用于控制台输出,指令前的文本不会受到影响,但指令后的文本将使用参数中的 CSS 声明进行样式设置。

比如想在控制台输出123456,其中456的颜色是红色的,可以在控制台中输入以下代码

console.log('123%c456','color: red;')

log123456.jpg

从上图中可以看到,我们的尝试是有效的,并且表现完全符合预期。

把上面用法封装一下,形成一个“语法糖”,实现如下:

const red = (str)=>{
  console.log(`%c${str}`,'color: red')
}
red('Hello world!')

可以直接复制以上代码到控制台中执行,看是不是效果一样。red 方法就是一个在控制台输出蓝色的信息的语法糖。

其它 4 个占位符,可以翻阅 mozilla console 了解哈。

更灵活的调用 console 方法

通过观察上面的用法,我们发现如果我们需要给同一个字符串添加更多的 CSS 效果,我们需要编辑 log 的第二个参数,而如果需要把两个有颜色的字符串拼接到一起,则需要修改第一参数,并且添加一个第三参数。

console.log(`%c123%c456`,'color: blue;','color: green;')

bluegreen.jpg

如果再继续拼接的话,那传入参数更多。在传入参数不确定的时候,我们可以使用 arguments 来获取传入参数,使用 ES6 语法的话,只需要写解构就行。

const log = (...args)=>{
    console.log.apply(void 0, args);
}

console.log 输出多色的信息

上面实现了单色的信息,那么多色的信息如何实现呢? console.log 在第一个参数后,还可以接受多个参数,每个参数可以给对应的信息设置不同的颜色,具体实现如下:

console.log('%c123%c456','color: blue','color: green')

bluegreen.jpg

那如何将 console.log 输出多色的信息封装一下,形成一个"语法糖"呢?

可以这么做,把一个信息拆分成多段,再对应设置不同的颜色,再组合起来 console.log 输出。

比如把 123456 拆分成 123456,再对应不同的颜色,数据如下所示:

['%c123','color: blue']
['%c456','color: green']

写一个 add 方法将上面数据拼接成 ['%c123%c456','color: red','color: blue'] ,然后利用 apply 调用 console.log ,如下所示:

console.log.apply(void 0, ['%c123%c456','color: blue','color: green']);

复制以上代码到控制台中执行,效果如下图所示:

bluegreen.jpg

下面来实现一下 add 方法,并对 console.log 进行二次封装。

const _console = console;
const createlog = (util) => (...args) => {
  const fun = _console[util] ? _console[util] : _console.log;
  fun.apply(void 0, args);
};

const add = (...arr) => {
    let fi = [[]];
    for (let key = 0; key < arr.length; key++) {
      const [first, ...other] = arr[key];
      fi[0] += first;
      fi = fi.concat(other);
    }
    return fi;
  };
createlog('log')(...add(['%c123','color: blue',],['%c456','color: green']));

复制以上代码到控制台中执行,看一下效果,效果如下图所示:

bluegreen.jpg

预设一些颜色值

我们将常用到的一些颜色值,都内置封装一下,方便使用,将上面实现的 red 方法的改造成按这样 chalk.log(chalk.red(123)) 调用。

const _console = console;
const createlog = (util) => (...args) => {
  const fun = _console[util] ? _console[util] : _console.log;
  fun.apply(void 0, args);
};

const add = (...arr) => {
    let fi = [[]];
    for (let key = 0; key < arr.length; key++) {
      const [first, ...other] = arr[key];
      fi[0] += first;
      fi = fi.concat(other);
    }
    return fi;
  };
const chalk = {
  log:createlog('log')
};
const color = {
  black: '#00000',
  red: '#FF0000',
  green: '#008000',
  yellow: '#FFFF00',
  blue: '#0000FF',
  magenta: '#FF00FF',
  cyan: '#00FFFF',
  white: '#FFFFFF',
};
Object.keys(color).forEach((key) => {
  chalk[key] = (str) => {
    // 用户会有两种用法 chalk.red('1231')
    if (typeof str === 'string' || typeof str === 'number') {
      return [`%c${str}`, `color:${color[key]}`];
      }
    // 用户第二种用法 chalk.red(chalk.bold('123'))
    for (let i = 1; i < str.length; i++) {
      str[i] += `;color:${color[key]}`;
    }
    return str;
  };
});

chalk.log(...add(
  chalk.black('black'),
  chalk.red('red'),
  chalk.green('green'),
  chalk.yellow('yellow'),
  chalk.blue('blue'),
  chalk.magenta('magenta'),
  chalk.cyan('cyan'),
  chalk.white('white')
  )
)

复制以上代码到控制台中执行,看一下效果,效果如下图所示:

color.jpg

增加背景色

通过观察我们发现背景色的方法名,其实就是颜色方法首字母大写再增加 bg 前缀。

简单的修改上面的方法实现

Object.keys(color).forEach((key) => {
    colorUtils[key] = (str: string | string[]) => {
      // 用户会有两种用法 chalk.red('1231')
      if (typeof str === 'string' || typeof str === 'number') {
        return [`%c${str}`, `color:${color[key]}`];
      }
      // 用户第二种用法 chalk.red(chalk.bold('123'))
      for (let i = 1; i < str.length; i++) {
        str[i] += `;color:${color[key]}`;
      }
      return str;
    };
    colorUtils[`bg${firstToUpperCase(key)}`] = (str: string | string[]) => {
      if (typeof str === 'string' || typeof str === 'number') {
        return [`%c${str}`, `padding: 2px 4px; border-radius: 3px; color: ${key === 'white' ? '#000' : '#fff'}; font-weight: bold; background:${color[key]};`];
      }
      for (let i = 1; i < str.length; i++) {
        str[i] += `;padding: 2px 4px; border-radius: 3px; font-weight: bold; background:${color[key]};`;
      }
      return str;
    };
  });

给日志分级别

为了进一步对日志输出作出更加合理的管控,也为了提供更多的默认颜色输出,因此我们给日志输出划分等级。

const colorHash = {
    log: 'black',
    wait: 'cyan',
    error: 'red',
    warn: 'yellow',
    ready: 'green',
    info: 'blue',
    event: 'magenta',
};

使用这些分级会默认给输出日志添加前缀,如 [Error],后面的颜色是默认颜色。

let chalk = {};
if (!window.chalk) {
  const _console = console;
  const color = {
    black: '#000000',
    red: '#FF0000',
    green: '#008000',
    yellow: '#FFFF00',
    blue: '#0000FF',
    magenta: '#FF00FF',
    cyan: '#00FFFF',
    white: '#FFFFFF',
  };
  const add = (...arr) => {
    let fi = [
      []
    ];
    for (let key = 0; key < arr.length; key++) {
      const [first, ...other] = arr[key];
      fi[0] += first;
      fi = fi.concat(other);
    }
  return fi;
  };
  const createlog = (util) => (...args) => {
    const fun = _console[util] ? _console[util] : _console.log;
    fun.apply(void 0, args);
  };
  const colorUtils = {
    bold: (str) => {
      if (typeof str === 'string' || typeof str === 'number') {
        return `${str};font-weight: bold;`;
      }
      for (let key = 1; key < str.length; key++) {
        str[key] += `;font-weight: bold;`;
      }
      return str;
    }
  };
  const colorHash = {
    log: 'black',
    wait: 'cyan',
    error: 'red',
    warn: 'yellow',
    ready: 'green',
    info: 'blue',
    event: 'magenta',
  };
  const createChalk = (name) => (...str) => {
    if (typeof str[0] === 'object') {
      createlog(name)(...add(colorUtils.bold(colorUtils[colorHash[name]](`[${firstToUpperCase(name)}] `)), ...str));
      return;
    }
    let strArr = str;
    if (typeof str === 'string' || typeof str === 'number') {
      strArr = colorUtils[colorHash[name]](str);
    }
    createlog(name)(...add(colorUtils.bold(colorUtils[colorHash[name]](`[${firstToUpperCase(name)}] `)), strArr));
  };
  const chalk = {};
  Object.keys(colorHash).forEach(key => {
    chalk[key] = createChalk(key);
  });
  const firstToUpperCase = (str) => str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
  Object.keys(color).forEach(key => {
    colorUtils[key] = (str) => {
      if (typeof str === 'string' || typeof str === 'number') {
        return [`%c${str}`, `color:${color[key]}`];
      }
      for (let i = 1; i < str.length; i++) {
        str[i] += `;color:${color[key]}`;
      }
      return str;
    };
    colorUtils[`bg${firstToUpperCase(key)}`] = (str) => {
    if (typeof str === 'string' || typeof str === 'number') {
       return [`%c${str}`, `padding: 2px 4px; border-radius: 3px; color: ${key === 'white' ? '#000' : '#fff'}; font-weight: bold; background:${color[key]};`];
    }
     for (let i = 1; i < str.length; i++) {
        str[i] += `;padding: 2px 4px; border-radius: 3px; font-weight: bold; background:${color[key]};`;
     }
     return str;
   };
  });
  window.chalk = {
    add,
    ...chalk,
    ...colorUtils,
  };
}
chalk = window.chalk;
chalk.log('log');
chalk.error('error');
chalk.warn('warn')

自定义定制函数

熟练的上述的方法之后,你就可以随意的添加你需要的函数了,比如常见的打印框架的版本号。

 const hello = (title: string, version: string) =>
      createlog('log')(
        `%c ${title} %c V${version} `,
        'padding: 2px 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060; font-weight: bold;',
        'padding: 2px 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e; font-weight: bold;',
      );
      
hello('Malita','0.0.6');      

hello.jpg

比如打印图片

const image = (img: string) => createlog('log')(`%c `, `font-size: 1px;
padding: 100px 100px;
background-image: url(${img});
background-size: contain;
background-repeat: no-repeat;
color: transparent;`);

image('https://logo.png')

logo.jpg

当然你也可以输出动图。只要你熟练掌握了这些方法,你就可以随意的发挥你的创意。

灵感来源

以上实现的灵感来源于一个很流行的 node 控制台美化仓库,chalk,它在 GitHub 上拥有 18.3k 的 star。

import chalk from 'chalk';

console.log(chalk.blue('Hello world!'));

chalk.svg

在项目中使用

为了方便大家和我自己使用,我将这个功能发布到了 npm 上,你可以在你的项目中使用。

npm i @alita/chalk
import chalk from '@alita/chalk';

window.alitadebug = true;

chalk.hello('Malita','0.0.6');

// 或者

import '@alita/chalk';

window.alitadebug = true;

window.chalk.hello('Malita','0.0.6');

我增加了一个日志开关 window.alitadebug = true;,好处就是部署上线的代码,正常情况下不打印日志,如果出现错误需要定位代码,可以直接通过控制台中输入 window.alitadebug = true; 来开启。

源码 MIT 开源

感谢阅读,如果你觉得这个东西很有趣,或者你有好的创意,欢迎在评论区和我互动。

想进一步的了解我,可以查看我的年中总结: Umi Core Maintainers,月榜作者,晋升 P8,来听我碎碎念如何|2022 年中总结