vConsole核心源码学习

@TOC


vConsole 核心类的实现,该工具由腾讯开发,用于在移动端进行调试。代码主要涉及 vConsole 的初始化、插件管理、事件触发和销毁等功能。以下是对代码的详细解析:

概述

  • vConsole 是一个用于移动端的调试工具。
  • 提供日志、网络、系统信息、元素查看和存储等调试功能。
  • 使用 Svelte 作为前端框架来构建调试面板。

核心类 VConsole

1. 导入模块和定义常量

javascript 复制代码
import type { SvelteComponent } from 'svelte';
import type { VConsoleOptions } from './options.interface';
import * as tool from '../lib/tool';
import $ from '../lib/query';
import { default as CoreCompClass } from './core.svelte';
import type { IVConsoleTopbarOptions, IVConsolePluginEventName } from '../lib/plugin';
import { VConsolePlugin } from '../lib/plugin';
import { VConsoleLogPlugin } from '../log/log';
import { VConsoleDefaultPlugin } from '../log/default';
import { VConsoleSystemPlugin } from '../log/system';
import { VConsoleNetworkPlugin } from '../network/network';
import { VConsoleElementPlugin } from '../element/element';
import { VConsoleStoragePlugin } from '../storage/storage';
import { VConsoleLogExporter } from '../log/log.exporter';
import { VConsoleNetworkExporter } from '../network/network.exporter';

const VCONSOLE_ID = '#__vconsole';
  • 导入必要的模块和类型。
  • 定义常量 VCONSOLE_ID,用于标识 vConsole 的 DOM 元素。

2. 类的定义和构造函数

javascript 复制代码
export class VConsole {
  public version: string = __VERSION__;
  public isInited: boolean = false;
  public option: VConsoleOptions = {};

  protected compInstance: SvelteComponent;
  protected pluginList: { [id: string]: VConsolePlugin } = {}; // plugin instance

  // Export plugin methods
  public log: VConsoleLogExporter;
  public system: VConsoleLogExporter;
  public network: VConsoleNetworkExporter;

  // Export static classes
  public static VConsolePlugin: typeof VConsolePlugin;
  public static VConsoleLogPlugin: typeof VConsoleLogPlugin;
  public static VConsoleDefaultPlugin: typeof VConsoleDefaultPlugin;
  public static VConsoleSystemPlugin: typeof VConsoleSystemPlugin;
  public static VConsoleNetworkPlugin: typeof VConsoleNetworkPlugin;
  public static VConsoleElementPlugin: typeof VConsoleElementPlugin;
  public static VConsoleStoragePlugin: typeof VConsoleStoragePlugin;

  constructor(opt?: VConsoleOptions) {
    if (!!VConsole.instance && VConsole.instance instanceof VConsole) {
      console.debug('[vConsole] vConsole is already exists.');
      return VConsole.instance;
    }
    VConsole.instance = this;

    this.isInited = false;
    this.option = {
      defaultPlugins: ['system', 'network', 'element', 'storage'],
      log: {},
      network: {},
      storage: {},
    };

    // merge options
    if (tool.isObject(opt)) {
      for (let key in opt) {
        this.option[key] = opt[key];
      }
    }

    // check deprecated options
    if (typeof this.option.maxLogNumber !== 'undefined') {
      this.option.log.maxLogNumber = this.option.maxLogNumber;
      console.debug('[vConsole] Deprecated option: `maxLogNumber`, use `log.maxLogNumber` instead.');
    }
    if (typeof this.option.onClearLog !== 'undefined') {
      console.debug('[vConsole] Deprecated option: `onClearLog`.');
    }
    if (typeof this.option.maxNetworkNumber !== 'undefined') {
      this.option.network.maxNetworkNumber = this.option.maxNetworkNumber;
      console.debug('[vConsole] Deprecated option: `maxNetworkNumber`, use `network.maxNetworkNumber` instead.');
    }

    // add built-in plugins
    this._addBuiltInPlugins();

    // try to init
    const _onload = () => {
      if (this.isInited) {
        return;
      }
      this._initComponent();
      this._autoRun();
    };
    if (document !== undefined) {
      if (document.readyState === 'loading') {
        $.bind(<any>window, 'DOMContentLoaded', _onload);
      } else {
        _onload();
      }
    } else {
      // if document does not exist, wait for it
      let _timer;
      const _pollingDocument = () => {
        if (!!document && document.readyState == 'complete') {
          _timer && clearTimeout(_timer);
          _onload();
        } else {
          _timer = setTimeout(_pollingDocument, 1);
        }
      };
      _timer = setTimeout(_pollingDocument, 1);
    }
  }
  • 定义 VConsole 类,并在构造函数中进行初始化。
  • 检查是否已有实例存在,如果有则返回现有实例。
  • 初始化选项并合并用户传入的选项。
  • 检查并处理已弃用的选项。
  • 添加内置插件。
  • 尝试初始化组件,等待文档加载完成后进行初始化。

3. 添加内置插件

javascript 复制代码
/**
 * Add built-in plugins.
 */
private _addBuiltInPlugins() {
  // add default log plugin
  this.addPlugin(new VConsoleDefaultPlugin('default', 'Log'));

  // add other built-in plugins according to user's config
  const list = this.option.defaultPlugins;
  const plugins = {
    'system': { proto: VConsoleSystemPlugin, name: 'System' },
  };
  if (__TARGET__ === 'web') {
    plugins['network'] = { proto: VConsoleNetworkPlugin, name: 'Network' };
    plugins['element'] = { proto: VConsoleElementPlugin, name: 'Element' };
    plugins['storage'] = { proto: VConsoleStoragePlugin, name: 'Storage' };
  }
  if (!!list && tool.isArray(list)) {
    for (let i = 0; i < list.length; i++) {
      const pluginConf = plugins[list[i]];
      if (!!pluginConf) {
        this.addPlugin(new pluginConf.proto(list[i], pluginConf.name));
      } else {
        console.debug('[vConsole] Unrecognized default plugin ID:', list[i]);
      }
    }
  }
}
  • 根据用户配置添加内置插件。
  • 支持日志、系统、网络、元素查看和存储等插件。

4. 初始化 Svelte 组件

javascript 复制代码
/**
 * Init svelte component.
 */
private _initComponent() {
  if (! $.one(VCONSOLE_ID)) {
    const switchX = <any>tool.getStorage('switch_x') * 1;
    const switchY = <any>tool.getStorage('switch_y') * 1;

    let target: HTMLElement;
    if (typeof this.option.target === 'string') {
      target = document.querySelector(this.option.target);
    } else if (this.option.target instanceof HTMLElement) {
      target = this.option.target;
    }
    if (! (target instanceof HTMLElement)) {
      target = document.documentElement;
    }
    this.compInstance = new CoreCompClass({
      target,
      props: {
        switchButtonPosition: {
          x: switchX,
          y: switchY,
        },
      },
    });

    // bind events
    this.compInstance.$on('show', (e) => {
      if (e.detail.show) {
        this.show();
      } else {
        this.hide();
      }
    });
    this.compInstance.$on('changePanel', (e) => {
      const pluginId = e.detail.pluginId;
      this.showPlugin(pluginId);
    });
  }

  // set options into component
  this._updateComponentByOptions();
}
  • 初始化 Svelte 组件并绑定事件。
  • 设置开关按钮的位置并绑定显示和切换面板事件。

5. 自动运行

javascript 复制代码
/**
 * Auto run after initialization.
 * @private
 */
private _autoRun() {
  this.isInited = true;

  // init plugins
  for (let id in this.pluginList) {
    this._initPlugin(this.pluginList[id]);
  }

  // show first plugin
  this._showFirstPluginWhenEmpty();

  this.triggerEvent('ready');
}
  • 初始化所有插件。
  • 如果没有激活的插件,显示第一个插件。
  • 触发 ready 事件。

6. 添加和移除插件

javascript 复制代码
/**
 * Add a new plugin.
 */
public addPlugin(plugin: VConsolePlugin) {
  // ignore this plugin if it has already been installed
  if (this.pluginList[plugin.id] !== undefined) {
    console.debug('[vConsole] Plugin `' + plugin.id + '` has already been added.');
    return false;
  }
  this.pluginList[plugin.id] = plugin;
  // init plugin only if vConsole is ready
  if (this.isInited) {
    this._initPlugin(plugin);
    // if it's the only plugin, show it by default
    this._showFirstPluginWhenEmpty();
  }
  return true;
}

/**
 * Remove a plugin.
 */
public removePlugin(pluginID: string) {
  pluginID = (pluginID + '').toLowerCase();
  const plugin = this.pluginList[pluginID];
  // skip

 if is has not been installed
  if (plugin === undefined) {
    console.debug('[vConsole] Plugin `' + pluginID + '` does not exist.');
    return false;
  }
  // trigger `remove` event before uninstall
  plugin.trigger('remove');
  try {
    delete this.pluginList[pluginID];
    delete this.compInstance.pluginList[pluginID];
  } catch (e) {
    this.pluginList[pluginID] = undefined;
    this.compInstance.pluginList[pluginID] = undefined;
  }
  this.compInstance.pluginList = this.compInstance.pluginList;
  // show the first plugin by default
  if (this.compInstance.activedPluginId == pluginID) {
    this.compInstance.activedPluginId = '';
    this._showFirstPluginWhenEmpty();
  }
  return true;
}
  • addPlugin 方法用于添加新插件,初始化插件并在需要时显示第一个插件。
  • removePlugin 方法用于移除插件,并在需要时显示第一个插件。

7. 显示和隐藏面板

javascript 复制代码
/**
 * Show console panel.
 */
public show() {
  if (!this.isInited) {
    return;
  }
  this.compInstance.show = true;
  this._triggerPluginsEvent('showConsole');
}

/**
 * Hide console panel.
 */
public hide() {
  if (!this.isInited) {
    return;
  }
  this.compInstance.show = false;
  this._triggerPluginsEvent('hideConsole');
}

/**
 * Show switch button
 */
public showSwitch() {
  if (!this.isInited) {
    return;
  }
  this.compInstance.showSwitchButton = true;
}

/**
 * Hide switch button.
 */
public hideSwitch() {
  if (!this.isInited) {
    return;
  }
  this.compInstance.showSwitchButton = false;
}

/**
 * Show a plugin panel.
 */
public showPlugin(pluginId: string) {
  if (!this.isInited) {
    return;
  }
  if (!this.pluginList[pluginId]) {
    console.debug('[vConsole] Plugin `' + pluginId + '` does not exist.');
  }
  // trigger plugin event
  this.compInstance.activedPluginId && this._triggerPluginEvent(this.compInstance.activedPluginId, 'hide');
  this.compInstance.activedPluginId = pluginId;
  this._triggerPluginEvent(this.compInstance.activedPluginId, 'show');
}
  • showhide 方法用于显示和隐藏调试面板。
  • showSwitchhideSwitch 方法用于显示和隐藏开关按钮。
  • showPlugin 方法用于显示指定插件的面板。

总结

vConsole 是一个功能强大的移动端调试工具,通过插件机制提供了日志、网络、系统信息、元素查看和存储等功能。核心类 VConsole 负责初始化、插件管理、事件触发和销毁等操作,使用 Svelte 组件来构建调试面板,并提供了一系列方法来管理插件和显示调试信息。

相关推荐
踩着两条虫9 分钟前
VTJ.PRO 在线应用开发平台的代码生成与模板系统
前端·低代码·ai编程
前端小崽子14 分钟前
线上复制按钮失效?也许是这个原因
前端
张元清15 分钟前
React 滚动效果:告别第三方库
前端·javascript·面试
有志16 分钟前
Vue 学习总结(Java 后端工程师视角)
前端
踩着两条虫18 分钟前
VTJ.PRO 在线应用开发平台的DSL生命周期
前端·低代码·ai编程
我是伪码农18 分钟前
JS 复习
开发语言·前端·javascript
小碗细面19 分钟前
Claude Code 很强,但为什么我越来越常打开 Codex App?
前端·chatgpt·ai编程
愿你如愿20 分钟前
React Fiber 的主要目标是什么
前端·react.js
漂移的电子24 分钟前
【echarts 细节】
前端·javascript·echarts
im_AMBER26 分钟前
万字长文:从零实现 Yjs + Hocuspocus 协同文档
前端·react.js·前端框架