货拉拉前端面试总结
货拉拉 前端开发 提前批一面面经
校招面试大多都是基础题目,大约一小时
面试题目
1、数组的方法
2、扁平化和数组去重
3、foreach和map的区别
4、类型判断的方法
5、 基本类型和引用数据类型的区别
6、深拷贝方法,如何实现
7、什么时候用JSON.stringify()
8、如何实现递归,数组扁平化中的递归的缺陷
9、 this指向
10、== 和 === 的区别
11、原型、原型链
12、创建对象方法
13、promise,async、await
14、模块化CommonJS、AMD、CMD、UMD、ES6 Module有什么区别
15、什么是事件循环?
16、宏任务微任务?
17、 隐藏元素的方法
18、BFC,实现方法
19、get、post区别
20、 状态码 301,401,501
21、浏览器缓存机制,强缓存、协商缓存区别
22、 同源策略,跨域解决方法
23、 React中 useState() 是同步还是异步
24、 Vue生命周期
25、 v-if和v-show
26、 v-for为什么要有key
面试详解
1、数组的方法
- push():在数组的末尾添加一个或多个元素,返回新数组的长度。
- pop():删除数组末尾的元素,并返回该元素的值。
- shift():删除数组第一个元素,并返回该元素的值,数组的长度减 1。
- unshift():在数组的开头添加一个或多个元素,返回新数组的长度。
- concat():连接两个或更多的数组,并返回新的数组。
- slice():从数组中选取一个子集,返回一个新数组。
- splice():删除或替换数组中的元素,返回被删除元素的数组。
- sort():对数组进行排序。
- reverse():颠倒数组中元素的顺序。
- forEach():遍历数组中的每个元素,没有返回值。
- map():将数组中的每个元素应用于给定函数,返回一个新的数组。
- filter():将数组中的每个元素传递给给定函数,并返回一个新数组,其中包含所有计算结果为真的元素。
- reduce():将数组中的每个元素累加到一个累加器中,返回一个累加器的值。
修改原数组的方法:这些方法会直接修改原始数组,包括 push()、pop()、shift()、unshift()、splice()、sort()、reverse()。
不修改原数组的方法:这些方法会返回一个新的数组或其他数据类型,而不会修改原数组,包括 concat()、slice()、join()、toString()、indexOf()、lastIndexOf()、includes()、some()、every()、filter()、map()、reduce()、reduceRight()、find()、findIndex()、flat()等。
2、扁平化和数组去重
数组扁平化可以使用 flat
方法
flat(Infinity)方法用于将多维数组转换为一维数组。如果不知道嵌套层级,可以使用Infinity作为参数。
const arrayToFlatten = [1, 2, [3, 4], [5, [6, 7]]];
const flattenedArray = arrayToFlatten.flat(Infinity);
console.log(flattenedArray);
或者使用递归实现数组扁平化:
function flattenArray(arr) {
let flattened = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
flattened = flattened.concat(flattenArray(arr[i]));
} else {
flattened.push(arr[i]);
}
}
return flattened;
}
const nestedArray = [1, 2, [3, 4], [5, [6, 7]]];
const flattenedArray = flattenArray(nestedArray);
console.log(flattenedArray);
数组去重是指从数组中删除重复的元素:
const arrayToDeduplicate = [1, 2, 2, 3, 4, 4, 5];
const deduplicatedArray = Array.from(new Set(arrayToDeduplicate));
console.log(deduplicatedArray);
通过new Set()构造函数创建一个集合,利用集合的特性自动去除重复元素,然后使用Array.from()方法将集合转换回数组。
3、foreach和map的区别
forEach
forEach是用于遍历数组的方法,它接受一个回调函数作为参数,并对数组中的每个元素执行该回调函数。forEach方法不会返回新的数组,而是对原数组进行操作或执行一些操作,比如打印元素、修改元素等。
const array = [1, 2, 3];
array.forEach((element) => {
console.log(element);
});
map
map方法也是用于遍历数组的方法,但它会返回一个新的数组,新数组的每个元素由原数组经过回调函数处理得到。
const array = [1, 2, 3];
const doubledArray = array.map((element) => {
return element * 2;
});
console.log(doubledArray); // 输出: [2, 4, 6]
- forEach只用于遍历数组,不返回新数组,主要用于对数组进行操作。
- map用于遍历数组并返回新数组,新数组的元素是通过回调函数处理得到的。
4、类型判断的方法
typeof
用于检测给定值的数据类型,并返回一个字符串表示该类型:
typeof 42; // 返回 "number"
typeof "hello"; // 返回 "string"
typeof true; // 返回 "boolean"
typeof []; // 返回 "object"
typeof {}; // 返回 "object"
typeof null; // 返回 "object"
typeof undefined; // 返回 "undefined"
typeof function() {}; // 返回 "function"
对于原始数据类型,我们可以使用typeof()函数来判断他的数据类型
typeof并不总是能够准确地区分对象类型,对于数组和null而言,它会返回"object"。为了更准确地判断是否为数组,可以使用Array.isArray()方法。
typeof 运算符对于 null 值会返回 "object"。这实际上是 JavaScript 最初实现中的一个错误,然后被 ECMAScript 沿用了。现在,null 被认为是对象的占位符,从而解释了这一矛盾,但从技术上来说,它仍然是原始值。
instanceof
instanceof 用来判断一个变量是否是某个对象的实例,所以对于引用类型我们使用instanceof来进行类型判断
const arr = [];
arr instanceof Array; // 返回 true
const obj = {};
obj instanceof Object; // 返回 true
const date = new Date();
date instanceof Date; // 返回 true
Object.prototype.toString.call()
Object.prototype.toString.call() 方法:用于返回对象的字符串表示,可以用来判断对象的类型
Object.prototype.toString.call(42); // 返回 "[object Number]"
Object.prototype.toString.call("hello"); // 返回 "[object String]"
Object.prototype.toString.call(true); // 返回 "[object Boolean]"
Object.prototype.toString.call([]); // 返回 "[object Array]"
Object.prototype.toString.call({}); // 返回 "[object Object]"
Object.prototype.toString.call(null); // 返回 "[object Null]"
Object.prototype.toString.call(undefined); // 返回 "[object Undefined]"
Object.prototype.toString.call(function() {}); // 返回 "[object Function]"
isNaN()
isNaN() 函数:用于检测一个值是否是 NaN。注意,它会先尝试将参数转换为数字类型,然后再进行判断
isNaN(NaN); // 返回 true
isNaN(42); // 返回 false
isNaN("hello"); // 返回 true
5、 基本类型和引用数据类型的区别
基本类型(也称为原始类型)包括以下几种:
- 数字(number)
- 字符串(string)
- 布尔值(boolean)
- null:表示一个空值。
- undefined:表示一个未定义的值。
- 符号(symbol):表示唯一的标识符,通常用于对象属性的键。
基本类型的特点:
- 存储在栈(stack)中,占据固定的内存空间。
- 通过复制来传递值,每个变量都有自己的副本。
- 比较值时比较的是它们的实际值。
引用类型的特点:
- 存储在堆(heap)中,内存空间可以动态分配。
- 存储的是指向对象的引用,而不是对象本身。
- 通过引用来传递值,多个变量可能引用同一个对象。
- 比较值时比较的是它们的引用是否指向同一个对象。
基本类型是存储简单数据值的数据类型,而引用类型是存储对象或复杂数据结构的数据类型。对于引用类型,可以通过原型继承来共享属性和方法,而基本类型则没有原型。
6、深拷贝方法,如何实现
浅拷贝:将对象的引用复制给一个新对象,新对象和原对象引用的是同一个对象,修改一个对象的属性会影响另一个对象的属性。常见的浅拷贝方法有Object.assign()、扩展运算符(...)等。
深拷贝:将对象完全复制一份,新对象和原对象是两个独立的对象,修改一个对象的属性不会影响另一个对象的属性。常见的深拷贝方法有递归拷贝、JSON.stringify和JSON.parse
function deepCopy(source) {
if (typeof source !== 'object' || source === null) {
return source;
}
const target = Array.isArray(source) ? [] : {};
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = deepCopy(source[key]);
}
}
return target;
}
7、什么时候用JSON.stringify()
JSON.stringify() 方法用于将 JavaScript 对象转换为 JSON 字符串
- 数据传输:当你需要通过网络发送数据或者在不同系统之间交换数据时,你通常会将 JavaScript 对象转换为 JSON 字符串,以便于传输。
- 本地存储:当你需要将数据存储在本地(例如浏览器的 localStorage 或 sessionStorage)时,通常会将 JavaScript 对象转换为 JSON 字符串进行存储。
- 深拷贝对象:在需要对对象进行深拷贝(复制对象及其所有嵌套属性)时,可以使用 JSON.stringify() 和 JSON.parse() 结合起来实现深拷贝。
JSON.stringify() 只能处理那些可以被有效转换为 JSON 格式的值,例如字符串、数字、布尔值、数组、对象字面量、以及可以被转换为这些类型的值。对于函数、正则表达式、循环引用等特殊情况,JSON.stringify() 会进行相应的处理(例如忽略函数和正则表达式,或抛出错误)。
8、如何实现递归,数组扁平化中的递归的缺陷
数组扁平化中使用递归的一个潜在缺陷是可能会导致栈溢出。当处理非常大的嵌套数组时,递归调用会不断增加调用栈的深度,最终可能导致栈溢出错误。
使用递归的扁平化方法也可能会影响性能,因为递归调用本身会产生一定的开销,尤其是在处理大型数组时。
为了解决这些问题,可以考虑使用迭代(iteration)而不是递归来实现数组扁平化:
function flattenArray(arr) {
const flattened = [];
const stack = [...arr];
while (stack.length) {
const next = stack.pop();
if (Array.isArray(next)) {
stack.push(...next);
} else {
flattened.unshift(next);
}
}
return flattened;
}
const nestedArray = [1, 2, [3, 4], [5, [6, 7]]];
const flattenedArray = flattenArray(nestedArray);
console.log(flattenedArray);
9、 this指向
全局上下文中
当在全局作用域中使用 this 时,它将指向全局对象,在浏览器中通常是 window 对象。
函数中
- 在普通函数中,this 的指向取决于函数的调用方式。
- 如果函数是作为普通函数调用(不作为对象的方法),this 将指向全局对象或者 undefined(在严格模式下)。
function sayHello() {
console.log(this);
}
sayHello(); // 在浏览器中,指向 window 对象
对象方法中
对象方法中,this 指向调用该方法的对象
const person = {
name: "Alice",
greet: function() {
console.log("Hello, my name is " + this.name);
}
};
person.greet(); // this 指向 person 对象
构造函数中
在构造函数中,this 指向通过 new 关键字创建的实例对象。
function Person(name) {
this.name = name;
}
const alice = new Person("Alice");
console.log(alice.name); // Alice
事件处理函数中
在事件处理函数中,this 通常指向触发事件的元素。
document.getElementById("myButton").addEventListener("click", function() {
console.log(this); // 指向触发点击事件的按钮元素
});
箭头函数不会改变 this 的指向,它会继承外层作用域的 this 值。因此,在箭头函数中,this 的指向由箭头函数定义时的外部环境决定,而不是调用时的情况。
10、== 和 === 的区别
== 和 === 都是用于比较两个值的运算符,它们之间的区别在于严格性和类型转换。
双等号 ==(相等运算符)
双等号用于比较两个值时会进行类型转换,如果类型不同,JavaScript 会尝试将它们转换为相同类型再进行比较。
- 如果两个操作数是相同类型,则直接进行比较。
- 如果一个操作数为 null,另一个为 undefined,则它们相等。
- 如果一个操作数是数字,另一个是字符串,则将字符串转换为数字再进行比较。
- 如果其中一个操作数为 true,另一个为 1,则它们相等;如果其中一个为 false,另一个为 0,则它们相等。
- 如果一个操作数为对象,另一个为字符串或数字,则将对象转换为原始值再进行比较。
三个等号 ===(严格相等运算符)
三个等号比较时不会进行类型转换,只有在类型相同且值也相等的情况下才会返回 true。
- 如果两个操作数的类型不同,则返回 false。
- 如果两个操作数为对象时,只有引用指向同一个对象时才会返回 true。
11、原型、原型链
每个对象都有一个原型(prototype),而原型本身也是一个对象。原型链(prototype chain)是一种机制,用于实现对象之间的继承关系。
原型(prototype)
原型:每个函数都有一个 prototype(原型) 属性,这个属性是一个指针,指向一个对象,而这个对象包含某种特定类型的所有实例共享的属性和方法。
原型链(prototype chain)
原型链:JavaScript 中所有的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链
当试图访问一个对象的属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 引擎会沿着原型链向上查找,直到找到对应的属性或方法,或者到达原型链的顶端(即 Object.prototype)为止。
12、创建对象方法
对象字面量
使用对象字面量可以直接创建一个对象,并为其添加属性和方法。
const person = {
name: "John",
age: 30,
sayHello: function() {
console.log("Hello, my name is " + this.name);
}
};
console.log(person.name); // 访问属性
person.sayHello(); // 调用方法
构造函数
使用构造函数可以创建一个对象模板,并通过 new 关键字实例化多个对象。
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log("Hello, my name is " + this.name);
};
}
const person1 = new Person("John", 30);
const person2 = new Person("Jane", 25);
console.log(person1.name); // 访问属性
person2.sayHello(); // 调用方法
Object.create()
使用 Object.create() 方法可以基于指定的原型创建一个新对象。
const personProto = {
sayHello: function() {
console.log("Hello, my name is " + this.name);
}
};
const person = Object.create(personProto);
person.name = "John";
console.log(person.name); // 访问属性
person.sayHello(); // 调用方法
Class
ES6 引入了类(class)语法,通过 class 关键字可以定义一个类,并使用 new 关键字实例化对象
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log("Hello, my name is " + this.name);
}
}
const person1 = new Person("John", 30);
const person2 = new Person("Jane", 25);
console.log(person1.name); // 访问属性
person2.sayHello(); // 调用方法
13、promise,async、await
Promise 是一种处理异步操作的方式,可以在异步操作完成后执行相应的操作。async 和 await 则是基于 Promise 的语法糖,让异步操作更加简单和易读。
Promise
使用 Promise 可以将一个异步操作封装成一个对象,通过 then() 方法注册回调函数,在异步操作完成后调用回调函数。
function asyncOperation() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Operation completed successfully!");
}, 1000);
});
}
asyncOperation().then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
async/await
async 和 await 是 ES7 引入的新特性,它们使得异步操作的代码更加简洁和易读。通过 async 关键字定义一个异步函数,该函数返回一个 Promise 对象。可以在异步函数中使用 await 关键字等待异步操作完成,然后执行下一步操作。
14、模块化CommonJS、AMD、CMD、UMD、ES6 Module有什么区别
模块化是指将程序划分为独立的模块,以便于代码的组织、复用和维护。在JavaScript中,有几种常见的模块化规范和标准,包括CommonJS、AMD、CMD、UMD和ES6 Module。它们之间有以下区别:
-
CommonJS:CommonJS是一种模块化规范,主要用于服务器端的JavaScript编程。它使用require()和module.exports语法来导入和导出模块。CommonJS模块是同步加载的,适用于在服务器环境下加载模块。
-
AMD:AMD(Asynchronous Module Definition)也是一种模块化规范,主要用于浏览器端的JavaScript编程。它通过定义模块和异步加载模块来解决浏览器中的模块加载问题。AMD规范中使用define()函数定义模块,使用require()函数异步加载模块。
-
CMD:CMD(Common Module Definition)是另一种浏览器端的模块化规范,与AMD相似,但更加懒加载。CMD规范中使用define()函数定义模块,使用require()函数异步加载模块。
-
UMD:UMD(Universal Module Definition)是一种兼容多种模块化规范的通用模块化解决方案。UMD模块可以在不同的环境中使用,包括浏览器和服务器端。
-
ES6 Module:ES6 Module是ES6(ECMAScript 2015)引入的官方模块化标准。ES6模块使用import和export语法来导入和导出模块。ES6模块可以静态解析依赖关系,使得编译器和工具可以进行更好的优化。
CommonJS主要用于服务器端,AMD和CMD主要用于浏览器端,UMD可以在不同的环境中使用,而ES6 Module是未来JavaScript开发的趋势,也可以通过工具转换成其他规范使用
15、什么是事件循环?
事件循环(Event Loop)是JavaScript中执行异步代码的一种机制。JavaScript是单线程的,同一时间只能执行一个任务,如果执行的任务需要等待一段时间才能完成,那么就会导致程序阻塞,影响用户体验。为了解决这个问题,JavaScript引入了异步编程模型。
在JavaScript中,异步操作可以通过回调函数、Promise、Async/Await等方式来实现。而事件循环则是异步编程模型的基础,它的作用在于从任务队列中取出任务,并将其放到主线程上执行。
事件循环的执行过程如下:
- 执行同步代码,如果遇到异步任务(如setTimeout()、fetch()等),就将其挂起,放到任务队列中等待执行。
- 当主线程空闲时(即调用栈为空),事件循环会从任务队列中取出一个任务,然后执行该任务。
- 重复上述过程,直到任务队列为空。
16、宏任务微任务?
宏任务包括整体代码块、setTimeout()、setInterval()、setImmediate(Node.js环境)、I/O操作、UI交互事件等
而微任务包括Promise.then()、MutationObserver等。
当事件循环执行完一个宏任务时,会先执行所有的微任务,然后再取出下一个宏任务执行。这个过程中,如果又产生了新的微任务,那么会立即执行这些微任务,然后再执行下一个宏任务。
17、 隐藏元素的方法
-
CSS中设置display属性:display: none
-
CSS中visibility属性: visibility: hidden;该方法会使元素不可见,但仍占据页面空间。
-
CSS中opacity属性:opacity: 0;该方法会使元素完全透明,但仍然占据页面空间。
-
使用position属性:top: -9999px;该方法将元素移出屏幕范围,实现隐藏效果。
-
使用JavaScript动态修改样式:
document.getElementById("elementId").style.display = "none"
; 可以通过JavaScript来动态修改元素的样式,将display属性设置为"none"来隐藏元素。
18、BFC,实现方法
BFC(块级格式化上下文)它是页面中一个独立的渲染区域,拥有自己的布局规则。BFC具有一些特性,例如浮动元素不会覆盖BFC区域,BFC可以包含浮动元素,BFC可以阻止垂直外边距重叠等。
有几种方式可以触发一个元素的BFC,实现方法如下:
-
设置浮动(float):将元素设置为浮动,例如将其float属性设置为left或right,这会触发元素的BFC。
-
设置定位(position):将元素的position属性设置为absolute或fixed
-
设置overflow属性:将元素的overflow属性设置为非visible的值,如auto、hidden、scroll
-
设置display属性为inline-block、table-cell、table-caption或inline-flex
-
使用块级格式化上下文的根元素:HTML文档中的根元素(通常是html或body元素)本身就是一个BFC。
19、get、post区别
- 参数传递方式:
- GET:通过 URL 参数传递数据,参数会附加在 URL 后面,例如:https://www.example.com/page?name=value
- POST:通过请求体传递数据,在请求体中以键值对的形式传递参数,不会在 URL 中显示参数信息。
- 安全性:
- GET:由于参数附加在 URL 中,因此在浏览器的历史记录、代理服务器日志等地方都会留下记录,因此不适合传输敏感信息。
- POST:参数在请求体中传递,相对于 GET 更安全,适合传输敏感信息。
- 数据长度限制:
- GET:由于参数直接附加在 URL 中,URL 的长度会受到浏览器和服务器的限制,通常不能超过 2048 个字符。
- POST:参数在请求体中,因此可以传输大量数据,受到服务器配置的限制。
- 幂等性:
- GET:幂等,即多次请求相同的 URL 会产生相同的结果,不会对服务器产生影响。
- POST:非幂等,每次提交可能会产生不同的结果,对服务器产生影响。
- 缓存:
- GET:可以被浏览器缓存,可以被书签保存。
- POST:默认情况下不会被浏览器缓存,不适合被书签保存。
20、 状态码 301,401,501
- 301 Moved Permanently(永久重定向)表示请求的资源已经被永久移动到新的 URL,搜索引擎会将新的 URL 作为该资源的新地址进行保存
- 401 Unauthorized(未授权)客户端发起请求时,需要进行身份认证,但是认证失败或者没有经过认证,则服务器返回此状态码
- 501 Not Implemented(未实现)服务器无法识别或不支持客户端请求的方法,服务器会返回此状态码。
21、浏览器缓存机制,强缓存、协商缓存区别
浏览器缓存机制是指浏览器在接收到服务器返回的资源后,根据设置的缓存策略来决定是否缓存该资源以及如何缓存的过程。
- 强缓存:
- 强缓存是利用 HTTP 头信息中的 Expires 和 Cache-Control 来控制的。当浏览器发起请求时,会先根据这些头信息判断是否命中强缓存。
- 如果资源命中强缓存,浏览器直接从本地缓存中读取资源,并不会向服务器发送请求,节省了网络流量和请求时间。
- 常见的强缓存响应头有:Cache-Control 的 max-age 和 s-maxage,Expires 等。
- 协商缓存:
- 协商缓存是利用 Last-Modified 和 ETag 等响应头信息来控制的。当资源未命中强缓存时,浏览器会向服务器发送请求,服务器会根据这些头信息来验证资源是否仍然有效。
- 如果资源未发生变化,服务器会返回 304 Not Modified 状态码,告诉浏览器可以使用本地缓存,浏览器再次从本地缓存中读取资源。
- 协商缓存可以更精细地控制资源的更新,适用于频繁更新但变化较小的资源。
总的来说,强缓存是通过设置响应头信息来告诉浏览器在一定时间内直接使用本地缓存,而协商缓存则是通过与服务器进行验证来确定是否可以使用缓存。合理地配置强缓存和协商缓存可以提高网页加载速度,减少网络流量消耗。
22、 同源策略,跨域解决方法
同源定义: 如果两个url的协议,域名,端口号完全一致,那么这两个url就是同源的
同源策略是浏览器的一种安全策略,用于限制一个域下的文档或脚本如何与另一个域进行交互。
常见的跨域解决方法有以下几种:
-
JSONP: JSONP 是一种利用 script 标签可以跨域加载资源的特性来实现的跨域方式。通过在客户端动态创建 script 标签,并指定 src 属性为一个返回 JSON 数据的 URL,并将回调函数名称作为参数传递到服务器上,服务器会将 JSON 数据嵌入到回调函数中返回给客户端,从而实现跨域获取数据的目的。
-
CORS: CORS(Cross-Origin Resource Sharing)是一种跨域资源共享机制,可以使用 HTTP 头信息来告诉浏览器是否允许跨域请求。服务器在响应请求时,通过设置 Access-Control-Allow-Origin、Access-Control-Allow-Methods 等头信息来授权给定域的访问权限,从而允许跨域请求。
-
代理: 在客户端和服务端之间设置代理服务器,在代理服务器上完成跨域请求并将结果返回给客户端,客户端只需要和代理服务器进行交互。
23、 React中 useState() 是同步还是异步
useState() 是异步的
24、 Vue生命周期
Vue 组件的生命周期是指组件从创建、挂载、更新、销毁等阶段经历的一系列过程,它们对应着不同的钩子函数,允许我们在不同的阶段执行自定义的逻辑。下面是常用的 Vue 组件生命周期钩子函数以及它们的执行顺序:
- 创建阶段:
-
beforeCreate: 在实例初始化之后,在数据观测和事件配置之前被调用。
-
created: 在实例创建完成后被调用。此时实例已经完成了数据观测,属性和方法的运算,但尚未开始 DOM 编译和挂载。
- 挂载阶段:
-
beforeMount: 在挂载开始之前被调用。相关的 render 函数首次被调用。
-
mounted: 实例挂载到 DOM 后调用,此时组件已经生成对应的 DOM 结构,可以对 DOM 进行操作。
- 更新阶段:
-
beforeUpdate: 数据更新时调用,但还未开始重新渲染 DOM。
-
updated: 数据更新并重新渲染 DOM 之后调用。
- 销毁阶段:
-
beforeUnmount(Vue 3.x)/ beforeDestroy(Vue 2.x): 实例销毁之前调用。在这个阶段可以进行一些清理工作。
-
unmounted(Vue 3.x)/ destroyed(Vue 2.x): 实例销毁之后调用,此时组件相关的监听器和子组件都已经被移除。
组件间生命周期的顺序是根据组件的嵌套关系决定的。父组件的生命周期钩子函数会先于子组件的生命周期钩子函数执行,即先从父组件开始逐级往下执行,再依次执行子组件的生命周期钩子函数。
例如,如果有一个父组件 A 包含一个子组件 B,则它们的生命周期顺序如下:
父组件 A的beforeCreate -> 父组件 A的 created -> 父组件 A的 beforeMount -> 子组件 B 的 beforeCreate -> 子组件 B 的 created -> 子组件 B 的 beforeMount -> 子组件 B 的 mounted -> 父组件 A 的 mounted
25、 v-if和v-show
v-if 和 v-show 都是用来根据条件控制元素显示和隐藏的指令
-
v-if 是“真正”的条件渲染指令。如果表达式返回 true,Vue 会根据这个条件渲染元素并且插入到 DOM 中,如果为 false,则不会渲染该元素,也不会占据任何 DOM 空间。 使用 v-if 时,当条件切换时,Vue 会销毁或重新创建条件块及其内部的事件监听器和子组件。因此,如果初始渲染时条件为假,那么该元素不会被渲染到 DOM 中。
-
v-show 也用于根据条件来展示或隐藏元素,但它是通过修改元素的 display CSS 属性来实现的。如果表达式的值为 true,则元素会显示出来,如果为 false,则会隐藏。 使用 v-show 时,无论条件是 true 还是 false,元素都会被渲染到 DOM 中,只是通过修改 display 属性来控制其显示和隐藏。
26、 v-for为什么要有key
key 的作用是帮助 Vue 进行更高效的列表渲染,以及在重新渲染时准确地跟踪每个节点的身份。
- 性能优化:使用 key 可以帮助 Vue 识别每个列表项的身份。当数据源发生变化,Vue 会根据新旧列表的 key 值对比,只更新发生变化的列表项,而不需要重新渲染整个列表。这样能够提升性能,减少不必要的 DOM 操作。
- 状态保持:如果没有为列表项提供 key,Vue 会使用默认的机制进行重用 DOM 节点。这意味着如果列表项的顺序发生变化,Vue 可能会将之前的 DOM 节点与新的列表项错乱使用,导致出现不符合预期的结果。而通过为每个列表项提供唯一的 key,Vue 可以正确地跟踪每个节点的身份,确保其状态正确保持。
根据真实面试经历盘点面试题目,总结面试经验,分类总结面试题目答案