- // ==UserScript==
- // @name vue-debug-helper
- // @name:en vue-debug-helper
- // @name:zh Vue调试分析助手
- // @name:zh-TW Vue調試分析助手
- // @name:ja Vueデバッグ分析アシスタント
- // @namespace https://github.com/xxxily/vue-debug-helper
- // @homepage https://github.com/xxxily/vue-debug-helper
- // @version 0.0.5
- // @description Vue components debug helper
- // @description:en Vue components debug helper
- // @description:zh Vue组件探测、统计、分析辅助脚本
- // @description:zh-TW Vue組件探測、統計、分析輔助腳本
- // @description:ja Vueコンポーネントの検出、統計、分析補助スクリプト
- // @author ankvps
- // @icon https://cdn.jsdelivr.net/gh/xxxily/vue-debug-helper@main/logo.png
- // @match http://*/*
- // @match https://*/*
- // @grant unsafeWindow
- // @grant GM_addStyle
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_deleteValue
- // @grant GM_listValues
- // @grant GM_addValueChangeListener
- // @grant GM_removeValueChangeListener
- // @grant GM_registerMenuCommand
- // @grant GM_unregisterMenuCommand
- // @grant GM_getTab
- // @grant GM_saveTab
- // @grant GM_getTabs
- // @grant GM_openInTab
- // @grant GM_download
- // @grant GM_xmlhttpRequest
- // @run-at document-start
- // @connect 127.0.0.1
- // @license GPL
- // ==/UserScript==
- (function (w) { if (w) { w._vueDebugHelper_ = 'https://github.com/xxxily/vue-debug-helper'; } })();
-
- /**
- * 对特定数据结构的对象进行排序
- * @param {object} obj 一个对象,其结构应该类似于:{key1: [], key2: []}
- * @param {boolean} reverse -可选 是否反转、降序排列,默认为false
- * @param {object} opts -可选 指定数组的配置项,默认为{key: 'key', value: 'value'}
- * @param {object} opts.key -可选 指定对象键名的别名,默认为'key'
- * @param {object} opts.value -可选 指定对象值的别名,默认为'value'
- * @returns {array} 返回一个数组,其结构应该类似于:[{key: key1, value: []}, {key: key2, value: []}]
- */
- const objSort = (obj, reverse, opts = { key: 'key', value: 'value' }) => {
- const arr = [];
- for (const key in obj) {
- if (Object.prototype.hasOwnProperty.call(obj, key) && Array.isArray(obj[key])) {
- const tmpObj = {};
- tmpObj[opts.key] = key;
- tmpObj[opts.value] = obj[key];
- arr.push(tmpObj);
- }
- }
-
- arr.sort((a, b) => {
- return a[opts.value].length - b[opts.value].length
- });
-
- reverse && arr.reverse();
- return arr
- };
-
- /**
- * 根据指定长度创建空白数据
- * @param {number} size -可选 指str的重复次数,默认为1024次,如果str为单个单字节字符,则意味着默认产生1Mb的空白数据
- * @param {string|number|any} str - 可选 指定数据的字符串,默认为'd'
- */
- function createEmptyData (count = 1024, str = 'd') {
- const arr = [];
- arr.length = count + 1;
- return arr.join(str)
- }
-
- /**
- * 将字符串分隔的过滤器转换为数组形式的过滤器
- * @param {string|array} filter - 必选 字符串或数组,字符串支持使用 , |符号对多个项进行分隔
- * @returns {array}
- */
- function toArrFilters (filter) {
- filter = filter || [];
-
- /* 如果是字符串,则支持通过, | 两个符号来指定多个组件名称的过滤器 */
- if (typeof filter === 'string') {
- /* 移除前后的, |分隔符,防止出现空字符的过滤规则 */
- filter.replace(/^(,|\|)/, '').replace(/(,|\|)$/, '');
-
- if (/\|/.test(filter)) {
- filter = filter.split('|');
- } else {
- filter = filter.split(',');
- }
- }
-
- filter = filter.map(item => item.trim());
-
- return filter
- }
-
- window.vueDebugHelper = {
- /* 存储全部未被销毁的组件对象 */
- components: {},
- /* 存储全部创建过的组件的概要信息,即使销毁了概要信息依然存在 */
- componentsSummary: {},
- /* 基于componentsSummary的组件情况统计 */
- componentsSummaryStatistics: {},
- /* 已销毁的组件概要信息列表 */
- destroyList: [],
- /* 基于destroyList的组件情况统计 */
- destroyStatistics: {},
-
- config: {
- /* 是否在控制台打印组件生命周期的相关信息 */
- lifecycle: {
- show: false,
- filters: ['created'],
- componentFilters: []
- }
- },
-
- /* 给组件注入空白数据的配置信息 */
- ddConfig: {
- enabled: false,
- filters: [],
- count: 1024
- }
- };
-
- const helper = window.vueDebugHelper;
-
- const methods = {
- objSort,
- createEmptyData,
- /* 清除全部helper的全部记录数据,以便重新统计 */
- clearAll () {
- helper.components = {};
- helper.componentsSummary = {};
- helper.componentsSummaryStatistics = {};
- helper.destroyList = [];
- helper.destroyStatistics = {};
- },
-
- /**
- * 对当前的helper.components进行统计与排序
- * 如果一直没运行过清理函数,则表示统计页面创建至今依然存活的组件对象
- * 运行过清理函数,则表示统计清理后新创建且至今依然存活的组件对象
- */
- componentsStatistics (reverse = true) {
- const tmpObj = {};
-
- Object.keys(helper.components).forEach(key => {
- const component = helper.components[key];
-
- tmpObj[component._componentName]
- ? tmpObj[component._componentName].push(component)
- : (tmpObj[component._componentName] = [component]);
- });
-
- return objSort(tmpObj, reverse, {
- key: 'componentName',
- value: 'componentInstance'
- })
- },
-
- /**
- * 对componentsSummaryStatistics进行排序输出,以便可以直观查看组件的创建情况
- */
- componentsSummaryStatisticsSort (reverse = true) {
- return objSort(helper.componentsSummaryStatistics, reverse, {
- key: 'componentName',
- value: 'componentsSummary'
- })
- },
-
- /**
- * 对destroyList进行排序输出,以便可以直观查看组件的销毁情况
- */
- destroyStatisticsSort (reverse = true) {
- return objSort(helper.destroyStatistics, reverse, {
- key: 'componentName',
- value: 'destroyList'
- })
- },
-
- /**
- * 对destroyList进行排序输出,以便可以直观查看组件的销毁情况
- */
- getDestroyByDuration (duration = 1000) {
- const destroyList = helper.destroyList;
- const destroyListLength = destroyList.length;
- const destroyListDuration = destroyList.map(item => item.duration).sort();
- const maxDuration = Math.max(...destroyListDuration);
- const minDuration = Math.min(...destroyListDuration);
- const avgDuration =
- destroyListDuration.reduce((a, b) => a + b, 0) / destroyListLength;
- const durationRange = maxDuration - minDuration;
- const durationRangePercent = (duration - minDuration) / durationRange;
-
- return {
- destroyList,
- destroyListLength,
- destroyListDuration,
- maxDuration,
- minDuration,
- avgDuration,
- durationRange,
- durationRangePercent
- }
- },
-
- /**
- * 获取组件的调用链信息
- */
- getComponentChain (component, moreDetail = false) {
- const result = [];
- let current = component;
- let deep = 0;
-
- while (current && deep < 50) {
- deep++;
-
- /**
- * 由于脚本注入的运行时间会比应用创建时间晚,所以会导致部分先创建的组件缺少相关信息
- * 这里尝试对部分信息进行修复,以便更好的查看组件的创建情况
- */
- if (!current._componentTag) {
- const tag = current.$vnode?.tag || current.$options?._componentTag || current._uid;
- current._componentTag = tag;
- current._componentName = isNaN(Number(tag)) ? tag.replace(/^vue-component-\d+-/, '') : 'anonymous-component';
- }
-
- if (moreDetail) {
- result.push({
- tag: current._componentTag,
- name: current._componentName,
- componentsSummary: helper.componentsSummary[current._uid] || null
- });
- } else {
- result.push(current._componentName);
- }
-
- current = current.$parent;
- }
-
- if (moreDetail) {
- return result
- } else {
- return result.join(' -> ')
- }
- },
-
- printLifeCycleInfo (lifecycleFilters, componentFilters) {
- lifecycleFilters = toArrFilters(lifecycleFilters);
- componentFilters = toArrFilters(componentFilters);
-
- helper.config.lifecycle = {
- show: true,
- filters: lifecycleFilters,
- componentFilters: componentFilters
- };
- },
- notPrintLifeCycleInfo () {
- helper.config.lifecycle = {
- show: false,
- filters: ['created'],
- componentFilters: []
- };
- },
-
- /**
- * 给指定组件注入大量空数据,以便观察组件的内存泄露情况
- * @param {Array|string} filter -必选 指定组件的名称,如果为空则表示注入所有组件
- * @param {number} count -可选 指定注入空数据的大小,单位Kb,默认为1024Kb,即1Mb
- * @returns
- */
- dd (filter, count = 1024) {
- filter = toArrFilters(filter);
- helper.ddConfig = {
- enabled: true,
- filters: filter,
- count
- };
- },
- /* 禁止给组件注入空数据 */
- undd () {
- helper.ddConfig = {
- enabled: false,
- filters: [],
- count: 1024
- };
-
- /* 删除之前注入的数据 */
- Object.keys(helper.components).forEach(key => {
- const component = helper.components[key];
- component.$data && delete component.$data.__dd__;
- });
- }
- };
-
- helper.methods = methods;
-
- class Debug {
- constructor (msg, printTime = false) {
- const t = this;
- msg = msg || 'debug message:';
- t.log = t.createDebugMethod('log', null, msg);
- t.error = t.createDebugMethod('error', null, msg);
- t.info = t.createDebugMethod('info', null, msg);
- t.warn = t.createDebugMethod('warn', null, msg);
- }
-
- create (msg) {
- return new Debug(msg)
- }
-
- createDebugMethod (name, color, tipsMsg) {
- name = name || 'info';
-
- const bgColorMap = {
- info: '#2274A5',
- log: '#95B46A',
- error: '#D33F49'
- };
-
- const printTime = this.printTime;
-
- return function () {
- if (!window._debugMode_) {
- return false
- }
-
- const msg = tipsMsg || 'debug message:';
-
- const arg = Array.from(arguments);
- arg.unshift(`color: white; background-color: ${color || bgColorMap[name] || '#95B46A'}`);
-
- if (printTime) {
- const curTime = new Date();
- const H = curTime.getHours();
- const M = curTime.getMinutes();
- const S = curTime.getSeconds();
- arg.unshift(`%c [${H}:${M}:${S}] ${msg} `);
- } else {
- arg.unshift(`%c ${msg} `);
- }
-
- window.console[name].apply(window.console, arg);
- }
- }
-
- isDebugMode () {
- return Boolean(window._debugMode_)
- }
- }
-
- var Debug$1 = new Debug();
-
- var debug = Debug$1.create('vueDebugHelper:');
-
- /**
- * 打印生命周期信息
- * @param {Vue} vm vue组件实例
- * @param {string} lifeCycle vue生命周期名称
- * @returns
- */
- function printLifeCycle (vm, lifeCycle) {
- const lifeCycleConf = helper.config.lifecycle || { show: false, filters: ['created'], componentFilters: [] };
-
- if (!vm || !lifeCycle || !lifeCycleConf.show) {
- return false
- }
-
- const { _componentTag, _componentName, _componentChain, _createdHumanTime, _uid } = vm;
- const info = `[${lifeCycle}] tag: ${_componentTag}, uid: ${_uid}, createdTime: ${_createdHumanTime}, chain: ${_componentChain}`;
- const matchComponentFilters = lifeCycleConf.componentFilters.length === 0 || lifeCycleConf.componentFilters.includes(_componentName);
-
- if (lifeCycleConf.filters.includes(lifeCycle) && matchComponentFilters) {
- debug.log(info);
- }
- }
-
- function mixinRegister (Vue) {
- if (!Vue || !Vue.mixin) {
- debug.error('未检查到VUE对象,请检查是否引入了VUE,且将VUE对象挂载到全局变量window.Vue上');
- return false
- }
-
- Vue.mixin({
- beforeCreate: function () {
- // const tag = this.$options?._componentTag || this.$vnode?.tag || this._uid
- const tag = this.$vnode?.tag || this.$options?._componentTag || this._uid;
- const chain = helper.methods.getComponentChain(this);
- this._componentTag = tag;
- this._componentChain = chain;
- this._componentName = isNaN(Number(tag)) ? tag.replace(/^vue-component-\d+-/, '') : 'anonymous-component';
- this._createdTime = Date.now();
-
- /* 增加人类方便查看的时间信息 */
- const timeObj = new Date(this._createdTime);
- this._createdHumanTime = `${timeObj.getHours()}:${timeObj.getMinutes()}:${timeObj.getSeconds()}`;
-
- /* 判断是否为函数式组件,函数式组件无状态 (没有响应式数据),也没有实例,也没生命周期概念 */
- if (this._componentName === 'anonymous-component' && !this.$parent && !this.$vnode) {
- this._componentName = 'functional-component';
- }
-
- helper.components[this._uid] = this;
-
- /**
- * 收集所有创建过的组件信息,此处只存储组件的基础信息,没销毁的组件会包含组件实例
- * 严禁对组件内其它对象进行引用,否则会导致组件实列无法被正常回收
- */
- const componentSummary = {
- uid: this._uid,
- name: this._componentName,
- tag: this._componentTag,
- createdTime: this._createdTime,
- createdHumanTime: this._createdHumanTime,
- // 0 表示还没被销毁
- destroyTime: 0,
- // 0 表示还没被销毁,duration可持续当当前查看时间
- duration: 0,
- component: this,
- chain
- };
- helper.componentsSummary[this._uid] = componentSummary;
-
- /* 添加到componentsSummaryStatistics里,生成统计信息 */
- Array.isArray(helper.componentsSummaryStatistics[this._componentName])
- ? helper.componentsSummaryStatistics[this._componentName].push(componentSummary)
- : (helper.componentsSummaryStatistics[this._componentName] = [componentSummary]);
-
- printLifeCycle(this, 'beforeCreate');
- },
- created: function () {
- /* 增加空白数据,方便观察内存泄露情况 */
- if (helper.ddConfig.enabled) {
- let needDd = false;
-
- if (helper.ddConfig.filters.length === 0) {
- needDd = true;
- } else {
- for (let index = 0; index < helper.ddConfig.filters.length; index++) {
- const filter = helper.ddConfig.filters[index];
- if (filter === this._componentName || String(this._componentName).endsWith(filter)) {
- needDd = true;
- break
- }
- }
- }
-
- if (needDd) {
- const count = helper.ddConfig.count * 1024;
- const componentInfo = `tag: ${this._componentTag}, uid: ${this._uid}, createdTime: ${this._createdHumanTime}`;
-
- /* 此处必须使用JSON.stringify对产生的字符串进行消费,否则没法将内存占用上去 */
- this.$data.__dd__ = JSON.stringify(componentInfo + ' ' + helper.methods.createEmptyData(count, this._uid));
-
- console.log(`[dd success] ${componentInfo} chain: ${this._componentChain}`);
- }
- }
-
- printLifeCycle(this, 'created');
- },
- beforeMount: function () {
- printLifeCycle(this, 'beforeMount');
- },
- mounted: function () {
- printLifeCycle(this, 'mounted');
- },
- beforeUpdate: function () {
- printLifeCycle(this, 'beforeUpdate');
- },
- updated: function () {
- printLifeCycle(this, 'updated');
- },
- beforeDestroy: function () {
- printLifeCycle(this, 'beforeDestroy');
- },
- destroyed: function () {
- printLifeCycle(this, 'destroyed');
-
- if (this._componentTag) {
- const uid = this._uid;
- const name = this._componentName;
- const destroyTime = Date.now();
-
- /* helper里的componentSummary有可能通过调用clear函数而被清除掉,所以需进行判断再更新赋值 */
- const componentSummary = helper.componentsSummary[this._uid];
- if (componentSummary) {
- /* 补充/更新组件信息 */
- componentSummary.destroyTime = destroyTime;
- componentSummary.duration = destroyTime - this._createdTime;
-
- helper.destroyList.push(componentSummary);
-
- /* 统计被销毁的组件信息 */
- Array.isArray(helper.destroyStatistics[name])
- ? helper.destroyStatistics[name].push(componentSummary)
- : (helper.destroyStatistics[name] = [componentSummary]);
-
- /* 删除已销毁的组件实例 */
- delete componentSummary.component;
- }
-
- // 解除引用关系
- delete this._componentTag;
- delete this._componentChain;
- delete this._componentName;
- delete this._createdTime;
- delete this._createdHumanTime;
- delete this.$data.__dd__;
- delete helper.components[uid];
- } else {
- console.error('存在未被正常标记的组件,请检查组件采集逻辑是否需完善', this);
- }
- }
- });
- }
-
- /*!
- * @name menuCommand.js
- * @version 0.0.1
- * @author Blaze
- * @date 2019/9/21 14:22
- */
-
- const monkeyMenu = {
- on (title, fn, accessKey) {
- return window.GM_registerMenuCommand && window.GM_registerMenuCommand(title, fn, accessKey)
- },
- off (id) {
- return window.GM_unregisterMenuCommand && window.GM_unregisterMenuCommand(id)
- },
- /* 切换类型的菜单功能 */
- switch (title, fn, defVal) {
- const t = this;
- t.on(title, fn);
- }
- };
-
- /**
- * 简单的i18n库
- */
-
- class I18n {
- constructor (config) {
- this._languages = {};
- this._locale = this.getClientLang();
- this._defaultLanguage = '';
- this.init(config);
- }
-
- init (config) {
- if (!config) return false
-
- const t = this;
- t._locale = config.locale || t._locale;
- /* 指定当前要是使用的语言环境,默认无需指定,会自动读取 */
- t._languages = config.languages || t._languages;
- t._defaultLanguage = config.defaultLanguage || t._defaultLanguage;
- }
-
- use () {}
-
- t (path) {
- const t = this;
- let result = t.getValByPath(t._languages[t._locale] || {}, path);
-
- /* 版本回退 */
- if (!result && t._locale !== t._defaultLanguage) {
- result = t.getValByPath(t._languages[t._defaultLanguage] || {}, path);
- }
-
- return result || ''
- }
-
- /* 当前语言值 */
- language () {
- return this._locale
- }
-
- languages () {
- return this._languages
- }
-
- changeLanguage (locale) {
- if (this._languages[locale]) {
- this._languages = locale;
- return locale
- } else {
- return false
- }
- }
-
- /**
- * 根据文本路径获取对象里面的值
- * @param obj {Object} -必选 要操作的对象
- * @param path {String} -必选 路径信息
- * @returns {*}
- */
- getValByPath (obj, path) {
- path = path || '';
- const pathArr = path.split('.');
- let result = obj;
-
- /* 递归提取结果值 */
- for (let i = 0; i < pathArr.length; i++) {
- if (!result) break
- result = result[pathArr[i]];
- }
-
- return result
- }
-
- /* 获取客户端当前的语言环境 */
- getClientLang () {
- return navigator.languages ? navigator.languages[0] : navigator.language
- }
- }
-
- var zhCN = {
- about: '关于',
- issues: '反馈',
- setting: '设置',
- hotkeys: '快捷键',
- donate: '赞赏',
- debugHelper: {
- viewVueDebugHelperObject: 'vueDebugHelper对象',
- componentsStatistics: '当前存活组件统计',
- destroyStatisticsSort: '已销毁组件统计',
- componentsSummaryStatisticsSort: '全部组件混合统计',
- getDestroyByDuration: '组件存活时间信息',
- clearAll: '清空统计信息',
- printLifeCycleInfo: '打印组件生命周期信息',
- notPrintLifeCycleInfo: '取消组件生命周期信息打印',
- printLifeCycleInfoPrompt: {
- lifecycleFilters: '请输入要打印的生命周期名称,多个可用,或|分隔,不输入则默认打印created',
- componentFilters: '请输入要打印的组件名称,多个可用,或|分隔,不输入则默认打印所有组件'
- },
- dd: '数据注入(dd)',
- undd: '取消数据注入(undd)',
- ddPrompt: {
- filter: '组件过滤器(如果为空,则对所有组件注入)',
- count: '指定注入数据的重复次数(默认1024)'
- }
- }
- };
-
- var enUS = {
- about: 'about',
- issues: 'feedback',
- setting: 'settings',
- hotkeys: 'Shortcut keys',
- donate: 'donate',
- debugHelper: {
- viewVueDebugHelperObject: 'vueDebugHelper object',
- componentsStatistics: 'Current surviving component statistics',
- destroyStatisticsSort: 'Destroyed component statistics',
- componentsSummaryStatisticsSort: 'All components mixed statistics',
- getDestroyByDuration: 'Component survival time information',
- clearAll: 'Clear statistics',
- dd: 'Data injection (dd)',
- undd: 'Cancel data injection (undd)',
- ddPrompt: {
- filter: 'Component filter (if empty, inject all components)',
- count: 'Specify the number of repetitions of injected data (default 1024)'
- }
- }
- };
-
- var zhTW = {
- about: '關於',
- issues: '反饋',
- setting: '設置',
- hotkeys: '快捷鍵',
- donate: '讚賞',
- debugHelper: {
- viewVueDebugHelperObject: 'vueDebugHelper對象',
- componentsStatistics: '當前存活組件統計',
- destroyStatisticsSort: '已銷毀組件統計',
- componentsSummaryStatisticsSort: '全部組件混合統計',
- getDestroyByDuration: '組件存活時間信息',
- clearAll: '清空統計信息',
- dd: '數據注入(dd)',
- undd: '取消數據注入(undd)',
- ddPrompt: {
- filter: '組件過濾器(如果為空,則對所有組件注入)',
- count: '指定注入數據的重複次數(默認1024)'
- }
- }
- };
-
- const messages = {
- 'zh-CN': zhCN,
- zh: zhCN,
- 'zh-HK': zhTW,
- 'zh-TW': zhTW,
- 'en-US': enUS,
- en: enUS,
- };
-
- /*!
- * @name i18n.js
- * @description vue-debug-helper的国际化配置
- * @version 0.0.1
- * @author xxxily
- * @date 2022/04/26 14:56
- * @github https://github.com/xxxily
- */
-
- const i18n = new I18n({
- defaultLanguage: 'en',
- /* 指定当前要是使用的语言环境,默认无需指定,会自动读取 */
- // locale: 'zh-TW',
- languages: messages
- });
-
- /*!
- * @name functionCall.js
- * @description 统一的提供外部功能调用管理模块
- * @version 0.0.1
- * @author xxxily
- * @date 2022/04/27 17:42
- * @github https://github.com/xxxily
- */
-
- const functionCall = {
- viewVueDebugHelperObject () {
- debug.log(i18n.t('debugHelper.viewVueDebugHelperObject'), helper);
- },
- componentsStatistics () {
- debug.log(i18n.t('debugHelper.componentsStatistics'), helper.methods.componentsStatistics());
- },
- destroyStatisticsSort () {
- debug.log(i18n.t('debugHelper.destroyStatisticsSort'), helper.methods.destroyStatisticsSort());
- },
- componentsSummaryStatisticsSort () {
- debug.log(i18n.t('debugHelper.componentsSummaryStatisticsSort'), helper.methods.componentsSummaryStatisticsSort());
- },
- getDestroyByDuration () {
- debug.log(i18n.t('debugHelper.getDestroyByDuration'), helper.methods.getDestroyByDuration());
- },
- clearAll () {
- helper.methods.clearAll();
- debug.log(i18n.t('debugHelper.clearAll'));
- },
-
- printLifeCycleInfo () {
- const lifecycleFilters = window.prompt(i18n.t('debugHelper.printLifeCycleInfoPrompt.lifecycleFilters'), localStorage.getItem('vdh_lf_lifecycleFilters') || 'created');
- const componentFilters = window.prompt(i18n.t('debugHelper.printLifeCycleInfoPrompt.componentFilters'), localStorage.getItem('vdh_lf_componentFilters') || '');
- lifecycleFilters && localStorage.setItem('vdh_lf_lifecycleFilters', lifecycleFilters);
- componentFilters && localStorage.setItem('vdh_lf_componentFilters', componentFilters);
-
- debug.log(i18n.t('debugHelper.printLifeCycleInfo'));
- helper.methods.printLifeCycleInfo(lifecycleFilters, componentFilters);
- },
-
- notPrintLifeCycleInfo () {
- debug.log(i18n.t('debugHelper.notPrintLifeCycleInfo'));
- helper.methods.notPrintLifeCycleInfo();
- },
-
- dd () {
- const filter = window.prompt(i18n.t('debugHelper.ddPrompt.filter'), localStorage.getItem('vdh_dd_filter') || '');
- const count = window.prompt(i18n.t('debugHelper.ddPrompt.count'), localStorage.getItem('vdh_dd_count') || 1024);
- filter && localStorage.setItem('vdh_dd_filter', filter);
- count && localStorage.setItem('vdh_dd_count', count);
- debug.log(i18n.t('debugHelper.dd'));
- helper.methods.dd(filter, Number(count));
- },
- undd () {
- debug.log(i18n.t('debugHelper.undd'));
- helper.methods.undd();
- }
- };
-
- /*!
- * @name menu.js
- * @description vue-debug-helper的菜单配置
- * @version 0.0.1
- * @author xxxily
- * @date 2022/04/25 22:28
- * @github https://github.com/xxxily
- */
-
- function menuRegister (Vue) {
- if (!Vue) {
- monkeyMenu.on('not detected ' + i18n.t('issues'), () => {
- window.GM_openInTab('https://github.com/xxxily/vue-debug-helper/issues', {
- active: true,
- insert: true,
- setParent: true
- });
- });
- return false
- }
-
- // 批量注册菜单
- Object.keys(functionCall).forEach(key => {
- const text = i18n.t(`debugHelper.${key}`);
- if (text && functionCall[key] instanceof Function) {
- monkeyMenu.on(text, functionCall[key]);
- }
- });
-
- // monkeyMenu.on('i18n.t('setting')', () => {
- // window.alert('功能开发中,敬请期待...')
- // })
-
- monkeyMenu.on(i18n.t('issues'), () => {
- window.GM_openInTab('https://github.com/xxxily/vue-debug-helper/issues', {
- active: true,
- insert: true,
- setParent: true
- });
- });
-
- // monkeyMenu.on(i18n.t('donate'), () => {
- // window.GM_openInTab('https://cdn.jsdelivr.net/gh/xxxily/vue-debug-helper@main/donate.png', {
- // active: true,
- // insert: true,
- // setParent: true
- // })
- // })
- }
-
- const isff = typeof navigator !== 'undefined' ? navigator.userAgent.toLowerCase().indexOf('firefox') > 0 : false;
-
- // 绑定事件
- function addEvent (object, event, method) {
- if (object.addEventListener) {
- object.addEventListener(event, method, false);
- } else if (object.attachEvent) {
- object.attachEvent(`on${event}`, () => { method(window.event); });
- }
- }
-
- // 修饰键转换成对应的键码
- function getMods (modifier, key) {
- const mods = key.slice(0, key.length - 1);
- for (let i = 0; i < mods.length; i++) mods[i] = modifier[mods[i].toLowerCase()];
- return mods
- }
-
- // 处理传的key字符串转换成数组
- function getKeys (key) {
- if (typeof key !== 'string') key = '';
- key = key.replace(/\s/g, ''); // 匹配任何空白字符,包括空格、制表符、换页符等等
- const keys = key.split(','); // 同时设置多个快捷键,以','分割
- let index = keys.lastIndexOf('');
-
- // 快捷键可能包含',',需特殊处理
- for (; index >= 0;) {
- keys[index - 1] += ',';
- keys.splice(index, 1);
- index = keys.lastIndexOf('');
- }
-
- return keys
- }
-
- // 比较修饰键的数组
- function compareArray (a1, a2) {
- const arr1 = a1.length >= a2.length ? a1 : a2;
- const arr2 = a1.length >= a2.length ? a2 : a1;
- let isIndex = true;
-
- for (let i = 0; i < arr1.length; i++) {
- if (arr2.indexOf(arr1[i]) === -1) isIndex = false;
- }
- return isIndex
- }
-
- // Special Keys
- const _keyMap = {
- backspace: 8,
- tab: 9,
- clear: 12,
- enter: 13,
- return: 13,
- esc: 27,
- escape: 27,
- space: 32,
- left: 37,
- up: 38,
- right: 39,
- down: 40,
- del: 46,
- delete: 46,
- ins: 45,
- insert: 45,
- home: 36,
- end: 35,
- pageup: 33,
- pagedown: 34,
- capslock: 20,
- num_0: 96,
- num_1: 97,
- num_2: 98,
- num_3: 99,
- num_4: 100,
- num_5: 101,
- num_6: 102,
- num_7: 103,
- num_8: 104,
- num_9: 105,
- num_multiply: 106,
- num_add: 107,
- num_enter: 108,
- num_subtract: 109,
- num_decimal: 110,
- num_divide: 111,
- '⇪': 20,
- ',': 188,
- '.': 190,
- '/': 191,
- '`': 192,
- '-': isff ? 173 : 189,
- '=': isff ? 61 : 187,
- ';': isff ? 59 : 186,
- '\'': 222,
- '[': 219,
- ']': 221,
- '\\': 220
- };
-
- // Modifier Keys
- const _modifier = {
- // shiftKey
- '⇧': 16,
- shift: 16,
- // altKey
- '⌥': 18,
- alt: 18,
- option: 18,
- // ctrlKey
- '⌃': 17,
- ctrl: 17,
- control: 17,
- // metaKey
- '⌘': 91,
- cmd: 91,
- command: 91
- };
- const modifierMap = {
- 16: 'shiftKey',
- 18: 'altKey',
- 17: 'ctrlKey',
- 91: 'metaKey',
-
- shiftKey: 16,
- ctrlKey: 17,
- altKey: 18,
- metaKey: 91
- };
- const _mods = {
- 16: false,
- 18: false,
- 17: false,
- 91: false
- };
- const _handlers = {};
-
- // F1~F12 special key
- for (let k = 1; k < 20; k++) {
- _keyMap[`f${k}`] = 111 + k;
- }
-
- // https://github.com/jaywcjlove/hotkeys
-
- let _downKeys = []; // 记录摁下的绑定键
- let winListendFocus = false; // window是否已经监听了focus事件
- let _scope = 'all'; // 默认热键范围
- const elementHasBindEvent = []; // 已绑定事件的节点记录
-
- // 返回键码
- const code = (x) => _keyMap[x.toLowerCase()] ||
- _modifier[x.toLowerCase()] ||
- x.toUpperCase().charCodeAt(0);
-
- // 设置获取当前范围(默认为'所有')
- function setScope (scope) {
- _scope = scope || 'all';
- }
- // 获取当前范围
- function getScope () {
- return _scope || 'all'
- }
- // 获取摁下绑定键的键值
- function getPressedKeyCodes () {
- return _downKeys.slice(0)
- }
-
- // 表单控件控件判断 返回 Boolean
- // hotkey is effective only when filter return true
- function filter (event) {
- const target = event.target || event.srcElement;
- const { tagName } = target;
- let flag = true;
- // ignore: isContentEditable === 'true', <input> and <textarea> when readOnly state is false, <select>
- if (
- target.isContentEditable ||
- ((tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') && !target.readOnly)
- ) {
- flag = false;
- }
- return flag
- }
-
- // 判断摁下的键是否为某个键,返回true或者false
- function isPressed (keyCode) {
- if (typeof keyCode === 'string') {
- keyCode = code(keyCode); // 转换成键码
- }
- return _downKeys.indexOf(keyCode) !== -1
- }
-
- // 循环删除handlers中的所有 scope(范围)
- function deleteScope (scope, newScope) {
- let handlers;
- let i;
-
- // 没有指定scope,获取scope
- if (!scope) scope = getScope();
-
- for (const key in _handlers) {
- if (Object.prototype.hasOwnProperty.call(_handlers, key)) {
- handlers = _handlers[key];
- for (i = 0; i < handlers.length;) {
- if (handlers[i].scope === scope) handlers.splice(i, 1);
- else i++;
- }
- }
- }
-
- // 如果scope被删除,将scope重置为all
- if (getScope() === scope) setScope(newScope || 'all');
- }
-
- // 清除修饰键
- function clearModifier (event) {
- let key = event.keyCode || event.which || event.charCode;
- const i = _downKeys.indexOf(key);
-
- // 从列表中清除按压过的键
- if (i >= 0) {
- _downKeys.splice(i, 1);
- }
- // 特殊处理 cmmand 键,在 cmmand 组合快捷键 keyup 只执行一次的问题
- if (event.key && event.key.toLowerCase() === 'meta') {
- _downKeys.splice(0, _downKeys.length);
- }
-
- // 修饰键 shiftKey altKey ctrlKey (command||metaKey) 清除
- if (key === 93 || key === 224) key = 91;
- if (key in _mods) {
- _mods[key] = false;
-
- // 将修饰键重置为false
- for (const k in _modifier) if (_modifier[k] === key) hotkeys[k] = false;
- }
- }
-
- function unbind (keysInfo, ...args) {
- // unbind(), unbind all keys
- if (!keysInfo) {
- Object.keys(_handlers).forEach((key) => delete _handlers[key]);
- } else if (Array.isArray(keysInfo)) {
- // support like : unbind([{key: 'ctrl+a', scope: 's1'}, {key: 'ctrl-a', scope: 's2', splitKey: '-'}])
- keysInfo.forEach((info) => {
- if (info.key) eachUnbind(info);
- });
- } else if (typeof keysInfo === 'object') {
- // support like unbind({key: 'ctrl+a, ctrl+b', scope:'abc'})
- if (keysInfo.key) eachUnbind(keysInfo);
- } else if (typeof keysInfo === 'string') {
- // support old method
- // eslint-disable-line
- let [scope, method] = args;
- if (typeof scope === 'function') {
- method = scope;
- scope = '';
- }
- eachUnbind({
- key: keysInfo,
- scope,
- method,
- splitKey: '+'
- });
- }
- }
-
- // 解除绑定某个范围的快捷键
- const eachUnbind = ({
- key, scope, method, splitKey = '+'
- }) => {
- const multipleKeys = getKeys(key);
- multipleKeys.forEach((originKey) => {
- const unbindKeys = originKey.split(splitKey);
- const len = unbindKeys.length;
- const lastKey = unbindKeys[len - 1];
- const keyCode = lastKey === '*' ? '*' : code(lastKey);
- if (!_handlers[keyCode]) return
- // 判断是否传入范围,没有就获取范围
- if (!scope) scope = getScope();
- const mods = len > 1 ? getMods(_modifier, unbindKeys) : [];
- _handlers[keyCode] = _handlers[keyCode].filter((record) => {
- // 通过函数判断,是否解除绑定,函数相等直接返回
- const isMatchingMethod = method ? record.method === method : true;
- return !(
- isMatchingMethod &&
- record.scope === scope &&
- compareArray(record.mods, mods)
- )
- });
- });
- };
-
- // 对监听对应快捷键的回调函数进行处理
- function eventHandler (event, handler, scope, element) {
- if (handler.element !== element) {
- return
- }
- let modifiersMatch;
-
- // 看它是否在当前范围
- if (handler.scope === scope || handler.scope === 'all') {
- // 检查是否匹配修饰符(如果有返回true)
- modifiersMatch = handler.mods.length > 0;
-
- for (const y in _mods) {
- if (Object.prototype.hasOwnProperty.call(_mods, y)) {
- if (
- (!_mods[y] && handler.mods.indexOf(+y) > -1) ||
- (_mods[y] && handler.mods.indexOf(+y) === -1)
- ) {
- modifiersMatch = false;
- }
- }
- }
-
- // 调用处理程序,如果是修饰键不做处理
- if (
- (handler.mods.length === 0 &&
- !_mods[16] &&
- !_mods[18] &&
- !_mods[17] &&
- !_mods[91]) ||
- modifiersMatch ||
- handler.shortcut === '*'
- ) {
- if (handler.method(event, handler) === false) {
- if (event.preventDefault) event.preventDefault();
- else event.returnValue = false;
- if (event.stopPropagation) event.stopPropagation();
- if (event.cancelBubble) event.cancelBubble = true;
- }
- }
- }
- }
-
- // 处理keydown事件
- function dispatch (event, element) {
- const asterisk = _handlers['*'];
- let key = event.keyCode || event.which || event.charCode;
-
- // 表单控件过滤 默认表单控件不触发快捷键
- if (!hotkeys.filter.call(this, event)) return
-
- // Gecko(Firefox)的command键值224,在Webkit(Chrome)中保持一致
- // Webkit左右 command 键值不一样
- if (key === 93 || key === 224) key = 91;
-
- /**
- * Collect bound keys
- * If an Input Method Editor is processing key input and the event is keydown, return 229.
- * https://stackoverflow.com/questions/25043934/is-it-ok-to-ignore-keydown-events-with-keycode-229
- * http://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html
- */
- if (_downKeys.indexOf(key) === -1 && key !== 229) _downKeys.push(key);
- /**
- * Jest test cases are required.
- * ===============================
- */
- ['ctrlKey', 'altKey', 'shiftKey', 'metaKey'].forEach((keyName) => {
- const keyNum = modifierMap[keyName];
- if (event[keyName] && _downKeys.indexOf(keyNum) === -1) {
- _downKeys.push(keyNum);
- } else if (!event[keyName] && _downKeys.indexOf(keyNum) > -1) {
- _downKeys.splice(_downKeys.indexOf(keyNum), 1);
- } else if (keyName === 'metaKey' && event[keyName] && _downKeys.length === 3) {
- /**
- * Fix if Command is pressed:
- * ===============================
- */
- if (!(event.ctrlKey || event.shiftKey || event.altKey)) {
- _downKeys = _downKeys.slice(_downKeys.indexOf(keyNum));
- }
- }
- });
- /**
- * -------------------------------
- */
-
- if (key in _mods) {
- _mods[key] = true;
-
- // 将特殊字符的key注册到 hotkeys 上
- for (const k in _modifier) {
- if (_modifier[k] === key) hotkeys[k] = true;
- }
-
- if (!asterisk) return
- }
-
- // 将 modifierMap 里面的修饰键绑定到 event 中
- for (const e in _mods) {
- if (Object.prototype.hasOwnProperty.call(_mods, e)) {
- _mods[e] = event[modifierMap[e]];
- }
- }
- /**
- * https://github.com/jaywcjlove/hotkeys/pull/129
- * This solves the issue in Firefox on Windows where hotkeys corresponding to special characters would not trigger.
- * An example of this is ctrl+alt+m on a Swedish keyboard which is used to type μ.
- * Browser support: https://caniuse.com/#feat=keyboardevent-getmodifierstate
- */
- if (event.getModifierState && (!(event.altKey && !event.ctrlKey) && event.getModifierState('AltGraph'))) {
- if (_downKeys.indexOf(17) === -1) {
- _downKeys.push(17);
- }
-
- if (_downKeys.indexOf(18) === -1) {
- _downKeys.push(18);
- }
-
- _mods[17] = true;
- _mods[18] = true;
- }
-
- // 获取范围 默认为 `all`
- const scope = getScope();
- // 对任何快捷键都需要做的处理
- if (asterisk) {
- for (let i = 0; i < asterisk.length; i++) {
- if (
- asterisk[i].scope === scope &&
- ((event.type === 'keydown' && asterisk[i].keydown) ||
- (event.type === 'keyup' && asterisk[i].keyup))
- ) {
- eventHandler(event, asterisk[i], scope, element);
- }
- }
- }
- // key 不在 _handlers 中返回
- if (!(key in _handlers)) return
-
- for (let i = 0; i < _handlers[key].length; i++) {
- if (
- (event.type === 'keydown' && _handlers[key][i].keydown) ||
- (event.type === 'keyup' && _handlers[key][i].keyup)
- ) {
- if (_handlers[key][i].key) {
- const record = _handlers[key][i];
- const { splitKey } = record;
- const keyShortcut = record.key.split(splitKey);
- const _downKeysCurrent = []; // 记录当前按键键值
- for (let a = 0; a < keyShortcut.length; a++) {
- _downKeysCurrent.push(code(keyShortcut[a]));
- }
- if (_downKeysCurrent.sort().join('') === _downKeys.sort().join('')) {
- // 找到处理内容
- eventHandler(event, record, scope, element);
- }
- }
- }
- }
- }
-
- // 判断 element 是否已经绑定事件
- function isElementBind (element) {
- return elementHasBindEvent.indexOf(element) > -1
- }
-
- function hotkeys (key, option, method) {
- _downKeys = [];
- const keys = getKeys(key); // 需要处理的快捷键列表
- let mods = [];
- let scope = 'all'; // scope默认为all,所有范围都有效
- let element = document; // 快捷键事件绑定节点
- let i = 0;
- let keyup = false;
- let keydown = true;
- let splitKey = '+';
-
- // 对为设定范围的判断
- if (method === undefined && typeof option === 'function') {
- method = option;
- }
-
- if (Object.prototype.toString.call(option) === '[object Object]') {
- if (option.scope) scope = option.scope; // eslint-disable-line
- if (option.element) element = option.element; // eslint-disable-line
- if (option.keyup) keyup = option.keyup; // eslint-disable-line
- if (option.keydown !== undefined) keydown = option.keydown; // eslint-disable-line
- if (typeof option.splitKey === 'string') splitKey = option.splitKey; // eslint-disable-line
- }
-
- if (typeof option === 'string') scope = option;
-
- // 对于每个快捷键进行处理
- for (; i < keys.length; i++) {
- key = keys[i].split(splitKey); // 按键列表
- mods = [];
-
- // 如果是组合快捷键取得组合快捷键
- if (key.length > 1) mods = getMods(_modifier, key);
-
- // 将非修饰键转化为键码
- key = key[key.length - 1];
- key = key === '*' ? '*' : code(key); // *表示匹配所有快捷键
-
- // 判断key是否在_handlers中,不在就赋一个空数组
- if (!(key in _handlers)) _handlers[key] = [];
- _handlers[key].push({
- keyup,
- keydown,
- scope,
- mods,
- shortcut: keys[i],
- method,
- key: keys[i],
- splitKey,
- element
- });
- }
- // 在全局document上设置快捷键
- if (typeof element !== 'undefined' && !isElementBind(element) && window) {
- elementHasBindEvent.push(element);
- addEvent(element, 'keydown', (e) => {
- dispatch(e, element);
- });
- if (!winListendFocus) {
- winListendFocus = true;
- addEvent(window, 'focus', () => {
- _downKeys = [];
- });
- }
- addEvent(element, 'keyup', (e) => {
- dispatch(e, element);
- clearModifier(e);
- });
- }
- }
-
- function trigger (shortcut, scope = 'all') {
- Object.keys(_handlers).forEach((key) => {
- const data = _handlers[key].find((item) => item.scope === scope && item.shortcut === shortcut);
- if (data && data.method) {
- data.method();
- }
- });
- }
-
- const _api = {
- setScope,
- getScope,
- deleteScope,
- getPressedKeyCodes,
- isPressed,
- filter,
- trigger,
- unbind,
- keyMap: _keyMap,
- modifier: _modifier,
- modifierMap
- };
- for (const a in _api) {
- if (Object.prototype.hasOwnProperty.call(_api, a)) {
- hotkeys[a] = _api[a];
- }
- }
-
- if (typeof window !== 'undefined') {
- const _hotkeys = window.hotkeys;
- hotkeys.noConflict = (deep) => {
- if (deep && window.hotkeys === hotkeys) {
- window.hotkeys = _hotkeys;
- }
- return hotkeys
- };
- window.hotkeys = hotkeys;
- }
-
- /*!
- * @name hotKeyRegister.js
- * @description vue-debug-helper的快捷键配置
- * @version 0.0.1
- * @author xxxily
- * @date 2022/04/26 14:37
- * @github https://github.com/xxxily
- */
-
- function hotKeyRegister () {
- const hotKeyMap = {
- 'shift+alt+a,shift+alt+ctrl+a': functionCall.componentsSummaryStatisticsSort,
- 'shift+alt+l': functionCall.componentsStatistics,
- 'shift+alt+d': functionCall.destroyStatisticsSort,
- 'shift+alt+c': functionCall.clearAll,
- 'shift+alt+e': function (event, handler) {
- if (helper.ddConfig.enabled) {
- functionCall.undd();
- } else {
- functionCall.dd();
- }
- }
- };
-
- Object.keys(hotKeyMap).forEach(key => {
- hotkeys(key, hotKeyMap[key]);
- });
- }
-
- /*!
- * @name vueDetector.js
- * @description 检测页面是否存在Vue对象
- * @version 0.0.1
- * @author xxxily
- * @date 2022/04/27 11:43
- * @github https://github.com/xxxily
- */
-
- function mutationDetector (callback, shadowRoot) {
- const win = window;
- const MutationObserver = win.MutationObserver || win.WebKitMutationObserver;
- const docRoot = shadowRoot || win.document.documentElement;
- const maxDetectTries = 1500;
- const timeout = 1000 * 10;
- const startTime = Date.now();
- let detectCount = 0;
- let detectStatus = false;
-
- if (!MutationObserver) {
- debug.warn('MutationObserver is not supported in this browser');
- return false
- }
-
- let mObserver = null;
- const mObserverCallback = (mutationsList, observer) => {
- if (detectStatus) {
- return
- }
-
- /* 超时或检测次数过多,取消监听 */
- if (Date.now() - startTime > timeout || detectCount > maxDetectTries) {
- debug.warn('mutationDetector timeout or detectCount > maxDetectTries, stop detect');
- if (mObserver && mObserver.disconnect) {
- mObserver.disconnect();
- mObserver = null;
- }
- }
-
- for (let i = 0; i < mutationsList.length; i++) {
- detectCount++;
- const mutation = mutationsList[i];
- if (mutation.target && mutation.target.__vue__) {
- let Vue = Object.getPrototypeOf(mutation.target.__vue__).constructor;
- while (Vue.super) {
- Vue = Vue.super;
- }
-
- /* 检测成功后销毁观察对象 */
- if (mObserver && mObserver.disconnect) {
- mObserver.disconnect();
- mObserver = null;
- }
-
- detectStatus = true;
- callback && callback(Vue);
- break
- }
- }
- };
-
- mObserver = new MutationObserver(mObserverCallback);
- mObserver.observe(docRoot, {
- attributes: true,
- childList: true,
- subtree: true
- });
- }
-
- /**
- * 检测页面是否存在Vue对象,方法参考:https://github.com/vuejs/devtools/blob/main/packages/shell-chrome/src/detector.js
- * @param {window} win windwod对象
- * @param {function} callback 检测到Vue对象后的回调函数
- */
- function vueDetect (win, callback) {
- let delay = 1000;
- let detectRemainingTries = 10;
- let detectSuc = false;
-
- // Method 1: MutationObserver detector
- mutationDetector((Vue) => {
- if (!detectSuc) {
- debug.info('------------- Vue mutation detected -------------');
- detectSuc = true;
- callback(Vue);
- }
- });
-
- function runDetect () {
- if (detectSuc) {
- return false
- }
-
- // Method 2: Check Vue 3
- const vueDetected = !!(win.__VUE__);
- if (vueDetected) {
- debug.info('------------- Vue 3 detected -------------');
- detectSuc = true;
- callback(win.__VUE__);
- return
- }
-
- // Method 3: Scan all elements inside document
- const all = document.querySelectorAll('*');
- let el;
- for (let i = 0; i < all.length; i++) {
- if (all[i].__vue__) {
- el = all[i];
- break
- }
- }
- if (el) {
- let Vue = Object.getPrototypeOf(el.__vue__).constructor;
- while (Vue.super) {
- Vue = Vue.super;
- }
- debug.info('------------- Vue 2 detected -------------');
- detectSuc = true;
- callback(Vue);
- return
- }
-
- if (detectRemainingTries > 0) {
- detectRemainingTries--;
- setTimeout(() => {
- runDetect();
- }, delay);
- delay *= 5;
- }
- }
-
- setTimeout(() => {
- runDetect();
- }, 100);
- }
-
- /**
- * 判断是否处于Iframe中
- * @returns {boolean}
- */
- function isInIframe () {
- return window !== window.top
- }
-
- /**
- * 由于tampermonkey对window对象进行了封装,我们实际访问到的window并非页面真实的window
- * 这就导致了如果我们需要将某些对象挂载到页面的window进行调试的时候就无法挂载了
- * 所以必须使用特殊手段才能访问到页面真实的window对象,于是就有了下面这个函数
- * @returns {Promise<void>}
- */
- async function getPageWindow () {
- return new Promise(function (resolve, reject) {
- if (window._pageWindow) {
- return resolve(window._pageWindow)
- }
-
- const listenEventList = ['load', 'mousemove', 'scroll', 'get-page-window-event'];
-
- function getWin (event) {
- window._pageWindow = this;
- // debug.log('getPageWindow succeed', event)
- listenEventList.forEach(eventType => {
- window.removeEventListener(eventType, getWin, true);
- });
- resolve(window._pageWindow);
- }
-
- listenEventList.forEach(eventType => {
- window.addEventListener(eventType, getWin, true);
- });
-
- /* 自行派发事件以便用最短的时候获得pageWindow对象 */
- window.dispatchEvent(new window.Event('get-page-window-event'));
- })
- }
-
- let registerStatus = 'init';
- window._debugMode_ = true
-
- ;(async function () {
- if (isInIframe()) {
- debug.log('running in iframe, skip init', window.location.href);
- return false
- }
-
- // debug.log('init')
-
- const win = await getPageWindow();
- vueDetect(win, function (Vue) {
- mixinRegister(Vue);
- menuRegister(Vue);
- hotKeyRegister();
-
- // 挂载到window上,方便通过控制台调用调试
- helper.Vue = Vue;
- win.vueDebugHelper = helper;
-
- // 自动开启Vue的调试模式
- if (Vue.config) {
- Vue.config.debug = true;
- Vue.config.devtools = true;
- Vue.config.performance = true;
- } else {
- debug.log('Vue.config is not defined');
- }
-
- debug.log('vue debug helper register success');
- registerStatus = 'success';
- });
-
- setTimeout(() => {
- if (registerStatus !== 'success') {
- menuRegister(null);
- debug.warn('vue debug helper register failed, please check if vue is loaded .', win.location.href);
- }
- }, 1000 * 10);
- })();