console.trace
打印堆栈
性能监控

陷入死循环,cpu飙升
切换source

手动暂停,查看call stack
js
import { highlightCode, consoleInfo } from "../utils/printCode";
export const infoData = {
tableexpose: async () => {
const list = [
"getSelectionRows 用于多选表格,返回当前选中的行数据,也可以监听select-change进行赋值",
"toggleRowSelection 用于多选表格,切换某一行的选中状态,第一个参数是行数据(row),如果使用了第二个参数,则可直接设置这一行选中与否",
"clearSelection 用于多选表格,清空用户的选择",
];
consoleInfo(list, "el-table expose");
},
validTable: async () => {
const list = [
"table的源数据作为form :model绑定的一个属性",
"在需要校验的组件外包裹el-form-item组件",
`<el-form-item :prop="'tableData.' + index + '.name'" :rules="rules.name">`,
`这里校验的是:model绑定数据下.tableData[index].name,所以组件也绑定这个值即可`,
`<el-input v-model="row.name">`,
];
consoleInfo(list, "el-form校验el-table");
},
虚拟列表: () => {
const list = [
"首先确定一下html结构,最外面 div 就是设定高度的窗口,内部一层的 div 需要计算出总高度,再内部一层的 div 通过 translateY XX 移动到合适的位置",
"初始时计算总高度和页面展示的子项个数,需要加上缓冲区",
"滚动过程中计算开始节点(需要减去上缓冲区)和需要展示的子项",
"对于不定高的,开始需要计算出高度数组和需要展示的子项个数,滚动过程中二分查找计算开始节点,并计算应该展示多少个子节点需要加上缓冲区",
];
},
"watch/watcheffect": () => {
const list = [
"都是用来监听响应式数据的变化,但使用场景不同",
"watch需要显式指定依赖对象,监听多个数据需要用数组,并能提供新值和旧值",
"watcheffect能自动收集所有依赖",
"watch可以配置flush,deep,immediated,watcheffect只有flush,没有deep,immediated相当于是true",
"如果只需要监听一个对象中的几个属性,使用watcheffect更好,因为它将只跟踪回调中使用到的属性,而不是递归的跟踪所有属性",
];
consoleInfo(list, "watch watcheffect的区别");
},
"ref/reactive": () => {
const list = [
"都是用来创建响应式数据,但是使用场景有些不同",
"reactive只能用于复杂数据类型,不能替换整个对象,对解构操作不友好。而ref更为通用,但是使用需要多写一个.value。",
"并且需要注意ref有自动解包策略",
"watch对ref reactive数据监听不同,如果是reactive,修改它的任何属性都会触发",
"对应ref,默认情况下只会对.value的重新分配做出反应,但是可以使用deep让他监听所有的嵌套属性",
"https://github.com/orgs/vuejs/discussions/9428(playground中有bug未解决)",
];
consoleInfo(list, "ref reactive");
},
"watch/computed": () => {
const list = [
"watch是监听动作,computed是计算属性",
"watch没缓存,只要监听的数据变化就执行。computed有缓存,只有响应式数据改变才会重新计算",
"watch可以执行异步操作,computed不行",
"watch常用于一个数据影响多个数据,而computer常用于多个数据影响一个数据",
];
consoleInfo(list, "watch/computed 的区别");
},
"defineProperty/proxy": () => {
const list = [
"使用defineProperty是因为当时proxy兼容性不好",
"defineProperty只能劫持对象属性的getter和setter,并且无法监听会修改原数组的数组方法,所以对这些方法就行重写",
"proxy能直接劫持整个对象",
"可以直接监听对象,数组的变化,并且拦截类型多达13种",
];
consoleInfo(list, "defineProperty/proxy 的区别");
},
scoped: () => {
const list = [
"scoped会为组件生成唯一标识,并在dom上添加这个属性,选择器也会在末尾加上这个属性选择器",
"使用scoped无法修改第三方组件库的样式,因为最后选择器会加上这个属性,但是使用样式穿透可以实现修改样式",
"本质是用了样式穿透后,在deep之后的选择器最后就不会加上这个属性",
"或者新增一个不带scoped的style,但要注意不要产生全局污染",
];
consoleInfo(list, "scoped");
},
js基本数据类型有哪些及它们的区别: () => {
const list = [
"js有八种数据类型,分别是null,undefined,number,string,boolean,object,symbol,bigint",
"symbol和bigint是es6新增的,symbol是为了创建一个独一无二的数据,解决可能出现的全局变量冲突的问题",
"js的number类型是基于IEEE754标准,最大可以表示的数是2^53-1,超过这个范围精度会丢失,bigint能表示任意大小的数,不会出现精度丢失",
"这些数据可以分为原始数据类型和引用数据类型",
"两种类型的区别在于存储位置的不同,原始数据类型放在栈中,引用数据类型放在堆中.但是在栈中会存放指向堆的指针",
];
},
数据类型检测的方式有哪些: () => {
const list = [
"typeof",
"typeof null 的结果为 object,这是官方承认的 typeof 的错误,这个问题来自于 JavaScript 语言的早期阶段,并为了兼容性而保留了下来。null 绝对不是一个 object。null 有自己的类型,它是一个特殊值。typeof 的行为在这里是错误的。",
"typeof alert 按理应该是返回 object。但是 typeof 会对函数区分对待,并返回 function。这也是来自于 JavaScript 语言早期的问题。从技术上讲,这种行为是不正确的,但在实际编程中却非常方便。",
"instanceof,其内部运行机制是判断在其原型链中能否找到该类型的原型,只能正确判断引用数据类型,而不能判断基本数据类型",
];
},
this: () => {
const list = [
"指向当前执行上下文中的 执行环境 或 函数调用的上下文",
"箭头函数的写法更简洁,箭头函数没有this,继承与外部词法环境,不能被修改,没有arguments,不能成为构造函数",
];
},
"var,let,const": () => {
const list = [
"let const 有块级作用域,var没有",
"var允许重复声明",
"使用var声明的全局函数和变量会成为全局对象的属性",
"var声明会被提升,,但是赋值不会,let const有暂时性死区",
"const必须设置初始值,const声明之后不能重新赋值",
];
},
new: () => {
const list = [
"创建一个空对象分配给this",
"执行函数体,通常会修改this",
"返回this",
];
},
es6: () => {
const list = [
"let const",
"箭头函数",
"解构赋值",
"模版字符串",
"promise",
"扩展运算符",
];
},
promise: () => {
const codeSnippet = `//ajax改造成promise
function ajax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
// 设置请求成功的回调
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText); // 请求成功,返回响应内容
} else {
reject(new Error()); // 请求失败
}
};
// 设置请求失败的回调
xhr.onerror = function() {
reject(new Error('Network error')); // 网络错误
};
// 发送请求
xhr.send();
});
}
// 使用 Promise 的方式进行调用
ajax('https://api.example.com/data')
.then(response => {
console.log('Success:', response);
})
.catch(error => {
console.log('Error:', error);
});
`;
highlightCode(codeSnippet);
},
"null/undefined": () => {
const list = [
"基本是同义的,只有一些细微的差别,null表示此处不应该有值,undefined表示此处应该有一个值,只是没有定义",
"所以访问一个不存在的对象属性返回是undefined",
"在双等检查中返回true,除此之外,它们在双等检查中不会进行隐式转换",
];
consoleInfo(list, "null undefined的区别");
},
promise执行顺序console: () => {
// 第一部分:Promise的执行顺序和说明
const list1 = [
`new Promise(resolve => {
console.log('test');
resolve();
});`,
"new Promise在then之前都是同步的,会立即打印test",
`new Promise(resolve => {
resolve();
console.log('test');
reject();
});`,
`执行了resolve、打印"test"、reject,这3句代码都会执行,但是reject不会生效`,
"方法:从上往下执行,先执行同步代码,微任务放入一个队列,宏任务放入一个队列,promise.then之前都是同步的",
];
consoleInfo(list1, "promise执行顺序console");
// 第二部分:第一个Promise示例(复杂的Promise嵌套和微任务)
const test1 = `
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve();
}, 0);
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
}));
first().then((arg) => {
console.log(arg);
});
console.log(4);
`;
// 预期输出顺序:3, 7, 4, 1, 2, 5
highlightCode(test1);
// 第三部分:关于async/await的注意点
const list2 = [
"async await,await之前的包括await这行都是同步执行,下面的进入微任务",
"async修饰的函数,如果不是返回一个promise,则会包装成一个已经fulfilled(resolve)的promise",
"函数return的是promise这种特殊情况,return意味着外层内容是等到return结果之后才执行的,return未完成,这个then就未完成",
"所以这个promsie会被加入微任务,但是对于async修饰的函数,并且不是返回promise,默认会被promise包装,这个promise不会进入微任务",
];
consoleInfo(list2, "async await的注意点");
// 第四部分:async/await 示例代码
const test2 = `
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
`;
// 预期输出顺序:script start、async1 start、async2、promise1、script end、async1 end、promise2
highlightCode(test2);
const test3 = `
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
return new Promise((resolve, reject) => {
resolve()
});
}
console.log('script start');
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
`;
// 预期输出顺序:script start、async1 start、async2、promise1、script end、promise2、async1 end
highlightCode(test3);
const test4 = `
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4);
}).then((res) => {
console.log(res)
});
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6);
});
`;
highlightCode(test4);
// 预期输出顺序:0, 1, 2, 3, 4, 5, 6
// 备注说明:
// return意味着外层内容是等到return结果之后才执行的,这个promise默认会执行.then从而加入微任务
// return Promise.resolve(4)会占据两个微任务
// 参考文章链接:
// https://juejin.cn/post/7092396674693201956
// https://blog.csdn.net/qq_53109172/article/details/138552985
// https://juejin.cn/post/6945319439772434469#heading-31
},
};