提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 一、Html
- 二、CSS
- 1. BFC布局
- 2. 定位总结
- 3. 动画
- 1. transform变换
- 2. transition过渡
- 3. @keyframes 和 animation
- 4. 伸缩盒模型:flex布局
- 5. 清除浮动
- 6. Less
- 三、JS
- 1. 逻辑中断
- 2. 原型
- 3. 数据扁平化
- 4. offsetwidth、clientwidth、scrollwidth
- 5. 节流防抖
- 6. 深拷贝浅拷贝
- 7. 闭包
- 8. 哪些非 boolean 值被强制转换为一个 boolean 时是 false
- 9. 数组去重
- 10. js数组
- 11. 0.1+0.2不等于0.3问题
- 12. 判断数组的方法
- 13. ES6新特性
- 14. void 0 和undefined的关系
- 15. -1/0、0/0、1/0 的输出分别是什么
- 16. js获取对象属性的方法
- 17. js实现继承的方法
- 18. 面向对象的三大特性
- 19. async / await
- 四、浏览器
- 1. 浏览器渲染
- 2. 回流重绘
- 3. 优化性能及性能指标
- 4. 浏览器拿到url之后的步骤
- 5. http的状态码
- 6. 跨域及其解决办法
- 7. 内存泄漏
- 8. 垃圾回收机制
- 9. 浏览器缓存
- 五、设计模式
- 1. 工厂模式
- 2. 单例模式
- 六、VUE
- 1. vue2与3的区别
- 2. vue中prop的验证类型
- 3. vue组件通信
- 4. 父子组件生命周期
- 5. 数据请求一般放在哪些生命周期钩子里,这些钩子之间有什么区别
- 6. Vue Router
- 不同的导航方式
- 7. ref和reactive
- 8. vue-router
- 9. v-if和v-for最好不要一起用
- 10. vue的内置组件
提示:以下是本篇文章正文内容,下面案例可供参考
一、Html
二、CSS
1. BFC布局
BFC(block formatting context)块级格式化上下文,他是页面中的一块渲染区域,并且有一套属于自己的渲染规则,BFC 是一个独立的布局环境,具有BFC特性的元素可以看作是隔离的独立容器,容器里面的元素不会在布局上影响到外面的元素。
触发BFC:
- 浮动元素
- 定位元素:绝对定位元素、固定定位元素(absolute、fixed)
- 非块级盒子的块级容器:display:inline-block,flex,table,table-cell、table-caption、inline-table、inline-flex、grid、inline-grid
- overflow 值不为visiable 的块级盒子:overflow:hidden、auto、scroll
- display:flow-root【新属性,BFC创建新方式,没有任何副作用,注意浏览器兼容】
使用场景
-
清除浮动:overflow:hidden(浮动塌陷,包含块没有设置高度或者是自适应的时候、包含块就不能撑起来,变成塌陷的状态。)
-
防止浮动文字环绕
-
解决边距重叠问题:根据 BFC 的定义,两个元素只有在同一BFC 内,才有可能发生垂直外边距的重叠,包括相邻元素、嵌套元素。
- 对于相邻元素,只要给它们加上 BFC 的外壳,就能使它们的 margin 不重叠;
- 对于嵌套元素,只要让父级元素触发 BFC(比如给父级加overflow:hidden),就能使父级 margin 和当前元素的 margin 不重叠。
参考地址:https://juejin/post/6991867254803529765
2. 定位总结
定位 | 参考点 | 是否脱离文档流 |
---|---|---|
relative | 自己原来的位置 | × |
absolute | 包含块(第一个有定位属性的父元素) | √ |
fixed | 视口 | √ |
sticky | 最近的拥有滚动机制的祖先元素 | × |
固定定位和绝对定位后,元素变成定位元素:默认宽高由内容撑开,且依旧可以设置宽高。
3. 动画
1. transform变换
transform 属性允许你对元素进行旋转、缩放、倾斜或移动等转换。
/* 将元素顺时针旋转45度 */
.element {
transform: rotate(45deg);
}
其他常见的 transform 函数:
translate(x, y):移动元素
scale(x, y):缩放元素
skew(x, y):倾斜元素
位移配合定位可实现水平垂直居中:
.box{
position : absolute;
left : 50%;
top : 50%;
transform : translate(-50%,-50%);
}
变换原点:transform-origin
元素变换时默认是元素的中心。transform-origin可以设置变换的原点。
修改变换原点对位移没有影响, 但是对旋转缩放会产生影响。
如果提供两个值,第一个是横坐标第二个是纵坐标。
如果只有一个值,另一个默认为50%。
3D变换的首要操作:父元素必须开启3D空间。
2. transition过渡
transition 属性用于控制元素状态变化时的过渡效果,例如在鼠标悬停时改变颜色、大小等。
.element {
background-color: lightblue;
transition: background-color 0.5s ease-in-out;
}
.element:hover {
background-color: lightgreen;
}
当你将鼠标悬停在 .element 上时,背景色将会在 0.5 秒内平滑地变为 lightgreen
- transition-property:定义哪个属性需要过渡:none,all,某个属性名(值为数字或值能转为数字的属性能过渡,否则不支持过渡)(常见属性:颜色、长度、百分比、z-index、opacity、2D转换属性、3D转换属性、阴影)
- transition-duration:设置过渡的持续时间
- transition-delay:设置开始过渡的延迟时间
- transition-timing-function:设置过渡的类型
– ease:平滑过渡
– linear:线性过渡
– ease-in:慢→快
– ease-out:快→慢
– ease-in-out:慢→快→慢
3. @keyframes 和 animation
keyframes 允许你定义一个动画序列,并通过 animation 属性将动画应用到元素上。@keyframes 定义了不同时间点的样式,animation 属性控制动画的持续时间、次数等。
/* 定义旋转和缩放的动画 */
@keyframes rotateScale {
0% {
transform: rotate(0deg) scale(1);
}
50% {
transform: rotate(180deg) scale(1.5);
}
100% {
transform: rotate(360deg) scale(1);
}
}
/* 将动画应用到元素 */
.animated-box {
width: 100px;
height: 100px;
background-color: lightcoral;
animation: rotateScale 2s infinite ease-in-out;
}
- animation-name:给元素执行具体的动画
- animation-duration: 设置动画所需时间
- animation-delay:设置延迟时间
- animation-timing-function:设置动画类型
- animation-iteration-count:指定动画播放次数(number,infinite无限循环)
- animation-direction:指定动画方向(normal、reverse、alternate、alternate-reverse)
- animation-fill-mode:设置动画外的状态(forward、backwards)
- animation-play-state:设置动画的播放状态(running、paused)
4. 伸缩盒模型:flex布局
伸缩容器:开启了flex的元素
伸缩项目:伸缩容器的子元素自动成为伸缩项目。无论原来是哪种元素,一旦成为伸缩项目都会“块状化”。
- flex-direction:主轴方向
- row:从左向右(默认)
- row-reverse:从右向左
- column:主轴方向垂直从上到下
- column-reverse:从下到上
- flex-wrap:换行方式
- nowrap:不换行(默认值)
- wrap:换行
- wrap-reverse:反向换行
- flex-flow:上述两个的复合属性,无顺序要求。
- justify-content:主轴对齐方式
- flex-start:起点对齐
- flex-end:终点对齐
- center:居中
- space-between:均匀分布,两端对齐
- space-around: 均匀分布,两端距离是中间的一半
- space-evenly:均匀分布,两端距离是中间一致
- align-item:侧轴对齐方式(一行) :flex-start、flex-end、center、baseline(伸缩项目第一行文字基线对齐)、stretch(伸缩项目未设置高度,将占满整个容器的高度。默认值)
- align-content:侧轴对齐方式(多行)
flex实现水平垂直居中
- 父容器开启flex布局,使用 justify-content 和 align-item 实现水平垂直居中
.outer{
width:400px;
height:400px;
display:flex;
justify-content:center;
align-item:center;
}
- 父容器开启 flex 布局,随后子容器 margin: auto
.outer{
width:400px;
height:400px;
display:flex;
}
.inner{
wigth:100px;
height:100px;
margin:auto;
}
垂直水平居中的其他方法:
绝对定位元素的垂直水平居中
.parent {
position: relative;
height: 300px;
background: lightgray;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 关键 */
width: 100px;
height: 100px;
background: green;
}
适用于固定宽高的元素
.parent {
position: relative;
height: 300px;
background: lightgray;
}
.child {
position: absolute;
top: 0; bottom: 0; left: 0; right: 0;
margin: auto;
width: 100px;
height: 100px;
background: purple;
}
伸缩性:
- flex-basis:主轴的基准长度,会让宽或高失效,默认值为auto。
- flex-grow:伸缩项目的放大比例,默认为0,
- 若所有伸缩项目 flex-grow:1:将等分剩余空间。
- 若三个伸缩项目的flex-grow值分别为1、2、3,则分别瓜分到1/6、2/6、3/6的空间。
- flex-shrink:伸缩项目的压缩比例,默认为1,即如果空间不足则该项目会缩小。
flex复合属性:复合flex-grow、flex-shrink、flex-basis三个属性
- flex:auto => flex:1 1 auto
- flex:1 => flex:1 1 0
- flex:none => flex:0 0 auto
- flex:0 auto => flex:0 1 auto
flex:1 意味着该项目会按比例扩展以占据容器的剩余空间,并在容器空间不足时按比例缩小
flex:1 和 flex:auto的区别:
flex:1 不考虑项目本身的大小,只根据剩余空间进行伸缩。
flex:auto 考虑项目本身大小同时也会根据剩余空间进行伸缩。
5. 清除浮动
- clear:在浮动元素后加一个空元素,并使用css的clear属性来清除浮动
- overflow:父元素使用overflow:auto / hidden;适用于不需要显示滚动条的布局
- ::after:父元素添加::after,并设置clear: both;
- flex:父元素display:flex
- Grid: 父元素布局设置为grid
6. Less
Less是CSS预处理器,是一种动态的样式表语言。
Less的特性包括变量(Variables)(@声明变量)、混入(Mixins)、嵌套(Nesting)、运算(Operations)、转义(Escaping)、函数(Functions)、命名空间和访问符、映射(Maps)、作用域(Scope)、注释(Comments)、导入(Importing)。
Sass是在服务端处理的,需要使用编译工具将Sass代码编译成CSS文件。
Less则需要在客户端使用JavaScript引入Less文件,并使用Less.js将Less代码编译成CSS文件。
三、JS
1. 逻辑中断
短路运算:在逻辑与(&&)和逻辑或(||)的操作中,如果左边的表达式已经能够确定整个表达式的结果,那么就不会再去计算右边的表达式。
2. 原型
每一个javascript对象(除null外)创建的时候,都会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。
- __proto__是实例指向原型的属性
- prototype是对象或者构造函数指向原型的属性
- constructor:每个原型都有一个constructor属性,指向该关联的构造函数。
https://blog.csdn/qq_34645412/article/details/105997336
3. 数据扁平化
将一个多维数组转换为一维数组。
可以通过递归、Array.propotype.flat()、或者通过reduce方法结合concat实现。
flat 方法可以接受一个可选的参数 depth,用来指定扁平化的深度。
不传参数或传 1 时,默认扁平化第一层嵌套数组。
传入数字 n 表示将数组扁平化到第 n 层深度。
如果传入 Infinity 作为参数,那么不管嵌套多深,都会被扁平化为一维数组。
let arr = [1,[2,[3,[4,5]]]];
console.log(arr.flat(1));//[1,2,[3,[4,5]]]
// 扁平化两层
console.log(arr.flat(2));//[1,2,3,[4,5]]
// 全部扁平化
console.log(arr.flat(Infinity));//[1,2,3,4,5]
递归
let arr = [1,[2,[3,[4,5]]]];
function flatten(arr){
let result = [];
for (let i = 0; i<arr.length;i++){
if(Array.isArray(arr[i])){
result = result.concat(flatten(arr[i]));
}
else{
result = result.concat(arr[i]);
}
}
return result;
}
console.log(flatten(arr));
reduce
reduce函数会遍历每一个元素,每次遍历都会执行一个回调函数,该函数有两个参数,一个是上一次回调函数返回的值,还有一个参数就是当前元素的值。可以给初始值,不给的话就默认初始值为第一个元素,然后从第二个元素开始遍历。最后返回最后一次回调函数返回的值。
let arr = [1,[2,[3,[4,5]]]];
function flatten(arr){
return arr.reduce((result,nowvalue)=>
Array.isArray(nowvalue)? result.concat(flatten(nowvalue)) : result.concat(nowvalue),[]);
}
console.log(flatten(arr));
4. offsetwidth、clientwidth、scrollwidth
offsetWidth:返回的是 元素的css宽度+padding+border+垂直方向滚动条的宽度(如果有)
clientWidth:padding+内容
内联元素以及没有 CSS 样式的元素的 clientWidth 属性值为 0。
scrollWidth:包括由于overflow溢出而在屏幕上不可见的内容。宽度与clientWidth一样
5. 节流防抖
节流:n秒内只运行1次,如果重复触发只有一次生效。
防抖:n秒后执行该事件,如果n秒内重复触发,则重新计时。
节流:
由于 setTimeout 是异步执行的,它会在延迟之后调用 fn,而此时可能已经改变了 this 或者丢失了参数,所以需要提前保存这些信息,确保在 setTimeout 回调时能够正确使用。
function throttled(fn, wait){
let timer;
return function(){
let _this = this;
let args = arguments;
if(!timer){
timer = setTimeout(function(){
timer = null;
fn.apply(_this, args);
},wait);
}
}
}
实际应用:滚动加载,加载更多或滚到底部监听 搜索框,搜索联想功能
防抖:
function debounce(func,delay){
let time;
return function (){
clearTimeout(time);
time = setTimeout((...args)=>{
func.apply(this,args);
}, delay);
}
}
实际应用:在搜索框输入时,可以防止每次按键都发送请求,而是等用户停止输入一段时间后再次发送请求。
手机号、邮箱验证输入检测、窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="search-input" placeholder="请输入关键字信息进行搜索">
<div id="search-result">搜索结果</div>
<script>
function debounce(func, delay) {
let time ;
return function(...args){
clearTimeout(time);
time = setTimeout(()=>{
func.apply(this, args);
}, delay);
}
}
function search(query){
const resultDiv = document.querySelector("#search-result");
resultDiv.innerHTML = `搜素结果:${query}`;
}
const debouncedSearch = debounce(search,1000);
document.addEventListener("DOMContentLoaded", ()=>{
const searchInput = document.querySelector("#search-input");
searchInput.addEventListener("input", ()=>{
const query = searchInput.value.trim();
if(query){
debouncedSearch(query);
}
});
});
</script>
</body>
</html>
6. 深拷贝浅拷贝
浅拷贝:只复制对象的第一层属性,嵌套的对象仍然引用原始对象中的数据。
Object.assign()、展开运算符、Array.prototype.slice()等
深拷贝:不仅复制对象的所有属性,还包括所有嵌套对象的值,使得新对象与原始对象完全独立。
深拷贝常用:
- JSON.stringify() 和JSON.parse()
let obj1 = { name :"Jia",info : {age:"24"}};
let obj2 = JSON.parse(JSON.stringify(obj1));
优点:适合处理简单的对象或数组
缺点:不能处理函数、symbol、undefined 等特殊类型。不能拷贝对象的原型链和循环引用对象。
- 递归深拷贝
可以拷贝包含嵌套对象和数组的结构
function deepClone(obj){
if(obj === null || obj !=='Object' ){
return obj;
}
let copy = Array.isArray(obj) ? []:{};
for (let key in obj){
if(obj.hasOwnProperty(key)){
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
缺点:
对于深度非常大的嵌套对象,递归可能会导致性能问题。
无法处理循环引用的对象结构。
穿插知识:hasOwnProperty:是用来判断一个对象是否有你给出的名称的属性或对象。但是无法检查原型链上的属性,该属性必须是对象本身的一个成员。
for in / for of的区别: for in 适合遍历对象,用于遍历对象的可枚举属性,返回的是键名或者下标。 for of 适合遍历可迭代对象(如数组、字符串、Map、Set等),返回的是元素值。
- Lodash的cloneDeep方法
import _ from "lodash";
const obj = { a: 1, b: { c: 2 }, d: new Date() };
const copy = _.cloneDeep(obj);
console.log(copy);
console.log(copy.b === obj.b); // false
console.log(copy.d === obj.d); // false
7. 闭包
闭包指有权访问另一个函数作用域变量的函数。
当一个函数嵌套另一个函数中时,内部函数可以访问外部函数的变量,即使外部函数已经返回了。这种情况下,内部函数就形成了一个闭包,他保留了外部函数的作用域链并且可以继续访问这些变量。
闭包容易导致内存泄漏。闭包中的变量存储的位置是堆内存。
function outfun(){
let age = 10;
function innerfun(){
age++;
return age;
}
return innerfun;
}
const add = outfun();
console.log(add());
实际应用:1. 数据封装和私有变量 2. 函数工厂 3.节流防抖 4. 事件处理 5. 在异步操作完成时维护状态。
8. 哪些非 boolean 值被强制转换为一个 boolean 时是 false
- false:布尔值 false 本身。
- 0:数字零。
- -0:负零(虽然在数值上与 0 相同,但在某些情况下会被视为不同的值)。
- NaN:表示“不是一个数字”的值。
- “” 或 ‘’:空字符串(无论是单引号还是双引号)。
- null:表示“无”或“没有值”。
- undefined:表示“未定义”的值。
undefined == false 结果为false
- document.all(在某些旧版浏览器中):document.all 在 JavaScript 中是一个特殊的对象,在强制转换为布尔值时也会返回 false,但这个用法较少见且通常不推荐
9. 数组去重
- var newArr = […new Set(arr)];Array.from(new Set(arr));
Set是es6新增的数据结构,似于数组,但它的一大特性就是所有元素都是唯一的,没有重复的值,我们一般称为集合。 - 利用includes去重
let list = [ 8, 8, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8];
let newList =[];
for (let i = 0;i<list.length;i++)
{
if(!newList.includes(list[i])){
newList.push(list[i]);
}
}
console.log(newList);
- 利用map去重
- 利用单层for循环去重
- 利用indexof去重
10. js数组
- indexOf():在一个数组上调用它,并传入要查找的元素作为参数。它会返回元素首次出现的索引位置,如果元素不存在,返回-1。
还可以传入第二个参数来指定从哪个索引开始搜索元素。
- 字符串转数组进行排序:
- strs[i].split(‘’).sort().join(‘’);
- let array = Array.from(str); array.sort(); let key = array.toString();
11. 0.1+0.2不等于0.3问题
原因:js中数字是二进制浮点数保存的。0.1,0.2在这种在二进制中是无限循环的小数。
解决方法:
- 设置误差范围:Number.EPSILION。
- toFixed():四舍五入,会返回一个字符串类型的结果,需要注意类型转换。
- Number.toPrecision():格式化数字并减少精度问题。
12. 判断数组的方法
- Array.isArray();
- obj instanceof Array;
- Object.prototype.toString.call(obj).slice(8,-1) === ‘Array’;
- obj._proto_ === Array.prototype;
- Array.prototype.isPrototypeOf(obj);
13. ES6新特性
1)let和 const:具有块级作用域,let 用来声明变量可重新赋值,const 用来声明常量不可再次赋值。
2)箭头函数:新的函数声明方式,语法简洁。
3)模版字符串:字符串插值功能,可定义多行字符串。
4)解构赋值:是一种 JavaScript 表达式,它允许从数组或对象中提取属性或值,并将这些值赋给其他变量,
5)默认参数:函数参数可设置默认值,
6)扩展运算符:可以将数组展开为逗号分隔的参数序列,或者合并多个对象或数组。
7)类与模块:通过 class 关键字定义类,使用 import 和 export 来导入和导出模块
8)Promise:用于处理异步操作。
9)Symbol和迭代器:提供了一种新的原始数据类型和自定义迭代行为的方式。
10)新的数据结构:Map、Set。
11)其他:对象属性简写,属性和方法简写,提升了JavaScript 的编码效率和可读性。
14. void 0 和undefined的关系
void 是js 的一个关键词,它可以执行一个表达式,然后始终返回undefined。undefined 是js的一个全局变量,表示未定义。在ES5+中,undefined是只读的,但是在之前的版本ES3以前,是可以被重新赋值的。
15. -1/0、0/0、1/0 的输出分别是什么
-1 / 0: -Infinity 负数除以 0,结果趋向负无穷大
0 ÷ 0 :NaN 数学上0 ÷ 0是未定义的,所以 JavaScript 返回 NaN
1/0:正数除以 0,数学上趋向于 +∞,所以 JavaScript 返回 Infinity
额外:
console.log(Infinity / Infinity); // NaN
console.log(Infinity - Infinity); // NaN
console.log(0 * Infinity); // NaN
console.log(1 / Infinity); // 0
console.log(-1 / Infinity); // -0
16. js获取对象属性的方法
- 点运算符(obj.key):. 不能用于访问 包含特殊字符(如 -、空格)的属性。
- 方括号运算符(obj[‘key’]):键是动态的(变量)、键包含特殊字符(如 -、空格)、键是数字
- Object.getOwnPropertyDescriptor():获取属性的详细信息,如可写性、可枚举性等。
- Object.hasOwn() / Object.prototype.hasOwnProperty():检查属性是否是对象自身的,而不是继承的
const obj = { name: "Alice" };
console.log(Object.hasOwn(obj, "name")); // true ✅ 推荐(ES2022+)
console.log(obj.hasOwnProperty("name")); // true ✅ 旧版支持
console.log("name" in obj); // true(但会检查原型链)
17. js实现继承的方法
- 原型链继承:让子类的prototype指向父类的实例。
function Parent() {
this.name = "Parent";
this.colors = ["red", "blue"];
}
Parent.prototype.getName = function() {
return this.name;
};
function Child() {}
Child.prototype = new Parent(); // 继承
Child.prototype.constructor = Child; // 修正构造函数指向
const child1 = new Child();
child1.colors.push("green");
console.log(child1.getName()); // "Parent"
console.log(child1.colors); // ["red", "blue", "green"]
const child2 = new Child();
console.log(child2.colors); // ["red", "blue"] ✅ 不受 child1 修改影响
共享属性问题:如果父类属性是引用类型(如数组),修改一个实例的属性会影响所有实例。
- 借用构造函数继承:在子类构造函数内部调用 Parent.call(this),让父类的 this 绑定到子类上
function Parent(name) {
this.name = name;
this.colors = ["red", "blue"];
}
function Child(name) {
Parent.call(this, name); // 继承
}
const child1 = new Child("Child1");
child1.colors.push("green");
console.log(child1.name); // "Child1"
console.log(child1.colors); // ["red", "blue", "green"]
const child2 = new Child("Child2");
console.log(child2.colors); // ["red", "blue"] ✅ 不受 child1 影响
无法继承父类的原型方法(如 Parent.prototype.getName)。
每个子类都有自己的父类副本,导致占用内存增加。
- 使用 class 语法糖,使用 extends 关键字继承父类。
语法简洁,比传统方式更易读。
支持 super 调用父类方法。
更符合面向对象编程。
18. 面向对象的三大特性
封装:把数据(属性)和行为(方法)封装到对象中,隐藏内部实现,只暴露必要的接口。
继承:子类(派生类)继承父类(基类)的属性和方法,JavaScript 使用 extends 关键字实现继承,子类可以使用 super() 调用父类的构造函数。
多态:相同的方法在不同类中具有不同的实现,使得调用同一方法时,表现出不同的行为。
19. async / await
async 关键字用于定义异步函数,返回 Promise
await 关键字暂停执行,等待 Promise 解析(只能在 async 函数内部使用)
四、浏览器
1. 浏览器渲染
解析→ 生成渲染树 → 布局 →绘制→复合
浏览器解析入口一定是一个html页面,html也是解析流程的入口。css和js相关文件或内容嵌套在html的多个地方,当解析到它们时,会立即停止html解析,进入css或者js的解析流程,解析完成之后再回到html解析流程。解析完成html文件最后一个标签,整个解析即结束。
html解析:解析html文本,生成html元素节点,识别html嵌套结构并构建DOM树
css解析:加载css资源,解析css文本,构建cssom树,样式规则计算
JS解析:加载js资源,解析js文本,构建结构树,解析优化代码
解析HTML:将字符串解析成DOM树和CSSOM树
样式计算:得到Computed Style
布局:产生布局树
分层:划分图层
绘制:产生绘制指令集
分块:划分区域
光栅化:生成位图
画:生成quad,提交硬件,完成成像
浏览器渲染
2. 回流重绘
优化:
- 让需要修改集合属性的容器先脱离文档流不显示,修改完再回到文档流中。
首先在开头给ul添加上none属性后,ul的HTML结构就不在渲染树中,即使是循环一万次,也不会触发回流。最后通过添加ul.style.display = ‘block’,将ul显示出来 - 借助文档碎片
通过 let frg = document.createDocumentFragment() 创建一个文档碎片。它的作用是会创建一个虚拟的标签,在浏览器中不被当成真实的标签来使用。
let frg = document.createDocumentFragment();
for(let i = 0; i < 10000; i++){
let li = document.createElement('li');
let text = document.createTextNode(i);
li.appendChild(text);
frg.appendChild(li); // 往虚拟标签中添加li
}
ul.appendChild(frg);
- 克隆体替换子节点
首先通过let clone = ul.cloneNode(true)克隆一份ul,于是循环中就可以往克隆体中添加ul,最后用克隆体替换原来的节点,这样也只会回流一次。
let clone = ul.cloneNode(true);
for(let i = 0; i < 10000; i++){
let li = document.createElement('li');
let text = document.createTextNode(i);
li.appendChild(text);
clone.appendChild(li); // 往克隆体中添加ul
}
ul.parentNode.replaceChild(clone, ul)
3. 优化性能及性能指标
常见的优化性能的手段:
- 优化图片:对图片进行压缩、使用合适的格式和尺寸以及图片懒加载技术。
- 使用CDN(内容分发网络):将静态资源分发到全球各地的服务器,减少用户加载资源的延迟。
- 缓存策略:利用浏览器缓存策略,如Cache-Control 和ETag,减少重复请求。
- 代码压缩和优化:通过压缩和移除不必要的代码,减少文件大小。同时优化代码逻辑,提高代码的利用率。
- 异步加载:使用异步加载策略(如async和await)减少阻塞渲染的脚本。vue3中有defineAsyncComponent API用于实现异步加载组件。
- 利用浏览器渲染优化:避免强制同步布局,减少重排重绘。
- 优化css选择器
- 代码分割和按需加载:降低首次页面的加载时间。
- 使用WebWorkers: 使用WebWorkers进行后台处理,避免阻塞主线程。
- 服务器渲染和预渲染
- 优化字体加载:减少字体文字大小,使用字体加载策略避免阻塞渲染。
- 优化资源优先级:利用 < link rel=“preload”> 和 < link rel = “prefetch”>标签,优化资源加载顺序。
preload:告诉浏览器这个css资源在当前页面必须使用,应该立即加载和执行。浏览器会在HTML文档下载和解析完成之前加载这个资源。
prefetch:告诉浏览器这个css资源可能在未来的某个时间点用到,但不需要立即加载。浏览器会在空闲时间内异步加载这个资源,以便在需要时可以立即使用。
- 使用事件委托,利用冒泡机制处理事件
- 使用节流、防抖处理频繁触发的事件。
常见的性能指标:
- 首次绘制:页面再用户设备上开始渲染的时间点。
- 首次内容绘制:页面上的任何内容(如图片、文字等)首次渲染的时间点。这个指标可以衡量用户看到内容的速度。
- 首次有效绘制:页面上主要内容呈现给用户的时间点。这个指标可以衡量页面的是视觉完整度。
- 首次输入延迟:用户首次与页面交互(点击输入等)所需的时间。这个指标用来衡量页面的交互性。
- 速度指数:描述页面加载过程中视觉体验的一个指标。速度指数越低,用户体验越好。
- 大致加载时间:从用户发出请求到接收到服务器相应的第一个字节所需要的时间。这个指标可以用来衡量网络延迟和服务器处理速度。
- 页面完全加载时间:从用户发出请求到页面完全加载所需要的时间。这个指标可以用来衡量页面加载速度的综合体验。
- 页面体积:页面所有资源的总大小。页面越小,加载越快。
- 请求次数:加载页面所需的网络请求次数。
- 首次CPU空闲时间:页面首次达到CPU空闲状态的时间点,这个指标可以用来衡量页面在何时可以响应用户的输入。
- 最大潜在首次输入延迟:一个预测性指标,衡量在页面首次可交互之前可能发生的最大输入延迟。
- 累计布局偏移:页面在加载过程中元素位置变化的总和。这个指标用来衡量页面的视觉稳定性。
怎样做性能监控?
- 利用浏览器提供的性能API:Navigation Timing API、User Timing API、 Resourse Timing API等
- 使用PerformanceObserver API监控性能数据变化,实时收集性能指标。
- 监控页面的错误信息,包括js的错误信息以及资源加载失败信息。可以使用window.onerror和window.addEventListener(‘error’)捕获。
- 监控用户的交互事件。
- 使用Long Tasks API来检测可能导致页面卡顿的长时间任务。
4. 浏览器拿到url之后的步骤
- 解析url
- DNS解析
- 封装http请求数据包
- 封装TCP请求数据包
- 进行TCP三次握手
- 参数从客户端发到服务器
- 服务器得到参数后,进行相应的业务并将结果封装为Http包返回给客户端
- 进行tcp四次挥手
- 浏览器通过自身执行引擎,渲染并展示最终结果给用户。
5. http的状态码
- 1xx:信息性状态码
100 :客户端应继续请求。 - 2xx:成功状态码
201:请求成功。
202:请求已经实现并且创建了一个新资源。
203:请求已经实现但没有返回任何内容。 - 3xx:重定向状态码
301:请求的资源已被永远挪到新的url上。
302:请求的资源临时从不同的url上响应。
304:客户端已执行get请求,文件未发生变化。 - 4xx:客户端错误状态码
400:无法理解。
401:请求要求进行身份验证。
403:服务器理解请求但是拒绝执行。
404:没找到请求资源。
405:请求中的指定方法不被允许。 - 5xx:服务器错误状态码
500:服务器遇到了一个未曾预料的错误,导致其无法完成请求。
501:不支持当前请求所需要的某个功能。
503:由于临时服务器维护或过载,服务器当前无法处理请求。
6. 跨域及其解决办法
跨域主要是因为浏览器的同源策略限制。同源是指两个页面拥有相同的协议(http/https)、主机(域名)和端口。
解决办法:
- CORS:通过服务器设置相应的HTTP头部信息,来允许跨域请求。
- 代理服务器:搭建一个代理服务器来转发请求,是前端可以通过代理服务器来间接访问不同源的资源。
- window.postMessage:是html5新引入的API,允许不同源的脚本之间进行通信。通过监听window对象中的‘message’事件,接受其他窗口发送过来的信息。
- 设置document.domain:如果两个页面属于同一个顶级域名下的不同子域名,可以通过设置document.domain为相同的顶域名来实现跨域。
- JSONP:利用 </script/> 标签不受同源策略限制的特性,通过动态插入标签请求不同源的资源。
7. 内存泄漏
内存泄漏是指当程序不再需要某些对象时,没有释放这些对象所占用的内存,而导致内存占用持续增加,最终可能导致系统性能下降甚至崩溃。
常见的内存泄漏的原因及解决办法:
- 意外的全局变量:当某个变量没有用var/let/const声明时候,js就会将其挂载到全局变量上,导致这个变量在整个程序生命周期内都被保存。
- 谨慎管理全局变量,确保所有变量都是用let/const/var声明。
- 闭包的未释放引用:当一个函数保持对一个外部变量的引用时,这些变量在函数执行之后仍然被保存在内存中,即便这些变量不再需要。
function outer(){
let a = '...(一个很大的对象)';
return function inner(){
console.log(a);
}
}
const run = outer();
//此时run不再使用,但是a也会保存在内存中。
- 仅在需要时使用闭包,并确保在不再需要闭包时,解除对外部变量的引用。避免长时间持有不必要的对象引用。
const run = outer();
run(); // 正确释放 memory
- 被遗忘的监听器和回调:setTimeout和setInterval等计时器函数,如果没有在不使用时及时处理,将会导致函数一直保存在内存中。同样监听器没有及时移除也会导致内存泄漏。
setTimeout本质是延迟执行,只执行一次,而setInterval本质是定时执行,会照指定的时间间隔重复执行函数
setTimeout应用场景:动画效果的延迟,按钮防抖;setInterval应用场景:”轮播图的切换、时钟的更新等
const upload = setInterval(()=>{
console.log('upload');
},1000);
// 如果不再需要时没有 clearInterval(intervalId),intervalId 仍然保留在内存中
- 不需要用监听器计数器时要及时清除。
clearInteval(upload);
clearTimeout(timeoutId);
button.removeEventListener('click',clickfun);
- DOM引用:当js对象引用了一个已经从DOM中移除的元素,该元素也会一直保存在内存里。
const element = document.querySelector('#bnt1');
element.addEventListener('click',()=> {
// body...
});
//即使从DOM中移除element,其引用也会一直保存在内存中。
- 当移除dom元素的时候,确保没有任何js对象引用他们。如果使用框架(如react或vue)框架会自动处理这种情况,但手动操作DOM元素时需要特别注意。
// 移除 DOM 元素时清理引用
function removeElement() {
element.removeEventListener('click', () => {
console.log('Button clicked');
});
element.parentNode.removeChild(element);
}
- Map 和 Set 的不当使用:Map和Set会强引用其所存储的键和值,意味着不手动删除不需要的元素他们就不会被垃圾回收。
const map = new Map();
let obj = { key: 'value' };
map.set(obj, 'some data');
// 即使 obj 不再使用,map 仍然保留 obj 的引用,导致内存泄漏
obj = null; // 应该使用 map.delete(obj) 以确保 obj 可以被垃圾回收
- 使用WeakMap和WeakSet来存储对象引用,以便在对象不再被使用时自动释放它们的内存。
const weakMap = new WeakMap();
(function() {
let obj = { key: 'value' };
weakMap.set(obj, 'some data');
// 当 obj 没有其他引用时,垃圾回收机制将自动释放它的内存
})();
// 在这种情况下,obj 不再存在于内存中,WeakMap 中的引用也会被垃圾回收
console.log(weakMap.has(obj)); // false,因为 obj 已被垃圾回收
- 循环引用:两个或多个对象相互引用时,并且没有任何引用指向它们之中的任何一个时,它们将无法被垃圾回收。
// 内存泄漏示例
function createObjects() {
const obj1 = {};
const obj2 = {};
obj1.ref = obj2;
obj2.ref = obj1;
}
createObjects();
- 避免循环引用,或者在不再需要这些引用时手动解除它们。如果必须有循环引用,考虑使用WeakMap或WeakSet来存储这些引用。
// 解决内存泄漏
function createObjects() {
const obj1 = {};
const obj2 = {};
obj1.ref = obj2;
obj2.ref = obj1;
// 不再需要 obj1 和 obj2 的引用时,将它们设为 null
obj1.ref = null;
obj2.ref = null;
}
createObjects();
// 或者使用 WeakMap 以避免内存泄漏
function createCyclicDependency() {
const a = {};
const b = { a };
a.b = b;
// 解除循环引用
delete a.b;
// 或者使用 WeakMap 以避免内存泄漏
const weakMap = new WeakMap();
weakMap.set(a, b);
}
createCyclicDependency();
- 及时清理大对象和数组:当大对象和数组不再需要时,显式地将它们设置为null,以便垃圾回收器回收它们。
let largeArray = new Array(1000000).fill('some data');
// 当 largeArray 不再需要时,显式地设置为 null
largeArray = null;
// 同样适用于大对象
let largeObject = { key1: 'value1', key2: 'value2', /* ... many more keys ... */ };
largeObject = null;
排查和预防内存泄漏:
- 使用浏览器开发工具
- 垃圾回收
- 使用现代框架和库
8. 垃圾回收机制
-
引用计数:通过跟踪每个对象引用的次数来确定对象是否为垃圾。每个对象都有一个引用计数器,当一个对象被创建时,引用计数器初始化为1,当该对象被其他对象引用的时候,引用计数器+1,不再被其他对象引用的时候,计数器-1,当引用计数器减为0,就意味着该对象不再被引用,可以被垃圾收集器回收。
-
标记-清除:通过标记不再使用的对象,然后清除这些对象的内存空间,以便后续的内存分配引用。**标记阶段:**垃圾回收器会对内存中的所有对象进行遍历,从根对象开始递归地遍历对象的引用关系。对于每个被访问的对象,垃圾回收器会给它打上标记,表示该对象是可达的。这个过程确保了所有可达对象都会被标记。**清除阶段:**垃圾回收器会遍历整个内存,对于没有标记的对象,就被认为是垃圾对象,会立即收回,释放空间。
可以处理循环引用
- 存在的问题:
- 垃圾回收过程中的停顿:标记-清除算法会暂停程序的执行,进行垃圾回收操作。当堆中的对象较多时,就会导致明显的停顿,影响用户体验。
- 内存碎片化: 在回收过程中会产生大量的、不连续的内存空间。这可能导致后续的内存分配难以找到足够大的连续内存块。
- 存在的问题:
-
标记-整理:分为三个阶段:标记阶段、整理阶段和清除阶段。整理阶段:将内存中的活动对象移到一端使空闲空间连续,并且没有碎片化。
-
V8垃圾回收机制:分代收集和增量标记。新生代使用Scavenge算法,通常是采用Cheney算法,将内存分为两个半区,From区和To区,每次垃圾回收时,会将存活的对象复制到To区,并且清空From区,老生代使用标记整理算法。
9. 浏览器缓存
- 强制缓存:如果浏览器判断请求的资源有效命中强缓存,就可以直接从内存中读取目标资源,无需与服务器进行通讯。
- expires:设置一个强缓存时间,在此时间内都从内存中进行读取。机制:获取本地的时间戳与资源文件内的Expires字段的时间做对比。
缺点:如果本地的时间不准,则会出现资源无法缓存/资源永远被缓存的情况。
- cache-control:有六个属性
- max-age:决定客户端资源被缓存多久。(单位 秒)
- s-maxage:代理服务器缓存的时长。
- no- cache:强制进行协商缓存
- no-store:禁止任何缓存策略
- public:资源可以被浏览器缓存也可以被代理服务器缓存。
- private:资源只能被浏览器缓存
如果private和public都没有设置,则默认是private
- expires:设置一个强缓存时间,在此时间内都从内存中进行读取。机制:获取本地的时间戳与资源文件内的Expires字段的时间做对比。
- 协商缓存 :
-
last-modified:
- 实现步骤:
- 服务器:
-
- 从服务器中读出文件修改的时间
-
- 将读出的时间赋给响应头的last-modified字段
-
- cache-control:no-cache。
缺一不可
- cache-control:no-cache。
- 客户端:客户端读取到last-modified的时候,会在下一次的请求头中携带If-modified-since(就是服务器第一次修改时候给的时间),服务器拿到这个时间就会跟资源的修改时间进行对比决定是读取缓存还是返回新的资源。
缺点:- 如果文件本身没有修改,依然可能出现文件修改时间改变(比如更改了文件名但是又改了回来),这样可能缓存就失效了。
- 如果文件修改的速度极快(比如几百毫秒)文件修改时间最小的单位是秒,就会出现内容修改了,但是修改时间依旧没变,依旧不会返回新的文件。
-
ETag:从比较时间戳变成了文件指纹。
文件指纹:根据文件内容计算的唯一哈希值,文件内容一旦改变,指纹就会改变。
- 实现步骤:
-
- 第一次请求资源时,服务器读取文件并计算文件指纹,然后将文件指纹放在响应头的Etag字段中跟资源一起进行返回。
-
- 第二次请求资源时,客户端从缓存中读取上一次的ETag,并附给请求头If-None-Match字段。
-
- 服务器拿到If-None-Match字段,读取目标资源生成文件指纹,两个指纹进行对比,如果没有发生改变,就返回304状态码和一个空的响应体并return。如果两个文件指纹不吻合,就将新的文件指纹重新存储到响应头ETag中并返回客户端。
缺点:1. 文件指纹意味着服务器需要更多的开销,如果文件尺寸大,数量多,计算频繁就会影响服务器性能。
2. ETag也有强验证和弱验证,强验证就是哪怕文件的一个字节改变,指纹就会改变,强验证非常消耗计算量。弱验证就是提取文件的部分属性生成哈希值,不必精确到每个字节。速度快但是准确率不高,会降低协商缓存的有效性。
- 服务器拿到If-None-Match字段,读取目标资源生成文件指纹,两个指纹进行对比,如果没有发生改变,就返回304状态码和一个空的响应体并return。如果两个文件指纹不吻合,就将新的文件指纹重新存储到响应头ETag中并返回客户端。
-
总结:
强制缓存有cache-control和expires两种模式,cache-control是通过设置缓存持续时间来进行判断的,expires是通过设置时间戳来进行判断的。两者相斥,只能选择一个设置。能用cache-control不用expires
协商缓存有两种模式:last-modified和ETag,last-modified是通过设置文件的修改时间来进行判断,ETag是通过设置文件指纹进行判断的。两者没有谁好谁坏,根据业务场景选择。要设置cache-control:no-cache
返回304的都是协商缓存。
五、设计模式
设计模式是针对软件设计中反复出现的问题所提出的通用解决方案。旨在提高代码的可复用性、可扩展性和可维护性。
分为三大类:
- 创建型模式:关注对象的创建过程:工厂模式、单例模式、原型模式、和创造者模式。
- 结构模式:关注类和对象的组合:适配器模式、桥接模式、装饰器模式、组合模式、外观模式、享元模式和代理模式。
- 行为模式:关注对象间的通信:责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。
1. 工厂模式
核心思想:是将对象的创建过程封装起来,让客户端不直接与具体的类进行交互,而是通过统一的接口来创建对象。
分为几种类型:
- 简单工厂模式:一个工厂类根据传入的参数创建对应的对象。缺点:当添加新的产品时,需要修改工厂类的代码,违反了开放封闭原则。
- 工厂方法模式:定义一个工厂接口,让各个具体工厂类实现这个接口,负责创建对应的产品。
- 抽象工厂模式:提供一个创建一系列相关或者相互依赖对象的结构,而无需指定他们的类。
2. 单例模式
核心思想:确保一个类只有一个实例,并且提供一个全局访问点来获取该实例。
六、VUE
1. vue2与3的区别
1.vue2是选项式API,Vue3是组合式API,能够更加灵活的服用和组合组件逻辑。
2.响应式系统的提升:使用Proxy代替Object.defineProperty,可监听新增删除属性以及数组的变化。
3.生命周期:用setup代替了vue2中的beforeCreate和Create生命周期。
4. diff算法重写:vue2通过标记静态根节点优化diff,Vue3 标记和提升所有静态根节点,diff的时候只需要对比动态节点内容。
5. vue3中支持多个template根节点
6. vuex,vue2创建实例使用new Store,vue3中使用createStore。
7. Route获取页面实例和路由信息:vue2用的是this获取route实例,vue3用useRoute和useRouter方法获取当前组件实例。
2. vue中prop的验证类型
在 Vue 中,props 是父组件向子组件传递数据的一种方式。为确保传递的数据符合预期,Vue 提供了 props 的类型验证。在定义 props 时,可以指定其类型、是否必填、默认值等,来帮助开发者在调试时识别潜在的错误。
String
Number
Boolean
Array
Object
Function
Symbol
3. vue组件通信
- 父传子:绑定属性和defineprops
- 子传父:绑定事件和emit
- vuex或pinia
- provide和inject(跨层)
- eventBus
4. 父子组件生命周期
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
5. 数据请求一般放在哪些生命周期钩子里,这些钩子之间有什么区别
created:实例创建完成之后可以进行发送请求。适用于不依赖DOM的数据请求。
mounted:dom挂载之后发送请求,适用于需要使用DOM元素数据的请求或者组件在页面渲染后加载的数据。
6. Vue Router
前端单页路由
客户端路由的作用是在单页应用 (SPA) 中将浏览器的 URL 和用户看到的内容绑定起来。当用户在应用中浏览不同页面时,URL 会随之更新,但页面不需要从服务器重新加载。
RouterLink 和 RouterView
- 使用组件 RouterLink 来创建链接。这使得 Vue Router 能够在不重新加载页面的情况下改变 URL,处理 URL 的生成、编码和其他功能。
- RouterView 组件可以使 Vue Router 知道你想要在哪里渲染当前 URL 路径对应的路由组件。
vue3中使用creatRouter()进行创建。通过 useRouter() 和 useRoute() 来访问路由器实例和当前路由。
vue2中使用new Router进行创建。通过this.$router 和 this.$route获取。
不同的导航方式
- router.push(…):这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。
当你点击 <router-link> 时,内部会调用这个方法,所以点击 相当于调用 router.push(…) - router.replace(…):它在导航时不会向 history 添加新记录。
- router.go(…):该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步,类似于 window.history.go(n)。
pushState是压入浏览器的会话历史栈中,会使得history.length加1,而replaceState是替换当前的这条会话历史,因此不会增加history.length
-
怎么获取路由传参?
– 动态参数:类似/user/:123 这种,用useRoute获取实例,然后用router.params获取(vue2中用this.$route.params)
– 查询参数:类似/user ? id= 123,用useRoute获取实例,然后用router.query获取(vue2中用this.$route.query) -
钩子函数
- router.beforeEach
- router.beforeResolve:解析守卫刚好会在导航被确认之前、所有组件内守卫和异步路由组件被解析之后调用
- router.afterEach
- router.beforeEnter
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫(2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
7. ref和reactive
ref和reactive
8. vue-router
RouterLink 是 Vue Router 提供的组件,用于在 Vue 应用中实现声明式的客户端路由跳转。它的作用类似于 <a> 标签,但不会触发页面刷新,而是使用 Vue Router 进行**单页面应用(SPA)**内的导航。
<template>
<RouterLink to="/home">跳转到首页</RouterLink>
</template>
//等价于
<a href="/home">跳转到首页</a>
主要 Props:
- to:指定跳转路径:
//字符串
<RouterLink to="/about">关于我们</RouterLink>
//对象
<RouterLink :to="{ path: '/about' }">关于我们</RouterLink>
//带query参数:访问的 URL 变成 product?id=123
<RouterLink :to="{ path: '/product', query: { id: 123 } }">商品详情</RouterLink>
- replace:使用 replaceState 而不是 pushState(如果希望不留下历史记录,可以使用 replace)
<RouterLink to="/home" replace>跳转到首页(不记录历史)</RouterLink>
默认是 push,点击后可以用浏览器返回键回到上一个页面。
replace 方式不会在浏览历史中留下记录,点击后无法后退.
- active-class & exact-active-class:修改选中状态的样式
<RouterLink to="/profile" active-class="my-active-class">个人中心</RouterLink>
与 router.push 的区别
RouterLink 适用于模板中的导航链接
router.push 适用于 JavaScript 代码中动态跳转
this.$router.push('/about');
9. v-if和v-for最好不要一起用
vue2 中v-for的优先级高于v-if,会优先渲染全部数据然后再判断v-if的条件,造成了性能的浪费。
vue3中v-if的优先级高于v-for,会优先进行v-if 的条件判断,然后再进行v-for渲染。如果v-if有依赖v-for的变量,就会发生报错。
10. vue的内置组件
Transition:会在一个元素或组件进入和离开 DOM 时应用动画。
TransitionGroup:用于对 v-for 列表中的元素或组件的插入、移除和顺序改变添加动画效果。
keepAlive:它的功能是在多个组件间动态切换时缓存被移除的组件实例。
Teleport:它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
Suspense:用来在组件树中协调对异步依赖的处理。
keepAlive:官方文档
可使用场景:
<script setup>
import Home from './Home.vue'
import Posts from './Posts.vue'
import Archive from './Archive.vue'
import { ref } from 'vue'
const currentTab = ref('Home')
const tabs = {
Home,
Posts,
Archive
}
</script>
<template>
<div class="demo">
<button
v-for="(_, tab) in tabs"
:key="tab"
:class="['tab-button', { active: currentTab === tab }]"
@click="currentTab = tab"
>
{{ tab }}
</button>
<component :is="tabs[currentTab]" class="tab"></component>
</div>
</template>
效果地址:
效果地址
<KeepAlive> 默认会缓存内部的所有组件实例,我们可以通过includes和exclude prop来定制该行为。
max 来限制可被缓存的最大组件实例数。
生命周期:OnActivated()和OnDeactivated()注册相应的两个状态生命周期钩子。
onActivated 在组件挂载时也会调用,并且 onDeactivated 在组件卸载时也会调用。
这两个钩子不仅适用于 <KeepAlive> 缓存的根组件,也适用于缓存树中的后代组件。
发布者:admin,转转请注明出处:http://www.yc00.com/web/1743906569a4527600.html
评论列表(0条)