1. 什么是 Vue 的钩子函数?
Vue 的钩子函数是在 Vue 实例生命周期的不同阶段自动执行的函数,能让开发者在特定时刻执行自定义逻辑。例如created钩子在实例创建完成后被调用,此时可以进行数据初始化、发起异步请求等操作;mounted钩子在组件挂载到 DOM 后调用,可用于操作 DOM 元素;updated钩子在数据更新且 DOM 重新渲染后调用,能对更新后的 DOM 进行处理;beforeDestroy钩子在实例销毁前调用,可用于清理定时器、解绑事件等收尾工作。
2. 你用过事件扩展符 (...),在什么场景下用的?
事件扩展符(...)通常指的是 ES6 中的展开运算符,在事件相关场景中,常用于将一个数组或对象展开为独立的参数。比如在 React 中,当向子组件传递多个事件处理函数时,可以通过展开运算符将事件对象展开,避免逐个传递。例如:
import React, { useState } from'react';
const ParentComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
const handleMouseEnter = () => {
console.log('Mouse entered');
};
const eventHandlers = {
onClick: handleClick,
onMouseEnter: handleMouseEnter
};
return (
<div>
<ChildComponent {...eventHandlers} />
</div>
);
};
const ChildComponent = ({ onClick, onMouseEnter }) => {
return (
<button onClick={onClick} onMouseEnter={onMouseEnter}>
Click me
</button>
);
};
export default ParentComponent;
3. 什么是服务端渲染?
服务端渲染(SSR)是指在服务器端生成 HTML 页面,再将其发送到客户端的过程。与传统的客户端渲染不同,SSR 在服务器端完成页面的构建,将完整的 HTML 发送给客户端,客户端只需解析和显示页面。优势在于首屏加载速度快,因为无需等待客户端下载和执行 JavaScript 代码来生成页面,有利于 SEO,搜索引擎爬虫可以直接抓取服务器返回的 HTML 内容;缺点是服务器负载增加,开发和维护成本较高,需要同时关注服务器端和客户端的代码逻辑。
4. 前端性能优化的指标有哪些,如何量化?
指标:
页面加载时间:从用户输入 URL 到页面完全加载完成的时间。
首次内容绘制时间(FCP):浏览器首次将任何来自 DOM 的内容渲染到屏幕的时间。
最大内容绘制时间(LCP):视口内最大的内容元素加载完成的时间。
可交互时间(TTI):页面达到完全可交互状态所需的时间。
量化工具:
Chrome DevTools:Performance 面板可以记录和分析页面加载过程,提供详细的时间线和性能指标数据。
Lighthouse:一款开源的自动化工具,可对网页进行性能、可访问性等方面的评估,并给出详细的报告和优化建议。
PageSpeed Insights:Google 提供的工具,能分析网页性能,并给出优化建议和分数。
5. 你知道哪些前端性能优化的手段?
代码层面:压缩和合并 CSS、JavaScript 文件,减少文件体积和 HTTP 请求数;使用代码拆分(如 Webpack 的splitChunks),将大的代码文件拆分成多个小文件,按需加载;优化算法和数据结构,减少不必要的计算和内存占用。
资源加载:使用浏览器缓存,设置合理的缓存策略,让静态资源在浏览器中缓存,减少重复加载;图片优化,压缩图片大小,使用合适的图片格式(如 WebP 格式,在支持的浏览器中有更好的压缩比),使用srcset和picture标签实现响应式图片加载。
渲染优化:减少 DOM 操作,批量修改 DOM,使用requestAnimationFrame代替setTimeout来执行动画和页面更新操作,以提高动画流畅度;避免强制同步布局,例如在修改元素样式后,立即获取元素的布局信息(如offsetWidth、scrollTop等),会导致浏览器重新计算布局,影响性能。
6. React 中的 Hooks 有哪些优缺点?
优点:
函数组件复用状态逻辑:Hooks 允许在不编写类组件的情况下使用状态和其他 React 特性,方便在不同函数组件间复用状态逻辑,例如useState、useEffect等。
简化代码结构:相比类组件,Hooks 使代码更简洁,避免了复杂的this绑定问题和生命周期方法的混乱。
更清晰的代码逻辑:useEffect可以将副作用操作(如数据获取、订阅事件等)集中管理,使代码逻辑更清晰。
缺点:
学习曲线:对于习惯类组件的开发者,Hooks 的概念和用法需要一定时间学习和适应。
滥用风险:如果过度使用或使用不当,可能会导致代码难以理解和维护,例如在useEffect中依赖项设置不正确可能导致不必要的重复渲染。
7. React 的事件绑定原理是什么?
React 的事件绑定并非直接绑定在 DOM 元素上,而是采用事件代理机制。React 在最外层的 DOM 元素(通常是document)上统一监听所有事件,当事件触发时,React 会根据事件类型和目标元素,在内部维护的事件映射表中找到对应的事件处理函数并执行。这种机制减少了内存消耗,提高了事件处理效率,同时也方便对事件进行统一管理和处理,例如对事件进行合成、批量处理等。
8. setState 是同步还是异步的?
setState在 React 中通常是异步的。这是为了性能优化,React 会将多个setState操作合并成一次批量更新,减少不必要的重新渲染。例如在一个循环中多次调用setState,如果是同步更新,会导致多次重新渲染,影响性能。但在某些情况下,setState也可以表现为同步,比如在setTimeout、Promise等异步回调中,或者在原生 DOM 事件处理函数中,setState是同步的。
9. ReactRouter 的基本用法是什么?
ReactRouter 是用于在 React 应用中实现路由功能的库。基本用法如下:
import React from'react';
import ReactDOM from'react-dom';
import { BrowserRouter as Router, Routes, Route } from'react-router-dom';
import HomePage from './HomePage';
import AboutPage from './AboutPage';
const App = () => {
return (
<Router>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
</Routes>
</Router>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
在上述代码中,Router组件包裹整个应用,提供路由功能;Routes组件用于定义路由规则;Route组件定义具体的路由路径和对应的组件,当 URL 路径匹配Route的path时,会渲染对应的element组件。
10. Vue-router 如何实现懒加载?
Vue-router 实现懒加载有两种常见方式:
异步组件:使用import()语法,将组件定义为异步加载。例如:
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/home',
name: 'Home',
component: () => import('./views/Home.vue')
}
]
});
export default router;
Webpack 的require.ensure:在 Webpack 环境下,也可以使用require.ensure来实现懒加载,不过import()语法更简洁和推荐。例如:
const router = new VueRouter({
routes: [
{
path: '/home',
name: 'Home',
component: resolve => require.ensure([], () => resolve(require('./views/Home.vue')))
}
]
});
11. Vue 列表为什么加 key?
在 Vue 列表中添加key主要是为了帮助 Vue 的虚拟 DOM 算法更高效地进行更新。当列表数据发生变化时,Vue 会根据key来判断哪些元素需要更新、删除或添加。如果没有key,Vue 会默认使用数组的索引作为key,但在数组元素顺序改变或有元素增删时,会导致不必要的 DOM 更新,影响性能。而使用唯一的key可以确保 Vue 准确识别每个元素,从而进行更精准的 DOM 更新,提高渲染效率。
12. 重绘和重排的区别是什么,如何避免?
区别:
重排(回流):当 DOM 的结构、布局、尺寸等发生改变时,浏览器需要重新计算元素的几何属性,重新构建渲染树,这个过程称为重排。重排会影响到页面的布局,开销较大。
重绘:当元素的外观(如颜色、背景等)发生改变,但不影响布局时,浏览器只需要重新绘制该元素,这个过程称为重绘。重绘的开销比重排小。
避免方法:
减少 DOM 操作:批量修改 DOM,避免频繁的增删改操作。
避免强制同步布局:在修改元素样式后,避免立即获取元素的布局信息(如offsetWidth、scrollTop等),因为这会导致浏览器强制同步布局,触发重排。
使用requestAnimationFrame:在执行动画和页面更新操作时,使用requestAnimationFrame代替setTimeout,它会在浏览器下一次重绘之前执行,能提高动画流畅度,减少不必要的重排和重绘。
13. 如何保持前后端实时通信?
WebSocket:是一种基于 TCP 协议的全双工通信协议,在客户端和服务器之间建立持久连接,允许实时双向通信。例如在聊天应用中,用户发送消息后,服务器能立即接收并推送给其他用户。
Server-Sent Events(SSE):是一种单向的实时通信技术,服务器可以向客户端推送消息,适用于实时数据更新场景,如股票行情、新闻推送等。客户端通过EventSource对象接收服务器发送的事件流。
长轮询:客户端向服务器发送请求,服务器在有新数据时才返回响应,否则保持连接等待,客户端收到响应后立即再次发送请求,实现实时通信,但相比 WebSocket 和 SSE,长轮询的开销较大。
14. fetch 请求方式有哪些?
fetch是浏览器提供的用于发起 HTTP 请求的 API,常见请求方式有:
GET:用于获取资源,请求参数会附加在 URL 后面,例如fetch('https://example.com/api/data?param1=value1¶m2=value2')。
POST:用于提交数据,请求参数放在请求体中,常用于表单提交、上传文件等场景,例如:
fetch('https://example.com/api/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: 'value' })
});
PUT:用于更新资源,将整个资源替换为请求体中的数据。
DELETE:用于删除资源,例如fetch('https://example.com/api/data/123', { method: 'DELETE' })。
15. 创建 ajax 的过程是什么?
在 JavaScript 中创建 AJAX 请求的基本过程如下:
创建 XMLHttpRequest 对象:const xhr = new XMLHttpRequest();
配置请求:设置请求方法(GET、POST 等)、URL 和是否异步等,例如xhr.open('GET', 'https://example.com/api/data', true);
设置请求头(可选):如果是 POST 请求,通常需要设置Content-Type头,例如xhr.setRequestHeader('Content-Type', 'application/json');
发送请求:如果是 GET 请求,直接xhr.send();;如果是 POST 请求,将请求体数据作为参数传入,例如xhr.send(JSON.stringify({ key: 'value' }));
处理响应:通过监听xhr的onreadystatechange事件,在readyState为 4 且status为 200 时,表示请求成功,获取响应数据,例如:
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
console.log(response);
}
};
16. axios 的拦截器原理及应用是什么?
原理:axios 的拦截器是在请求发送前或响应返回后,对请求或响应进行拦截和处理的机制。它基于 Promise 的链式调用,通过axios.interceptors.request.use和axios.interceptors.response.use方法分别添加请求拦截器和响应拦截器。请求拦截器可以在请求发送前对请求配置进行修改,如添加请求头、设置 loading 状态等;响应拦截器可以在响应返回后对响应数据进行处理,如错误统一处理、数据格式化等。
应用:
添加请求头:在请求拦截器中添加Authorization头,用于身份验证,例如:
axios.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
错误处理:在响应拦截器中统一处理错误,例如:
axios.interceptors.response.use(response => {
return response;
}, error => {
if (error.response) {
// 处理不同的HTTP错误状态码
switch (error.response.status) {
case 401:
// 处理未授权错误
break;
case 404:
// 处理资源未找到错误
break;
default:
break;
}
}
return Promise.reject(error);
});
17. 盒模型是什么?
CSS 盒模型是用于描述 HTML 元素在页面上的布局和尺寸的概念。它由内容区(content)、内边距(padding)、边框(border)和外边距(margin)组成。盒模型分为标准盒模型和怪异盒模型(IE 盒模型)。在标准盒模型中,元素的宽度和高度仅指内容区的尺寸,计算整个元素占据空间的宽度公式为width + padding + border + margin,高度同理。在怪异盒模型中,元素的宽度和高度包含了内容区、内边距和边框,即设置的width和height属性值包含了content、padding和border,计算占据空间的宽度公式为width + margin,高度同理。可以通过box-sizing属性来切换盒模型,box-sizing: content-box表示标准盒模型(默认值),box-sizing: border-box表示怪异盒模型。
18. 伪数组和数组的区别是什么?
数组:是一种有序的数据结构,具有length属性,并且可以通过数字索引访问元素,同时拥有数组的原型方法,如push、pop、map等。例如const arr = [1, 2, 3];
伪数组:虽然看起来像数组,有length属性,可以通过数字索引访问元素,但不具备数组的原型方法。常见的伪数组有函数的arguments对象、document.querySelectorAll返回的结果等。例如:
function example() {
const args = arguments;
console.log(args.length); // 有length属性
console.log(args[0]); // 可以通过索引访问元素
// 但没有数组的原型方法,例如args.map is not a function
}
可以通过Array.from或展开运算符(...)将伪数组转换为真正的数组,从而使用数组的方法。
19. 如何实现可过期的 localStorage 数据?
可以通过在存储数据时,同时存储一个过期时间戳来实现。例如:
function setLocalStorageWithExpiry(key, value, ttl) {
const now = new Date();
const item = {
value: value,
expiry: now.getTime() + ttl
};
localStorage.setItem(key, JSON.stringify(item));
}
function getLocalStorageWithExpiry(key) {
const itemStr = localStorage.getItem(key);
if (!itemStr) {
return null;
}
const item = JSON.parse(itemStr);
const now = new Date();
if (now.getTime() > item.expiry) {
localStorage.removeItem(key);
return null;
}
return item.value;
}
// 使用示例
setLocalStorageWithExpiry('userData', { name: 'John' }, 60 * 1000); // 1分钟过期
const data = getLocalStorageWithExpiry('userData');
console.log(data);
评论区