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 组件来构建调试面板,并提供了一系列方法来管理插件和显示调试信息。

相关推荐
乐闻x5 分钟前
Pinia 实战教程:构建高效的 Vue 3 状态管理系统
前端·javascript·vue.js
weixin_4314496818 分钟前
web组态软件
前端·物联网·低代码·编辑器·组态
橘子味小白菜24 分钟前
el-table的树形结构后端返回的id没有唯一键怎么办
前端·vue.js
前端Hardy1 小时前
HTML&CSS:比赛记分卡
前端·javascript·css·3d·html
疯狂的沙粒1 小时前
Vue项目开发 element-UI 前端实现 1到10排列选择的按钮
前端·vue.js·ui
刺客-Andy1 小时前
React第六节 组件属性prop的propTypes类型使用介绍
前端·javascript·react.js·typescript
Mr.Liu62 小时前
小程序24-滚动效果:scroll-view组件详解
前端·微信小程序·小程序
三金121382 小时前
局部使用Vue
前端·javascript·vue.js
LinXunFeng2 小时前
Flutter - 子部件任意位置观察滚动数据
前端·flutter·开源
好开心333 小时前
js高级06-ajax封装和跨域
开发语言·前端·javascript·ajax·okhttp·ecmascript·交互