面试准备需要系统性地掌握HTML、CSS、java script和Vue等前端核心技术,理解其原理与应用场景,并能够灵活运用在实际项目中。本文将从高频考点出发,结合实战技巧,帮助你在技术面试中脱颖而出。
HTML&CSS
::before 和 :after 中双冒号和单冒号有什么区别?
在CSS中,::before 和 :after 是伪元素选择器,用于在元素的内容前后插入生成内容。双冒号(::)用于伪元素,而单冒号(:)用于伪类。这是CSS3中引入的更明确的语法,帮助开发者更容易识别伪元素和伪类。
- ::before:用于在元素内容之前插入内容。
- :after:用于在元素内容之后插入内容。
清除浮动的方式都有哪些,优缺点是什么?
浮动会导致父元素高度塌陷,因此需要清除浮动。常见的清除浮动方式包括:
- 使用 clear 属性:如
.clearfix:after { content: ""; clear: both; display: table; },优点是简单直接,适用于大多数情况,缺点是需要额外的标签或类。 - 使用 overflow: hidden:将父元素设置为 overflow: hidden,优点是简单,缺点是可能影响布局。
- 使用 display: flow-root:适用于现代浏览器,优点是简洁且不影响布局,缺点是兼容性较差。
- 使用 flex 布局:将父元素设置为 display: flex,优点是现代且简单,缺点是兼容性不好。
- 使用 grid 布局:将父元素设置为 display: grid,优点是现代且简洁,缺点是兼容性差。
垂直水平居中的几种方式?
方法一:flex 布局(推荐)
.parent {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
}
- 优点:代码简洁,现代浏览器支持好
- 缺点:IE10 以下不支持
方法二:绝对定位 + transform
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
- 优点:兼容性好,不依赖父元素尺寸
- 缺点:需要父元素设置 position: relative
方法三:绝对定位 + margin 负值
.child {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
height: 100px;
margin-top: -50px; /* 高度的一半 */
margin-left: -100px; /* 宽度的一半 */
}
- 优点:兼容性好
- 缺点:需要知道子元素尺寸
方法四:绝对定位 + margin: auto
.child {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
width: 200px;
height: 100px;
}
- 优点:兼容性好
- 缺点:需要知道子元素尺寸
方法五:table-cell
.parent {
display: table-cell;
vertical-align: middle;
text-align: center;
}
.child {
display: inline-block;
}
- 优点:兼容性好
- 缺点:父元素会变成表格单元格,可能影响布局
方法六:grid 布局
.parent {
display: grid;
place-items: center;
}
- 优点:代码最简洁
- 缺点:IE 不支持
方法七:line-height(仅文本)
.parent {
height: 200px;
line-height: 200px;
text-align: center;
}
- 优点:简单
- 缺点:只适用于单行文本
盒模型的理解,什么是标准盒模型,什么是怪异盒模型?
CSS盒模型由四部分组成:content(内容区)、padding(内边距)、border(边框)、margin(外边距)。
标准盒模型(W3C Box Model)
- 特点:width 和 height 只包含 content 的宽高
- 总宽度 = width + padding + border + margin
- 总高度 = height + padding + border + margin
.box {
box-sizing: content-box; /* 默认值 */
width: 200px;
padding: 20px;
border: 5px solid black;
margin: 10px;
/* 实际总宽度 = 200 + 20*2 + 5*2 + 10*2 = 270px */
}
怪异盒模型(IE Box Model / Border Box)
- 特点:width 和 height 包含 content + padding + border
- 总宽度 = width + margin
- 总高度 = height + margin
.box {
box-sizing: border-box;
width: 200px;
padding: 20px;
border: 5px solid black;
margin: 10px;
/* 实际总宽度 = 200 + 10*2 = 220px */
/* content 实际宽度 = 200 - 20*2 - 5*2 = 150px */
}
对比总结
| 特性 | 标准盒模型(content-box) | 怪异盒模型(border-box) |
|---|---|---|
| width/height 包含 | 仅 content | content + padding + border |
| 计算方式 | 需要手动计算总尺寸 | 直接设置总尺寸 |
| 使用场景 | 传统布局 | 现代布局(更直观) |
| 默认值 | 不是(需设置) | 不是(需设置) |
实际应用
现代开发推荐使用 border-box,因为它更直观,能够避免内容溢出。可以通过全局设置来统一:
* {
box-sizing: border-box;
}
这样设置 width: 100% 时不会超出父容器,布局更加稳定。
java script
java script 原型,原型链?
原型(Prototype)
每个 java script 对象(除 null 外)都有一个 proto 属性,指向它的原型对象(prototype)。
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('Hello, ' + this.name);
};
const person = new Person('Alice');
person.sayHello(); // Hello, Alice
// person.__proto__ === Person.prototype
console.log(person.__proto__ === Person.prototype); // true
原型链(Prototype Chain)
当访问对象的属性或方法时,java script 会沿着原型链向上查找:
- 先在对象自身查找
- 如果找不到,在对象的 proto(即构造函数的 prototype)中查找
- 如果还找不到,继续在 proto.proto 中查找,直到找到 Object.prototype,如果还找不到则返回 undefined
function Animal() {}
Animal.prototype.eat = function() {
console.log('eating...');
};
function Dog() {}
Dog.prototype = new Animal(); // 继承 Animal
Dog.prototype.bark = function() {
console.log('barking...');
};
const dog = new Dog();
dog.eat(); // eating... (从 Animal.prototype 继承)
dog.bark(); // barking... (从 Dog.prototype)
// 原型链:dog -> Dog.prototype -> Animal.prototype -> Object.prototype -> null
关键概念
- constructor:每个原型对象都有一个 constructor 属性,指向构造函数
- Object.create():创建以指定对象为原型的新对象
- hasOwnProperty():判断属性是否为对象自身属性(非继承)
实际应用
原型继承是一个常见场景:
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name, age) {
Parent.call(this, name); // 调用父构造函数
this.age = age;
}
Child.prototype = Object.create(Parent.prototype); // 继承原型
Child.prototype.constructor = Child; // 修正 constructor
const child = new Child('Tom', 10);
console.log(child.getName()); // Tom
事件代理是什么?冒泡和捕获?
事件冒泡(Event Bubbling)
事件从子元素向上传播到父元素,直到到达文档的顶部。
事件捕获(Event Capturing)
事件从父元素向下传播到子元素,这是事件流的第三阶段。
事件流三个阶段
- 捕获阶段(从最外层到目标元素)
- 目标阶段(事件到达目标元素)
- 冒泡阶段(从目标元素向上传播)
事件代理(Event Delegation)
将事件监听器绑定到父元素上,而不是每个子元素,这样可以减少内存占用并提高性能。
document.getElementById('parent').addEventListener('click', function(e) {
if (e.target.matches('.child')) {
// 处理子元素的点击事件
}
});
事件代理的优势
- 减少内存占用:避免为每个子元素添加事件监听器
- 提高性能:尤其是对大量动态元素
阻止事件传播
可以通过 stopPropagation() 和 stopImmediatePropagation() 来阻止事件传播:
element.addEventListener('click', function(e) {
e.stopPropagation(); // 阻止事件冒泡
});
如何解决跨域问题?
什么是跨域?
当请求的资源与当前页面的源(协议、域名、端口)不同时,就会产生跨域问题。
解决方案
- CORS(跨域资源共享) - 推荐
- 通过设置响应头
Access-Control-Allow-Origin来允许跨域访问 - JSONP
- 通过动态创建
<script>标签实现跨域请求 - 代理服务器
- 在服务器端设置代理,将请求转发到目标服务器
- postMessage(跨窗口通信)
- 使用
window.postMessage()实现跨窗口通信 - document.domain(仅限主域相同)
- 适用于子域名间通信
- WebSocket
- 建立持久连接,实现双向通信
方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| CORS | 安全、灵活 | 需要服务器配置 |
| JSONP | 简单、兼容性好 | 只能处理 GET 请求 |
| 代理服务器 | 灵活、安全 | 需要服务器支持 |
| postMessage | 简单、兼容性好 | 通信复杂 |
| document.domain | 简单 | 仅限主域相同 |
| WebSocket | 双向通信、实时 | 需要服务器支持 |
for 循环和 forEach 循环,哪个性能更好?
性能对比
for 循环通常比 forEach 更快,因为它是原生的 java script 循环,而 forEach 是数组的方法,内部调用了 for 循环的实现。
性能测试详细对比
const arr = [1, 2, 3, 4, 5];
// for 循环
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// forEach 循环
arr.forEach(function(item) {
console.log(item);
});
特殊情况
- 优化后的 for 循环:避免使用
arr.length而是使用let i = 0; while (i < arr.length) { ... } - for...of 循环:适用于可迭代对象,性能与
for循环相当 - 需要中断时:
for循环可以使用break或return,而forEach不能中断
实际建议
在需要高性能的场景中,推荐使用 for 循环。如果只是遍历数组元素,for...of 也是一个不错的选择。
变量提升的理解?
什么是变量提升?
在 java script 中,变量和函数声明会被提升到当前作用域的顶部,但这并不意味着它们在代码执行前被赋值。
var 的变量提升
console.log(name); // undefined,但 name 被提升
var name = 'Alice';
函数声明提升
console.log(sayHello); // function sayHello() { ... }
function sayHello() {
console.log('Hello');
}
函数表达式不提升
console.log(sayHello); // 报错,因为 sayHello 未定义
const sayHello = function() {
console.log('Hello');
};
let 和 const 的暂时性死区(TDZ)
console.log(name); // 报错,因为 name 在 TDZ 中
let name = 'Alice';
提升优先级
函数声明优先于变量声明提升。
实际示例
console.log(name); // undefined
var name = 'Alice';
console.log(name); // Alice
let name = 'Bob';
块级作用域(let/const)
if (true) {
let name = 'Alice';
console.log(name); // Alice
}
console.log(name); // 报错,因为 name 是块级作用域
避免变量提升的问题
- 使用 let 和 const 替代 var
- 避免在函数内部直接访问未声明的变量
- 明确变量声明位置,避免混淆
防抖和节流的理解?
防抖(Debounce)
防抖是指在事件被触发后,等待一段时间再执行函数,如果在这段时间内事件再次触发,则重新计时。
function debounce(fn, delay) {
let timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
节流(Throttle)
节流是指在一定时间间隔内只执行一次函数,防止函数被频繁调用。
function throttle(fn, delay) {
let last = 0;
return function() {
const now = Date.now();
if (now - last > delay) {
fn.apply(this, arguments);
last = now;
}
};
}
防抖 vs 节流
| 特点 | 防抖 | 节流 |
|---|---|---|
| 执行时机 | 事件停止后执行 | 一定时间间隔执行 |
| 使用场景 | 防止频繁触发(如输入搜索) | 控制触发频率(如滚动监听) |
| 是否中断 | 可以中断 | 不能中断 |
数组去重的方式都有哪些?
方法一:Set(推荐)
const arr = [1, 2, 3, 2, 1];
const unique = [...new Set(arr)];
方法二:filter + indexOf
const unique = arr.filter((item, index, array) => array.indexOf(item) === index);
方法三:reduce
const unique = arr.reduce((acc, item) => {
if (!acc.includes(item)) {
acc.push(item);
}
return acc;
}, []);
方法四:for 循环 + 新数组
const unique = [];
for (let i = 0; i < arr.length; i++) {
if (unique.indexOf(arr[i]) === -1) {
unique.push(arr[i]);
}
}
方法五:对象/Map 去重
const unique = Object.keys(arr.reduce((acc, item) => {
acc[item] = true;
return acc;
}, {})).map(Number);
方法六:Map 去重(推荐处理对象)
const unique = Array.from(new Map(arr.map(item => [item, true])).keys());
方法对比
| 方法 | 优点 | 缺点 |
|---|---|---|
| Set | 简洁、高效 | 不适用于对象 |
| filter + indexOf | 适用于简单数组 | 性能较差 |
| reduce | 灵活、可拓展 | 代码较长 |
| for 循环 + 新数组 | 易于理解 | 代码较长 |
| 对象/Map 去重 | 可处理对象 | 需要额外处理 |
| Map 去重 | 推荐处理对象 | 仅适用于对象 |
手写深拷贝的逻辑,讲述一下?
什么是深拷贝?
深拷贝是将对象的所有属性和嵌套对象都进行复制,确保新对象与原对象完全独立。
基础版本(只处理对象和数组)
function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
const clone = Object.assign({}, obj);
for (let key in clone) {
if (typeof clone[key] === 'object') {
clone[key] = deepClone(clone[key]);
}
}
return clone;
}
完整版本(处理各种边界情况)
function deepClone(obj) {
if (obj === null) return obj;
if (typeof obj !== 'object') return obj;
const clone = Object.create(Object.getPrototypeOf(obj));
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
使用示例
const original = { name: 'Alice', age: 30 };
const cloned = deepClone(original);
console.log(cloned); // { name: 'Alice', age: 30 }
其他实现方式
- JSON 序列化(简单但不完美)
- 适用于简单对象
-
无法处理函数、undefined、Symbol 类型
-
使用 structuredClone(现代浏览器)
- 支持更复杂的对象
- 适用于现代浏览器
核心要点总结
- 使用 JSON.stringify 和 JSON.parse 是最简单的深拷贝方式
- 处理嵌套对象、函数、Symbol 等边界情况需要更复杂的逻辑
- 现代浏览器推荐使用 structuredClone
this 指向的理解?
this 是什么?
在 java script 中,this 是一个关键字,它指向当前执行上下文的上下文对象。
绑定规则
- 默认绑定(独立函数调用):this 指向全局对象(在浏览器中是 window)
- 隐式绑定(对象方法调用):this 指向调用该方法的对象
- 显式绑定(call/apply/bind):this 可以被显式绑定到特定对象
- new 绑定(构造函数调用):this 指向新创建的对象
- 箭头函数(词法绑定):this 由外层作用域决定
优先级总结
默认绑定 < 隐式绑定 < 显式绑定 < new 绑定 < 箭头函数
常见面试题总结
this在函数中指向谁?this在对象方法中如何绑定?- 如何手动绑定
this?
如何使用 Promise 封装原生 Ajax?
原生 Ajax 回顾
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onload = function() {
if (xhr.status === 200) {
console.log(xhr.responseText);
}
};
xhr.send();
Promise 封装 GET 请求
function get(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function() {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(new Error('请求失败'));
}
};
xhr.onerror = function() {
reject(new Error('网络错误'));
};
xhr.send();
});
}
Promise 封装 POST 请求
function post(url, data) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(new Error('请求失败'));
}
};
xhr.onerror = function() {
reject(new Error('网络错误'));
};
xhr.send(JSON.stringify(data));
});
}
通用封装(支持所有请求方法)
function request(method, url, data) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status === 200) {
resolve(xhr.responseText);
} else {
reject(new Error('请求失败'));
}
};
xhr.onerror = function() {
reject(new Error('网络错误'));
};
xhr.send(data ? JSON.stringify(data) : null);
});
}
使用 async/await
async function fetchData() {
try {
const response = await request('GET', 'https://api.example.com/data');
console.log(response);
} catch (error) {
console.error(error);
}
}
核心要点
- Promise 是处理异步操作的现代方式
- async/await 是 Promise 的语法糖,使代码更清晰
- 封装 Ajax 请求可以提高代码的复用性和可维护性
箭头函数和普通函数的区别?
语法对比
| 特点 | 普通函数 | 箭头函数 |
|---|---|---|
| this 指向 | 由执行上下文决定 | 由外层作用域决定 |
| 构造函数 | 可以作为构造函数 | 不可以作为构造函数 |
| arguments | 支持 arguments 对象 | 不支持 arguments 对象 |
| prototype | 有 prototype 属性 | 没有 prototype 属性 |
| yield | 可以使用 yield | 不可以使用 yield |
| 词法绑定 | 不是 | 是 |
对比表格
| 特点 | 箭头函数 | 普通函数 |
|---|---|---|
| this 指向 | 词法绑定 | 运行时绑定 |
| 构造函数 | 不可以 | 可以 |
| arguments | 不支持 | 支持 |
| prototype | 没有 | 有 |
| yield | 不支持 | 支持 |
| 适用场景 | 作为回调函数、事件处理 | 作为构造函数、生成器函数 |
使用场景
- 适合使用箭头函数:作为回调函数、事件处理、对象方法
- 不适合使用箭头函数:作为构造函数、生成器函数、需要修改 this 的情况
总结
箭头函数在语法和功能上与普通函数有很多不同,选择使用时需要根据具体场景。在需要 this 绑定和生成器功能时,应该使用普通函数。
浏览器环境中事件循环的理解?
什么是事件循环?
事件循环是浏览器中处理异步任务的核心机制,它允许 java script 在等待异步操作完成时继续执行代码。
java script 执行机制
java script 是单线程语言,所有代码按顺序执行。当遇到异步操作时,会将任务放入任务队列中,等待事件循环处理。
执行顺序
- 同步代码按顺序执行
- 异步代码被放入任务队列
- 事件循环从任务队列中取出任务并执行
宏任务和微任务
- 宏任务(MacroTask):如
setTimeout、setInterval、setImmediate(Node.js) - 微任务(MicroTask):如
Promise.then、Promise.catch、MutationObserver、queueMicrotask
宏任务(MacroTask)
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
console.log('End');
微任务(MicroTask)
console.log('Start');
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
执行规则
- 同步代码执行
- 执行所有微任务
- 执行所有宏任务
- 重复上述过程
经典示例
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
复杂示例
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
async/await 的执行
async function example() {
console.log('Start');
await new Promise(resolve => setTimeout(resolve, 0));
console.log('Awaited');
}
example();
console.log('End');
实际应用场景
- 处理异步请求
- 控制流程顺序
- 提高代码可读性
总结
事件循环是浏览器中处理异步任务的核心机制,理解其原理和执行顺序是 java script 面试中的高频考点。掌握宏任务和微任务的区别,以及 async/await 的执行机制,能够帮助你在面试中展现出扎实的 java script 基础。
=== 和 Object.is() 的区别?
=== 严格相等
严格相等运算符(===)在比较值时,会同时比较类型和值,如果类型不同则直接返回 false。
Object.is() 同值相等
Object.is() 是严格相等的增强版本,支持处理 NaN 和 +0 与 -0 的比较问题。
核心区别
- NaN 的比较:=== 认为 NaN 不等于 NaN,而 Object.is() 认为 NaN 等于 NaN
- +0 和 -0 的比较:=== 认为 +0 等于 -0,而 Object.is() 认为它们不相等
对比表格
| 比较 | === | Object.is() |
|---|---|---|
| NaN | false | true |
| +0 和 -0 | true | false |
使用场景
- 推荐使用 ===,因为它在大多数情况下足够,并且性能更好
- Object.is() 适用于特殊场景,如需要区分 +0 和 -0
实际应用
在需要精确比较数值时,使用 Object.is() 可以避免一些常见的错误:
console.log(NaN === NaN); // false
console.log(Object.is(NaN, NaN)); // true
console.log(+0 === -0); // true
console.log(Object.is(+0, -0)); // false
Promise 的理解?
Promise 是什么?
Promise 是用于处理异步操作的对象,它有三种状态:pending(等待中)、fulfilled(已解决)、rejected(已拒绝)。
三种状态
- pending:初始状态
- fulfilled:操作成功
- rejected:操作失败
基本用法
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
resolve('Success');
}, 1000);
});
promise.then(result => {
console.log(result); // Success
}).catch(error => {
console.error(error);
});
核心方法
- then() - 处理成功
- catch() - 处理失败
- finally() - 无论成功或失败都执行
promise.then(result => {
console.log(result); // Success
}).catch(error => {
console.error(error);
}).finally(() => {
console.log('Finally');
});
静态方法
- Promise.resolve() - 快速创建成功的 Promise
- Promise.reject() - 快速创建失败的 Promise
- Promise.all() - 等待所有 Promise 完成
- Promise.race() - 谁先完成用谁
- Promise.allSettled() - 等待所有 Promise 完成(不管成功失败)
async/await(Promise 的语法糖)
async function example() {
try {
const result = await new Promise((resolve) => {
setTimeout(() => resolve('Success'), 1000);
});
console.log(result); // Success
} catch (error) {
console.error(error);
}
}
实际应用
- 封装 Ajax 请求:使用 Promise 封装 Ajax 请求,提高代码可读性
- 处理异步流程:使用 async/await 控制异步流程顺序
- 错误处理:使用 catch() 或 try/catch 处理异步错误
总结
Promise 是处理异步操作的强大工具,理解其原理和方法对于前端开发至关重要。掌握 async/await 的执行机制和错误处理方式,可以写出更加清晰和高效的异步代码。
递归函数的理解?
什么是递归?
递归是指函数在执行过程中调用自身,常用于处理树形结构、分治算法等。
基本结构
function factorial(n) {
if (n === 0) return 1;
return n * factorial(n - 1);
}
经典示例
- 计算阶乘
- 斐波那契数列
- 数组求和
- 树形结构遍历
递归 vs 循环
| 优点 | 缺点 |
|---|---|
| 代码简洁 | 可能导致栈溢出 |
| 适合处理树形结构 | 递归深度受限制 |
优缺点
- 优点:代码简洁,适合处理树形结构
- 缺点:可能导致栈溢出,递归深度受限制
优化:尾递归
尾递归是指递归调用是函数的最后一步操作,可以被优化成循环。
实际应用场景
- 计算阶乘
- 遍历树形结构
- 分治算法
总结
递归是一种强大的编程技巧,但在使用时需要注意递归深度和栈溢出的问题。掌握递归与循环的区别,以及如何优化递归,是面试中的常见考点。
Vue
Vue 的修饰符都有哪些?
Vue 的修饰符是用于修改事件或表单的默认行为的,常见的修饰符包括:
- .prevent:阻止默认事件行为(如表单提交)
- .stop:阻止事件冒泡
- .capture:使用事件捕获阶段
- .self:阻止事件冒泡到父元素
- .once:只触发一次事件
Vue 自定义指令的理解?
自定义指令是 Vue 提供的一种扩展机制,允许我们自定义一些指令来操作 DOM。
Vue.directive('highlight', {
bind(el, binding) {
el.style.backgroundColor = binding.value;
}
});
v-if 和 v-show 的区别?
- v-if:根据条件判断是否渲染元素
- v-show:根据条件控制元素的显示状态
v-model 可以用在哪些表单上?
v-model 可以用于:
- text:文本输入框
- number:数字输入框
- checkbox:复选框
- radio:单选框
- select:下拉框
- textarea:多行文本框
Vue 中 computed 和 watch 和 methods 的区别?
- computed:计算属性,基于响应式依赖进行缓存,提高性能
- watch:监听数据变化,适合执行异步操作
- methods:方法,适合执行同步操作
watch 监听对象,新值和旧值一样,如何区分新旧值?
在监听对象时,新值和旧值一样,但可以通过 deep true 来监听属性变化。
watch: {
someObject: {
handler(newVal, oldVal) {
// 处理新旧值变化
},
deep: true
}
}
Vue 的组件通讯?
组件通讯是 Vue 中的一个重要概念,主要方式包括:
- props 和 $emit:父子组件之间通信
- $root:全局通信
- $event:向上传递参数
- provide/inject:祖孙组件之间通信
- Vuex:状态管理
- Event Bus:全局事件总线
Vue 的生命周期钩子函数?
Vue 2.x 生命周期钩子
- 创建阶段(Creation):
- beforeCreate
-
created
-
挂载阶段(Mounting):
- beforeMount
-
mounted
-
更新阶段(Updating):
- beforeUpdate
-
updated
-
销毁阶段(Destruction):
- beforeDestroy
- destroyed
Vue 3.x 生命周期钩子
- 创建阶段(Creation):
- onBeforeCreate
-
onCreated
-
挂载阶段(Mounting):
- onBeforeMount
-
onMounted
-
更新阶段(Updating):
- onBeforeUpdate
-
onUpdated
-
销毁阶段(Destruction):
- onBeforeUnmount
- onUnmounted
Vue 2.x 与 Vue 3.x 生命周期钩子对比
| 钩子 | Vue 2.x | Vue 3.x |
|---|---|---|
| 创建阶段 | beforeCreate, created | onBeforeCreate, onCreated |
| 挂载阶段 | beforeMount, mounted | onBeforeMount, onMounted |
| 更新阶段 | beforeUpdate, updated | onBeforeUpdate, onUpdated |
| 销毁阶段 | beforeDestroy, destroyed | onBeforeUnmount, onUnmounted |
vue-router 的导航守卫?
导航守卫是 vue-router 提供的一种机制,用于在导航过程中进行拦截和处理。
全局守卫
- 全局前置守卫
beforeEach - 全局解析守卫
beforeResolve - 全局后置钩子
afterEach
路由独享守卫 beforeEnter
const router = new VueRouter({
routes: [
{
path: '/user',
component: User,
beforeEnter: (to, from, next) => {
// 检查权限
if (user.isLoggedIn()) {
next();
} else {
next('/login');
}
}
}
]
});
组件内守卫
beforeRouteEnter:在组件创建前执行beforeRouteUpdate:在组件更新前执行beforeRouteLeave:在组件离开前执行
执行顺序
- 全局前置守卫
beforeEach - 路由独享守卫
beforeEnter - 组件内守卫
beforeRouteEnter - 组件内守卫
beforeRouteUpdate - 组件内守卫
beforeRouteLeave - 全局后置钩子
afterEach
vuex 的 state、getters、mutations、actions 的区别?
- state:保存应用