本轮要点: 生命周期、Vue3 API、响应式原理、v-for、事件循环
本轮共 21 道题。答案默认折叠,便于先自行作答。
1. 为什么使用而非题目要点
<header>是HTML5语义化标签,明确头部区域含义
- 语义化标签有助于SEO和辅助技术识别
- 提高代码可读性和维护性,减少依赖类名赋予语义
- 推荐优先使用语义标签代替无语义的
<div>+类名组合
参考答案
考察点
- 理解HTML语义化标签的重要性及作用
- 掌握语义化标签对SEO和无障碍的影响
- 了解语义化标签在开发协作和维护中的优势
参考答案
一、语义化标签的核心概念
- HTML5引入了如
<header>、<nav>、<article>、<footer> 等语义化标签,明确表达页面结构和内容含义
- 语义化标签比普通
<div> 搭配类名更具意义,利于浏览器、搜索引擎、辅助设备等理解页面
二、使用 <header> 的优势
明确语义
<header>表示页面或区块的头部区域,包含标题、导航等信息
- 比
<div class="header"> 直接通过类名赋予语义更标准、易识别
提升可访问性
- 语义化标签被屏幕阅读器等辅助技术识别,帮助视障用户理解页面结构
<header>使辅助设备快速定位页眉内容,提升用户体验
SEO友好
- 搜索引擎利用语义标签准确抓取页面结构和重点内容
- 语义标签利于内容分类和权重判断,可能提高搜索排名
代码规范与维护
- 语义化标签增强代码自解释性,方便团队协作和代码维护
- 减少对类名、ID等非结构性属性的依赖,代码更简洁清晰
三、使用场景对比
<header>适合定义页面或区块的头部区域
<div class="header">仅作为普通容器,没有内置语义
- 两者均可通过CSS样式控制外观,但语义化标签具备结构语义
2. SEO和可访问性有何影响
题目要点
- 语义化标签提升SEO抓取效率和内容权重分配
- 结构化页面内容,增强搜索引擎对重点信息的识别能力
- 语义标签是辅助技术识别页面结构的关键,提升可访问性
- 有助于残障用户快速导航和理解页面
- 符合无障碍标准,降低法律和合规风险
参考答案
考察点
- 理解SEO(搜索引擎优化)对网页结构和内容的重要性
- 掌握语义化标签对搜索引擎抓取的正面影响
- 了解可访问性(Accessibility)概念及辅助技术如何利用语义标签
- 能说明如何通过结构优化提升用户体验及业务价值
参考答案
一、SEO影响
语义化标签助力搜索引擎理解页面结构
- 搜索引擎依赖HTML结构判断内容层级和重点
- 使用
<header>、<article>、<nav> 等标签,明确标识内容区块,提升爬虫抓取效率
提升关键词和内容相关性权重
- 语义标签让搜索引擎区分正文、导航、页脚等区域,避免误判
- 正确结构化内容帮助搜索引擎更精准地抓取和索引,提高排名可能性
丰富搜索结果展示
- 结构化良好的页面更容易获得富媒体摘要(如站点链接、快速导航)
二、可访问性影响
辅助技术依赖语义信息提供用户导航
- 屏幕阅读器、盲人设备通过语义标签识别页面结构,便于快速定位
<header>、<nav>等标签帮助用户了解页面布局和功能区
提升残障用户体验
- 语义清晰的页面减少理解和操作成本,提升交互效率和满意度
符合法律法规和行业标准
- 许多国家和地区对无障碍有强制性要求,合理使用语义标签有助合规
3. 屏幕阅读器如何解析语义化标签
题目要点
- 屏幕阅读器依赖HTML语义化标签构建页面结构模型
- 语义化标签帮助划分区域、提升导航效率和内容理解
- 结合ARIA属性,辅助技术识别交互元素的角色和状态
- 非语义标签缺乏结构信息,降低辅助设备的可用性
- 合理使用语义化标签是提升无障碍体验的关键
参考答案
考察点
- 理解屏幕阅读器的工作原理及语义化标签的重要性
- 掌握语义化标签如何辅助辅助技术(如屏幕阅读器)识别页面结构
- 了解辅助技术与HTML结构的交互方式及用户体验影响
参考答案
一、屏幕阅读器的工作原理简述
- 屏幕阅读器通过解析网页的DOM结构和HTML标签,读取文本内容并以语音或盲文输出
- 其核心依赖网页的语义信息(标签、ARIA属性等)来构建页面的“语义树”
- 语义树反映页面结构,帮助用户理解内容层次、导航方式及控件功能
二、语义化标签在屏幕阅读器中的作用
明确页面结构
<header>、<nav>、<main>、<footer>等标签标识不同页面区域,屏幕阅读器据此划分内容块
- 用户可以跳转到这些语义块,快速浏览和定位
提高内容可理解性
- 语义标签提供内容的上下文信息,比如
<article>表示独立文章,<section>表示逻辑区块
- 这有助于用户理解页面内容的组织方式
辅助交互控件识别
- 语义标签结合ARIA属性,帮助屏幕阅读器识别按钮、菜单、表单等交互元素
- 提供正确的角色(role)、状态(state)和属性(property)信息
三、实际解析流程
- 屏幕阅读器监听DOM变化,构建和维护一棵语义树
- 访问者使用快捷键导航时,屏幕阅读器基于语义树跳转对应标签区域
- 阅读顺序由语义结构决定,非语义标签(如无意义
<div>)无法提供有效导航点
- 开发者通过合理语义标签和ARIA属性增强用户体验
4. 浏览器解析HTML时遇到 <script defer> 和 <script async> 会如何影响渲染?如何优化
题目要点
defer延迟执行,保证执行顺序,不阻塞HTML解析
async异步执行,执行顺序不可控,可能阻塞渲染
- 优先用
defer加载依赖顺序脚本,async用于独立脚本
- 合理拆分脚本体积,配合网络优化提升性能
- 选择合适属性结合业务需求,达到最佳加载与渲染平衡
参考答案
考察点
- 理解
<script> 标签中 defer 和 async 属性的区别
- 掌握浏览器HTML解析与脚本加载、执行的流程
- 了解不同脚本加载方式对页面渲染性能和用户体验的影响
- 熟悉合理使用
defer和async的优化策略
参考答案
一、<script defer> 和 <script async> 的核心概念
<script defer>:脚本会在HTML文档解析完成后,按顺序执行,保证脚本不会阻塞DOM的构建
<script async>:脚本会在下载完成后立即执行(异步执行),不保证执行顺序,可能打断HTML解析
二、浏览器解析流程及影响
阶段 普通 <script> <script defer><script async>HTML解析 解析遇到脚本立即暂停,下载并执行脚本,阻塞渲染 解析时不阻塞,脚本异步下载,等文档解析完后执行 解析时不阻塞,脚本异步下载,下载完成立即执行 执行顺序 按出现顺序依次执行 按出现顺序依次执行 不保证顺序,哪个先下载完成先执行 页面渲染阻塞 可能阻塞,造成页面白屏 不阻塞HTML解析,减小渲染阻塞 可能在解析中断,影响渲染
三、优化建议
优先使用 defer 加载顺序敏感的脚本
- 确保脚本不会阻塞DOM构建
- 保持脚本执行顺序,避免因异步执行引发依赖问题
使用 async 加载独立、非依赖性脚本
- 适合广告、分析、统计等独立功能脚本
- 提升加载效率,减少页面阻塞时间
合理拆分脚本,避免单个大文件阻塞
- 利用代码拆分(Code Splitting)减少单个文件体积
- 配合
defer/async提高加载和渲染速度
结合其他性能优化手段
- 使用HTTP/2多路复用,提升脚本加载并发能力
- 合理设置缓存,减少重复加载
- 将非关键脚本延迟加载,保证首屏渲染速度
5. CSS中的 “flex:1;” 是什么意思?
题目要点
在CSS中,flex: 1; 是一个简写属性,用于设置一个元素为Flexbox子项时的布局行为。具体来说,它包括以下几个部分:
- flex-grow:定义了当父容器有多余空间时,子项如何增长。flex-grow: 1 表示子项将尽可能多地占据可用空间。
- flex-shrink:定义了当父容器空间不足时,子项如何缩小。默认值是 1,表示子项可以等比例缩小以适应容器空间。
- flex-basis:定义了在分配多余空间之前,子项的默认大小。flex-basis: 1 通常意味着子项的初始大小是相对于父容器的1%(如果父容器设置了flex-basis)。
flex: 1; 通常用于确保Flexbox子项能够充满其父容器,同时在空间不足时能够适当缩小,保持布局的灵活性。
参考答案
flex 是 flex-grow, flex-shrink 和 flex-basis的简写。
除了auto (1 1 auto) 和 none (0 0 auto)这两个快捷值外,还有以下设置方式:
- 当 flex 取值为一个非负数字,则该数字为 flex-grow 值,flex-shrink 取 1,flex-basis 取 0%,如下是等同的:
.item {flex: 1;}
.item {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
}
- 当 flex 取值为 0 时,对应的三个值分别为 0 1 0%
.item {flex: 0;}
.item {
flex-grow: 0;
flex-shrink: 1;
flex-basis: 0%;
}
- 当 flex 取值为一个长度或百分比,则视为 flex-basis 值,flex-grow 取 1,flex-shrink 取 1,有如下等同情况(注意 0% 是一个百分比而不是一个非负数字)
.item-1 {flex: 0%;}
.item-1 {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
}
.item-2 {flex: 24px;}
.item-2 {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 24px;
}
- 当 flex 取值为两个非负数字,则分别视为 flex-grow 和 flex-shrink 的值,flex-basis 取 0%,如下是等同的:
.item {flex: 2 3;}
.item {
flex-grow: 2;
flex-shrink: 3;
flex-basis: 0%;
}
- 当 flex 取值为一个非负数字和一个长度或百分比,则分别视为 flex-grow 和 flex-basis 的值,flex-shrink 取 1,如下是等同的:
.item {flex: 11 32px;}
.item {
flex-grow: 11;
flex-shrink: 1;
flex-basis: 32px;
}
6. 解释以下代码输出并说明内存风险:
function createCounter() {
let count = 0;
return function() { count++; console.log(count) };
}
const counter = createCounter();
题目要点
counter是闭包函数,持有对count变量的引用- 每次调用
counter()都会累加并输出count值 - 闭包导致变量生命周期延长,可能引发内存泄漏风险
- 需谨慎管理闭包,避免无谓占用大量内存
- 通过合理释放引用,防止闭包引起的内存泄漏
参考答案
考察点
- 理解闭包(Closure)及其实现原理
- 掌握闭包对变量生命周期的影响
- 识别闭包可能带来的内存泄漏风险及优化方法
- 理解JavaScript函数作用域与执行上下文的关系
参考答案
一、代码输出解释
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 输出 1
counter(); // 输出 2
createCounter 返回了一个闭包函数,该函数引用了其外部作用域中的变量 count- 每调用一次
counter(),count 自增并输出当前值,体现了闭包对外部变量的持久引用 count 不会在 createCounter 执行结束后销毁,因为闭包保持对它的引用
二、闭包的原理和作用
- 闭包是函数与其相关作用域环境的组合,允许函数访问定义时的作用域
- JavaScript中,函数执行时会形成执行上下文,变量在上下文中存活
- 返回的内部函数持续持有对外部变量的引用,防止变量被垃圾回收
三、内存风险分析
- 潜在内存泄漏:闭包会使其引用的变量长时间保留在内存,尤其当闭包持有大量数据或大量闭包被持续创建时
- 如果闭包中引用了大对象或DOM节点,且未及时释放,可能导致内存泄漏
- 反复创建闭包函数且不释放引用,会持续占用内存,影响性能
四、如何避免风险
- 合理设计闭包,避免闭包无意义地持有大量变量或对象
- 当闭包不再使用时,解除对变量的引用(如将闭包置为null)以辅助GC回收
- 注意避免闭包与DOM节点互相引用,导致循环引用
7. 如何用WeakMap重构避免内存泄漏?闭包中变量存储在堆的哪个区域
题目要点
- 闭包变量存储在堆内存,受引用关系影响生命周期
WeakMap利用弱引用特性,键对象无引用时自动回收- 通过
WeakMap存储闭包私有数据,避免闭包导致的内存泄漏 - 用外部对象作为键,数据与闭包解耦,方便管理和释放
- 适合复杂状态管理,提升内存效率和应用性能
参考答案
考察点
- 理解闭包变量的内存存储位置及生命周期
- 掌握使用
WeakMap 管理闭包数据,防止内存泄漏的原理
- 理解JavaScript垃圾回收机制及弱引用的作用
- 能结合实际场景说明
WeakMap 替代闭包变量存储的优化方案
参考答案
一、闭包中变量的内存存储位置
- JavaScript中,函数执行时创建执行上下文,局部变量存储在堆内存中
- 闭包使函数持续持有对外层作用域变量的引用,导致这些变量无法被垃圾回收
- 堆内存(Heap)是动态分配区域,用于存储对象和闭包捕获的变量,生命周期取决于引用关系
- 只要闭包存在,对应的变量就存活在堆中
二、WeakMap避免内存泄漏的原理
WeakMap的键是弱引用对象,不计入垃圾回收的根引用
- 当键对象不再被引用时,
WeakMap中的键值对会自动被GC回收
- 适合存储和管理闭包私有数据,避免闭包对象持有过长生命周期
- 通过将闭包中状态或数据存储在
WeakMap,利用外部对象作为键,实现自动释放
三、用 WeakMap 重构闭包示例
传统闭包写法(存在闭包持久引用风险):
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
使用 WeakMap 存储私有状态:
const countMap = new WeakMap();
function createCounter() {
const obj = {}; // 用作WeakMap的键
countMap.set(obj, 0);
return function() {
let count = countMap.get(obj) || 0;
count++;
countMap.set(obj, count);
console.log(count);
};
}
- 这里状态
count不直接存在闭包内,而是存在WeakMap中,绑定到外部对象obj - 当闭包不再引用
obj时,该键值对会被自动回收,防止内存泄漏
四、应用场景及优势
- 适合管理大量实例私有状态,避免闭包链过长导致内存长驻
- 在类或函数组件中,存储不方便直接挂载的私有数据
- 减少因闭包持有大量变量导致的内存占用,提升性能
8. 写出以下代码的输出顺序并解释:
setTimeout(() => console.log(1), 0);
Promise.resolve().then(() => console.log(2));
console.log(3);
题目要点
- 同步代码先执行,立即输出
3 - Promise.then 属于微任务,执行优先级高于setTimeout的宏任务
- setTimeout 属于宏任务,最后执行
- 事件循环机制确保先清空微任务队列再执行下一个宏任务
- 理解微任务与宏任务的调度顺序是分析异步输出顺序的关键
参考答案
考察点
- 理解JavaScript事件循环(Event Loop)机制
- 掌握宏任务(Macrotask)和微任务(Microtask)的区别及执行顺序
- 能分析异步代码的执行流程及输出顺序
参考答案
一、代码输出顺序
setTimeout(() => console.log(1), 0);
Promise.resolve().then(() => console.log(2));
console.log(3);
输出顺序:
3
2
1
二、原因解析
console.log(3) 是同步代码,立即执行,第一步输出 3。
Promise.resolve().then(...) 注册了一个微任务(微任务队列),不会立即执行,而是等待当前同步代码执行完毕后执行。
setTimeout(..., 0) 注册了一个宏任务,宏任务队列中排队等待执行,且会在微任务执行完后才执行。
执行顺序遵循事件循环模型:
- 先执行同步代码(输出3)
- 执行所有微任务(Promise.then,输出2)
- 执行宏任务(setTimeout,输出1)
9. new操作符内部执行步骤
题目要点
new创建空对象并设置原型链- 构造函数内部
this指向该对象执行初始化 - 返回非对象类型则返回新对象,返回对象类型则以返回对象为准
new实现了构造函数实例化和继承机制的结合
参考答案
考察点
- 理解JavaScript中
new操作符的底层执行流程
- 掌握构造函数实例化对象的原理
- 理解
new与函数调用的区别及其对this的绑定机制
- 能解释构造函数返回值对
new结果的影响
参考答案
一、new操作符的内部执行步骤
创建一个空对象
- 创建一个全新的空对象,作为将要返回的实例对象
设置原型链
- 将新对象的内部
[[Prototype]](即__proto__)指向构造函数的prototype属性
- 实现继承构造函数原型上的属性和方法
绑定函数执行上下文
- 将构造函数内部的
this指向新创建的对象
- 执行构造函数代码,给新对象添加属性或方法
返回对象
- 如果构造函数返回了一个对象类型(非基本类型),则返回该对象
- 否则,返回步骤1创建的新对象
二、举例说明
function Person(name) {
this.name = name;
return { custom: 'object' }; // 返回对象,则new结果是此对象
}
const p = new Person('Tom');
console.log(p); // { custom: 'object' }
function Animal(name) {
this.name = name;
return 123; // 返回基本类型,忽略返回值
}
const a = new Animal('Cat');
console.log(a.name); // 'Cat'
三、new的关键点总结
- 创建新对象且继承构造函数原型链
this绑定新对象执行构造函数- 返回值规则(对象类型覆盖默认返回)
10. Vue3中reactive()与ref()的区别?为何需要ref包装基本类型
题目要点
reactive 基于 Proxy 实现,深度代理对象,实现复杂对象响应式ref 用于包装基本类型,实现响应式的包装对象,访问需用 .value- 基本类型无法用
reactive 代理,必须用 ref 包装 - 组合式API中,
ref 适合单值管理,reactive 适合对象管理 - 设计目的是保证响应式系统能统一追踪和更新数据变化
参考答案
考察点
- 理解 Vue3 响应式核心API
reactive 和 ref 的区别与设计初衷
- 掌握响应式数据的实现机制及底层原理
- 理解基本类型响应式的特殊处理原因及解决方案
- 能结合实际项目场景,说明两者的使用时机和注意点
参考答案
一、reactive() 与 ref() 的核心区别
reactive()
- 用于将一个对象(包括数组、普通对象)包装成响应式对象
- 通过
Proxy 实现深度响应式,拦截所有属性访问和修改
- 访问属性时直接操作响应式对象,无需
.value 访问
- 不支持基本类型(如字符串、数字、布尔)作为参数,传入基本类型会原样返回,不具备响应式
ref()
- 用于包装单一基本类型或对象,返回一个包含
.value 属性的响应式引用
- 通过内部对象包装,实现对基本类型的响应式支持
- 使用时需要通过
.value 访问或赋值
- 对象类型作为参数时,
ref 只做浅包装,不会递归转换为响应式对象
二、为何需要 ref 包装基本类型
- 基本类型(如字符串、数字、布尔值)是不可被
Proxy 直接代理的
reactive 是基于 Proxy 的对象代理机制,无法监听基本类型的变化
ref 使用了一个包裹对象,利用对象的属性访问拦截,实现对基本类型的响应式包装
- 通过
.value 访问,保证了响应式系统能正确追踪和触发依赖更新
三、使用场景与注意点
场景 适用API 访问方式 说明 响应式对象(复杂数据) reactive直接访问属性 深度响应式,适合大多数对象数据 响应式基本类型数据 ref通过 .value 访问 只能用 ref 包装,方便响应式追踪 组合式API中单值响应式管理 ref.value方便解构、传递与监听单值
四、示例代码
import { reactive, ref } from 'vue';
const state = reactive({ count: 0 }); // 对象响应式
state.count++; // 响应式更新
const num = ref(0); // 基本类型响应式
num.value++; // 响应式更新
const objRef = ref({ name: 'Tom' });
console.log(objRef.value.name); // 访问对象的属性
11. 依赖收集时为何用WeakMap存储target->deps
题目要点
- 依赖收集存储
target -> deps 关系核心是管理响应式对象和副作用绑定
WeakMap 的键为弱引用,避免强引用导致的内存泄漏
- 响应式对象不再使用时,
WeakMap条目可自动被垃圾回收
- 普通
Map 会造成响应式对象无法释放,易导致内存泄漏
- 使用
WeakMap 是响应式系统设计中的内存安全和性能保障
参考答案
考察点
- 理解依赖收集在响应式系统中的核心作用
- 掌握响应式系统中数据结构设计原理及内存管理策略
- 理解 JavaScript 中
WeakMap 的特点及其在依赖管理中的应用
- 能结合Vue等框架实际场景,说明为何使用
WeakMap 而非普通 Map
参考答案
一、依赖收集简介
- 响应式系统通过“依赖收集”追踪数据(target)与副作用(effects)之间的关系
- 当数据变化时,能通知相关副作用重新执行,保持视图与数据同步
- 依赖关系通常存储为:
target(响应式对象) -> key(属性名) -> deps(依赖的副作用函数集合)
二、为何使用 WeakMap 存储 target -> deps
- 避免内存泄漏
- 响应式对象
target 作为键,副作用作为值
WeakMap 的键是弱引用,不阻止垃圾回收
- 当某个响应式对象不再被引用时,
WeakMap 中对应的条目会被自动回收,避免内存泄漏
- 性能优化
- 不必手动清理依赖,减少维护成本和复杂度
- 设计契合
- 响应式对象是引用类型,适合做
WeakMap 键
WeakMap 无法枚举键,天然保证了依赖管理的封装性和安全性
三、普通 Map 的不足
- 使用普通
Map 会导致键对象被强引用
- 响应式对象即使不再使用,也无法被回收,造成内存泄漏
- 必须额外编写清理逻辑,增加复杂度和出错风险
四、总结
- 依赖收集中的
target 对象生命周期与依赖关系绑定
- 利用
WeakMap 的弱引用特性,自动管理内存
- 保证响应式系统既高效又安全
12. 说说vue中,key的原理
题库原题:说说vue中,key的原理
题目要点
在 Vue 中,key 是用于帮助 Vue 识别和跟踪虚拟 DOM 的变化的特殊属性。当 Vue 更新渲染真实 DOM 时,它使用 key 属性来比较新旧节点,并尽可能地复用已存在的真实 DOM 节点,以提高性能。
参考答案
在 Vue 中,key 是用于帮助 Vue 识别和跟踪虚拟 DOM 的变化的特殊属性。当 Vue 更新渲染真实 DOM 时,它使用 key 属性来比较新旧节点,并尽可能地复用已存在的真实 DOM 节点,以提高性能。
Vue 在进行虚拟 DOM 的 diff 算法时,会使用 key 来匹配新旧节点,以确定节点的更新、移动或删除。它通过 key 属性来判断两个节点是否代表相同的实体,而不仅仅是根据它们的内容是否相同。这样可以保留节点的状态和避免不必要的 DOM 操作。
key 的工作原理如下:
- 当 Vue 更新渲染真实 DOM 时,它会对新旧节点进行比较,找出它们之间的差异。
- 如果两个节点具有相同的
key 值,则 Vue 认为它们是相同的节点,会尝试复用已存在的真实 DOM 节点。 - 如果节点具有不同的
key 值,Vue 会将其视为不同的节点,并进行适当的更新、移动或删除操作。
使用 key 可以提供更准确的节点识别和跟踪,避免出现一些常见的问题,比如在列表中重新排序时导致的元素闪烁、输入框内容丢失等。
key 必须是唯一且稳定的,最好使用具有唯一标识的值,例如使用数据的唯一 ID。同时,不推荐使用随机数作为 key,因为在每次更新时都会生成新的 key,导致所有节点都重新渲染,无法复用已有的节点,降低性能。
13. 详细讲一下vue的diff算法
题目要点
- Vue Diff通过同层对比新旧虚拟DOM,实现最小DOM更新
- 采用双端指针策略快速处理列表头尾节点
- 通过Key建立映射,实现节点复用和移动优化
- 深度递归比较节点属性和子节点,细粒度更新
- 多种优化策略(跳过静态节点、缓存等)提升性能
- 复杂度由
O(n³)降为O(n),确保实际高效运行
参考答案
考察点
- 理解虚拟DOM中Diff算法的核心原理
- 掌握Vue中Diff算法的具体实现流程及优化手段
- 能分析Diff算法的性能瓶颈及其应对策略
- 熟悉Diff算法在组件更新和渲染中的实际应用场景
参考答案
一、Diff算法核心原理
定义
Diff算法是虚拟DOM核心技术,用于比较新旧虚拟DOM树的差异,从而最小化实际DOM操作,实现高效渲染。
目标
通过比较,找到最少的节点变更,避免全量重绘,提高性能。
关键思想
- 只对比同一层级的节点,跨层比较复杂度高,Vue和React都遵循此原则。
- 利用节点的唯一标识(Key)进行快速定位和复用。
二、Vue的Diff算法实现流程
同层比较
- 只比较同一级的子节点,递归比较节点树结构。
类型判断
- 如果新旧节点类型不同,直接销毁旧节点,创建新节点。
Key与无Key比较策略
- 有Key列表
- 使用双端指针策略快速匹配前后端节点(头尾指针)。
- 通过Key构建映射表,快速定位新节点在旧节点中的位置,实现移动和复用。
- 无Key列表
- 按顺序比较,对比对应索引位置的节点,新增或删除尾部节点。
具体算法细节
双端指针优化
- 用四个指针指向新旧节点的头尾,逐步往中间推进。
- 依次比较头头、尾尾、头尾、尾头节点,若匹配则移动指针。
- 通过这种方式快速定位节点复用或移动,减少查找。
中间节点处理
- 处理完双端指针后剩余的中间节点,通过Key建立映射表。
- 新节点遍历映射旧节点,决定是复用、移动还是新增。
递归深度对比
- 对比节点属性、事件监听、文本内容等,更新发生变化的部分。
- 对子节点递归执行Diff。
三、Diff算法的优化策略
避免不必要的DOM操作
- 复用旧节点,减少销毁和创建。
- 最小化移动操作,性能更优。
双端指针减少查找次数
- 在大量列表更新时显著提升性能。
使用Key提高准确性和效率
- Key提供节点稳定身份,避免错误复用。
跳过静态节点
- Vue3编译时会标记静态节点,避免每次更新时重复比较。
缓存节点和Patch策略
- 针对组件和Fragment等结构,采用不同的Patch策略。
四、应用场景
- 组件更新时,确保状态改变只影响对应部分DOM。
- 列表渲染(
v-for)中的节点复用和移动。
- 动态节点插入、删除、替换。
五、Diff算法复杂度分析
- 传统树的Diff算法复杂度为
O(n³),不可行。
- Vue采用同层比较和Key映射,复杂度降至
O(n)。
- 双端指针优化进一步减少实际查找次数。
14. 讲一下Vue3从到DOM渲染的完整流程
题目要点
- 模板字符串解析成AST
- AST编译成渲染函数,生成VNode树
- 渲染函数执行时收集响应式依赖
- 生成新虚拟DOM,进行Diff与旧DOM比较
- 最小化真实DOM操作,进行增量更新
- 静态节点提升和指令优化提升性能
- 响应式数据变化触发更新,保持视图一致
参考答案
考察点
- 掌握 Vue3 编译与渲染的整体流程
- 理解模板编译、虚拟DOM生成与渲染机制
- 了解响应式系统在渲染中的作用
- 熟悉从模板到真实DOM的转换细节与优化点
参考答案
一、Vue3从 <template> 到 DOM 渲染的完整流程
模板解析(Template Parsing)
- Vue3 编译器读取单文件组件(SFC)中的
<template> 模板字符串。
- 将模板字符串解析成抽象语法树(AST)。
- 处理指令(如
v-if、v-for)、插值、事件绑定等,构建树状结构。
模板编译(Template Compilation)
- 将 AST 转换为渲染函数(render function)。
- 编译器生成的渲染函数是一个返回虚拟DOM节点(VNode)的函数,使用 Vue3 的虚拟DOM API。
- 编译阶段进行优化:静态节点标记、事件绑定优化、代码分块等。
响应式依赖收集(Reactive Dependency Collection)
- Vue3 的响应式系统基于 Proxy。
- 组件实例创建时,会执行渲染函数,读取响应式数据,触发依赖收集。
- 这样数据变化时,能精准触发对应组件重新渲染。
虚拟DOM生成(VNode Creation)
- 渲染函数执行,生成虚拟DOM树(VNode树)。
- VNode是一个描述DOM节点类型、属性、子节点的JS对象。
- 该过程是纯函数式,不操作真实DOM,性能开销较小。
虚拟DOM与真实DOM同步(Patch过程)
- Vue3 根据上次渲染的旧VNode树与本次新VNode树做Diff对比。
- 通过Diff算法计算出最小变更集(插入、更新、删除)。
- 进行真实DOM的增量更新,避免全量重绘。
DOM渲染与挂载
- 新生成或更新的真实DOM节点挂载到页面指定容器中。
- 完成用户可见的UI更新。
后续更新
- 响应式数据变化后,重新执行渲染函数生成新VNode,触发Patch流程。
- 维持界面和数据状态同步。
二、流程中的关键优化点
- 静态节点提升:静态节点不参与后续重新渲染,减少无用计算。
- 指令预编译和事件缓存:减少运行时开销。
- 碎片化渲染:支持Fragment,减少额外DOM层级。
- 高效Diff算法:双端指针、Key优化,确保复杂界面快速更新。
15. beforeUpdate和updated钩子中修改数据会引发什么问题?如何解决
题目要点
- 在
beforeUpdate 和 updated 钩子修改响应式数据会触发无限更新循环
- 无限循环原因是钩子修改数据触发新一轮更新,钩子再次执行
- 导致浏览器卡死、性能严重下降
- 避免在这两个钩子中直接修改数据,或者使用异步方式延迟修改
- 优选在合适的生命周期或业务事件中修改数据,确保渲染流程稳定
参考答案
考察点
- 理解 Vue 生命周期钩子的执行时机和机制
- 掌握在
beforeUpdate 和 updated 钩子中修改数据带来的副作用
- 能说明引发无限更新循环或渲染异常的原因
- 了解避免此类问题的最佳实践和解决方案
参考答案
一、生命周期钩子简介
beforeUpdate:组件数据更新,虚拟DOM重新渲染之前调用
updated:组件更新完成,真实DOM渲染完成后调用
二、在 beforeUpdate 和 updated 中修改数据的问题
引发无限渲染循环
- 修改响应式数据会触发视图重新渲染
- 钩子中修改数据导致数据变更,触发更新,钩子再次执行
- 形成死循环,浏览器卡顿甚至崩溃
渲染异常与性能问题
- 不断重复渲染增加性能开销
- UI状态可能不稳定,用户体验差
三、产生问题的根本原因
- Vue的响应式系统是“脏检查”机制
- 钩子执行时修改数据,Vue监听到变化,自动重新触发更新
beforeUpdate 和 updated 钩子处于更新周期内,频繁触发更新导致无限循环
四、解决方案与最佳实践
- 避免在这两个钩子中直接修改响应式数据
- 若必须修改,确保修改是幂等且不会引起额外变化
- 将修改操作延迟到下一个事件循环(
nextTick)之外执行
- 例如使用
this.$nextTick(() => { ... }) 或 setTimeout
- 将数据修改放在更合适的生命周期钩子中,如
mounted 或事件回调中
- 使用状态管理或业务逻辑层分离,避免在渲染钩子中写业务逻辑
- 监控渲染性能,避免不必要的数据更新
16. 讲一下你在项目中如何使用Chrome DevTools Performance面板分析FPS卡顿的
题目要点
- 利用 Performance 面板录制并捕获性能数据
- 观察 FPS 曲线,定位帧率低下的时间段
- 通过主线程时间轴和火焰图分析长任务与瓶颈函数
- 重点关注重排、重绘、合成及JS执行耗时
- 结合渲染机制调整DOM操作和动画实现方式
- 采用异步拆分任务和GPU加速优化渲染效率
参考答案
考察点
- 熟悉 Chrome DevTools Performance 面板的使用方法
- 理解浏览器渲染流程及影响 FPS 的关键环节
- 能分析帧率卡顿的具体原因(如长任务、重排重绘等)
- 掌握性能瓶颈定位和优化思路
参考答案
一、Chrome DevTools Performance 面板简介
- Performance 面板用于捕获网页的性能数据,展示 CPU 时间线、帧率(FPS)、事件分布等
- 能分析脚本执行、样式计算、布局、绘制、合成等阶段耗时情况
- 通过录制一段操作过程,捕获渲染性能快照
二、使用流程
开启性能录制
- 打开 DevTools,切换到 Performance 面板
- 点击“Record”按钮,模拟或复现页面卡顿场景
- 结束录制,生成性能分析报告
查看 FPS 曲线
- 观察顶部 FPS 图,理想帧率是 60 FPS(每帧 ~16.6ms)
- 低帧率区域标红,重点关注这些区域
分析主线程耗时
- 在 Main Thread 时间轴中查看长任务(黄色条)
- 重点检查 JavaScript 执行、样式计算(Recalculate Style)、布局(Layout)、绘制(Paint)时间
- 长任务导致浏览器无法及时刷新,FPS 下降
检查渲染阻塞原因
- 通过 Flame Chart(火焰图)查看调用栈,定位具体函数或操作
- 识别是否存在大量同步DOM操作、频繁触发重排或重绘
查看合成层与GPU活动
- 关注 Composite Layers 阶段,判断是否合理使用了 GPU 加速
- 查看是否存在不必要的图层合成或频繁触发合成导致开销大
捕获事件与帧边界
- 对比用户输入事件(如滚动、点击)与帧渲染时间,判断响应延迟
- 确认是否有任务阻塞帧边界导致卡顿
三、项目中定位FPS卡顿的实例步骤
- 录制一段页面滚动或动画的过程
- 观察 FPS 下降时间点,锁定主线程长任务
- 分析火焰图定位耗时函数,找到性能瓶颈(如大量 DOM 操作或复杂计算)
- 配合代码审查,确认是否存在同步阻塞操作
- 针对重排重绘优化,如减少DOM修改频率、使用transform代替位移等
- 优化 JS 执行效率,异步拆分大任务
- 使用 GPU 加速合理拆分图层,减少合成压力
17. requestAnimationFrame优化动画?对比CSS动画的GPU加速原理
题目要点
- RAF 是浏览器刷新同步的动画回调,能减少丢帧和卡顿
- CSS动画借助合成层实现GPU加速,避免重排重绘,提高性能
- RAF灵活但依赖JS执行,CSS动画性能更优但灵活性有限
- 两者根据需求场景合理选择与配合使用
- 现代动画优化中,推荐用CSS动画做轻量效果,用RAF做复杂动画控制
参考答案
考察点
- 理解
requestAnimationFrame(RAF)的工作机制及优势
- 掌握如何使用 RAF 优化动画性能
- 了解 CSS 动画的 GPU 加速原理及其区别
- 能比较 JS 动画与 CSS 动画的优劣及使用场景
参考答案
一、requestAnimationFrame 优化动画原理
requestAnimationFrame 是浏览器提供的接口,用于在浏览器下一次重绘前执行回调函数。
- 它能保证动画回调在浏览器的刷新周期(一般60FPS,即16.6ms)内执行,避免不必要的多余帧更新。
- 使用 RAF 可以让动画与浏览器渲染同步,减少掉帧、卡顿现象。
- RAF 具有自动节流功能:当标签页不可见时,RAF 会暂停执行,节省资源。
- 典型用法:
function animate() {
// 更新动画状态,比如元素位置
updateElementPosition();
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
#### 二、CSS动画的GPU加速原理
* CSS 动画(如 `transform`, `opacity`)通常会触发 GPU 加速。
* 浏览器会将这类动画单独放到合成层(Composite Layer)进行处理,利用 GPU 进行图层合成,避免触发主线程中的重排(reflow)和重绘(repaint)。
* 这样,动画只需合成图层,不改变布局或绘制,性能开销小,流畅度高。
* 典型GPU加速的CSS属性有:`transform`, `opacity`, `filter` 等。
#### 三、对比分析
| 维度 | requestAnimationFrame (JS 动画) | CSS 动画 (GPU 加速) |
| ----- | ----------------------------- | --------------- |
| 执行机制 | JS 脚本控制,每帧执行回调,需手动更新状态 | 浏览器声明式执行,底层自动优化 |
| 渲染同步 | 与浏览器刷新同步,减少丢帧 | 直接触发合成层,绕过布局绘制 |
| 性能 | 较好,但大量 JS 计算依然可能阻塞主线程 | 极高性能,轻量,适合常见动画 |
| 控制灵活度 | 灵活,可执行复杂逻辑 | 灵活性受限于 CSS 动画能力 |
| 兼容性 | 所有现代浏览器支持 | 所有现代浏览器支持 |
| 节流机制 | 自动节流,标签页隐藏时暂停 | 同样支持节流 |
#### 四、使用场景
* **`requestAnimationFrame` 适合**:
* 需要复杂动画逻辑、物理模拟、动画与业务逻辑高度耦合的场景
* 动态计算位置、动画帧控制等
* **CSS动画适合**:
* 简单的 UI 交互动效,如按钮点击反馈、元素进出场动画
* 需要高性能、低主线程占用的动画
</details>
## 18. 虚拟列表实现中如何计算渲染区间?滚动时如何避免布局抖动 {#question-subjective-d4c9eb523d3e}
### 题目要点
- 计算渲染区间基于 scrollTop、容器高度和单项高度加缓冲区<br>
- 不固定高度需维护高度缓存并结合二分查找定位索引<br>
- 抖动多因占位高度不准确和频繁重排导致<br>
- 通过占位元素撑起正确高度、设置缓冲区和减少同步布局避免抖动<br>
- transform 位移与异步渲染优化性能和体验
<details>
<summary>参考答案</summary>
### 考察点
- 理解虚拟列表(Virtual List)的核心思想及其性能优势<br>
- 掌握如何根据滚动位置和容器高度计算渲染区间(startIndex、endIndex)<br>
- 了解滚动过程中布局抖动(闪烁、跳动)产生的原因<br>
- 掌握避免布局抖动的技术手段和优化策略<br>
---
### 参考答案
#### 一、虚拟列表核心原理
- 虚拟列表通过只渲染视口内及附近一定范围内的列表项,避免渲染全部数据,极大降低DOM节点数量,提升性能。<br>
- 通过动态计算“渲染区间”,即当前需要渲染的起始和结束索引,实现按需渲染。<br>
#### 二、渲染区间的计算方法
1. **关键数据**<br>
- `scrollTop`:当前滚动容器的垂直滚动距离<br>
- `viewportHeight`:容器可视高度<br>
- `itemHeight`:单个列表项固定高度(或估算平均高度)<br>
- `buffer`:预渲染的额外项数,防止快速滚动时白屏<br>
2. **基本计算**<br>
- 起始索引 `startIndex = Math.floor(scrollTop / itemHeight) - buffer`,不能小于0<br>
- 结束索引 `endIndex = Math.ceil((scrollTop + viewportHeight) / itemHeight) + buffer`,不能超过总条数<br>
3. **动态高度处理**<br>
- 若列表项高度不固定,需维护每项高度数组,累计计算滚动偏移<br>
- 结合二分查找快速定位当前滚动位置对应的起始项索引<br>
4. **渲染时**<br>
- 仅渲染 `[startIndex, endIndex]` 区间内的元素<br>
- 通过占位元素(padding 或 margin)撑开总高度,保持滚动条准确<br>
#### 三、滚动时布局抖动产生原因
- 由于动态渲染元素高度计算不准确,导致占位高度与实际渲染元素高度不符<br>
- 频繁增减 DOM 节点引起的重排,造成滚动跳动或闪烁<br>
- 直接操作 DOM 或频繁触发同步布局,导致渲染卡顿和界面抖动<br>
#### 四、避免布局抖动的优化方案
1. **准确计算占位高度**<br>
- 通过缓存每个元素真实高度,动态调整占位容器高度<br>
- 使用占位容器(如上、下 padding)保持滚动高度稳定<br>
2. **合理设置缓冲区(buffer)**<br>
- 预渲染上下多余项,平滑快速滚动,减少白屏<br>
3. **减少同步布局触发**<br>
- 避免频繁读取布局属性(如offsetHeight),减少重排<br>
- 使用异步手段(如 requestAnimationFrame)批量处理渲染更新<br>
4. **使用transform位移代替修改top/scrollTop**<br>
- transform GPU 加速且不触发重排,减少抖动<br>
5. **懒加载图片和资源,防止加载时尺寸变化**
</details>
## 19. 动态导入(`import()`)如何影响Chunk生成?如何预加载分割的Chunk {#question-subjective-5b01366a776a}
### 题目要点
* 动态导入触发构建工具代码拆分,生成独立Chunk文件
* Chunk命名可通过魔法注释自定义,便于缓存和调试
* 预加载(preload)抢占式加载关键Chunk,预取(prefetch)空闲时加载未来可能用的Chunk
* 合理使用预加载/预取提升体验,避免无谓资源占用
* 适用于路由懒加载、模块按需加载等场景,降低首屏体积,提升加载速度
<details>
<summary>参考答案</summary>
### 考察点
- 理解动态导入(`import()`)的工作机制及其对代码分割的影响<br>
- 掌握动态导入如何触发Chunk(代码块)自动拆分<br>
- 熟悉Webpack或类似构建工具中Chunk的生成与命名策略<br>
- 了解如何使用预加载(preload)和预取(prefetch)技术优化分割Chunk的加载时机和性能<br>
---
### 参考答案
#### 一、动态导入(`import()`)及Chunk生成原理
- `import()` 是ES提案中的动态模块加载语法,返回一个Promise,异步加载对应模块。<br>
- 构建工具(如Webpack)检测到 `import()` 后,会将被动态导入的模块及其依赖单独打包成一个Chunk(代码块)。<br>
- 动态导入实现了代码按需加载,减小首屏包体积,提高页面初始加载速度。<br>
- 每次 `import()` 调用都会对应一个单独的Chunk文件(如果模块被多处动态导入,构建工具会合并成一个Chunk)。<br>
#### 二、Chunk生成细节与命名
- Webpack默认以模块路径和内容哈希生成Chunk名称,方便缓存管理。<br>
- 可通过魔法注释自定义Chunk名称,如:<br>
```js
import(/* webpackChunkName: "myChunk" */ './moduleA')
- 这种命名帮助在网络请求中更易识别和调试,也便于缓存策略配置。
三、预加载与预取Chunk
预加载(Preload)
通过 <link rel="preload" as="script" href="chunk.js">,浏览器提前加载Chunk资源,保证后续动态导入时能快速响应。
预加载适用于即将用到的重要资源,通常优先级较高。
Webpack支持通过魔法注释自动生成预加载标签,例如:
import(/* webpackPreload: true */ './moduleA')
预取(Prefetch)
通过 <link rel="prefetch" href="chunk.js">,浏览器在空闲时加载资源,准备未来可能需要的代码。
预取优先级低,不会影响当前页面性能,适合用户后续操作可能触发的模块。
Webpack对应魔法注释:
import(/* webpackPrefetch: true */ './moduleA')
区别总结
特性 预加载 (Preload) 预取 (Prefetch) 加载时机 尽快加载,抢占带宽 空闲时加载,低优先级 使用场景 页面当前或马上需要的模块 未来可能会用到的模块 浏览器行为 会阻塞其他资源加载 不阻塞,异步后台加载
四、使用场景与注意事项
- 动态导入减少首屏体积,提高性能,适合大型应用和路由懒加载场景。
- 通过预加载保证关键动态Chunk在用户操作前已下载,提升交互流畅度。
- 预取减少用户等待,但不能保证即时可用,适合预测用户后续行为。
- 预加载和预取结合使用,需合理评估网络环境,避免资源浪费和带宽竞争。
20. HTTPS 加密算法和加解密过程是啥?
题库原题:HTTPS 加密算法和加解密过程是啥?
题目要点
HTTPS 通过结合非对称加密和对称加密来确保通信的安全。非对称加密用于握手过程中的身份验证和密钥交换,而对称加密则用于实际的数据传输。哈希算法用于确保数据的完整性。这个加解密过程确保了数据在客户端和服务器之间的安全传输。
参考答案
HTTPS 使用 SSL/TLS 协议来加密传输的数据,确保数据的机密性和完整性。HTTPS 的加解密过程主要包括以下几个步骤和算法:
1. 加密算法和密钥类型
- 对称加密算法:使用相同的密钥进行加密和解密。常见的对称加密算法包括 AES(Advanced Encryption Standard)和 3DES(Triple Data Encryption Standard)。
- 非对称加密算法:使用一对密钥(公钥和私钥),公钥用于加密,私钥用于解密。常见的非对称加密算法包括 RSA(Rivest-Shamir-Adleman)和 ECC(Elliptic Curve Cryptography)。
- 哈希算法:用于生成数据的摘要,以确保数据完整性。常见的哈希算法包括 SHA-256(Secure Hash Algorithm 256-bit)和 MD5(Message Digest Algorithm 5,虽然 MD5 不再安全)。
2. SSL/TLS 握手过程
握手阶段
客户端发起连接
- 客户端发送一个 “ClientHello” 消息,包含客户端支持的 TLS 版本、加密套件(加密算法组合)、压缩方法和其他信息。
服务器响应
- 服务器回应一个 “ServerHello” 消息,选择客户端支持的加密套件和 TLS 版本。
- 服务器发送其数字证书给客户端。数字证书包含服务器的公钥,由证书颁发机构(CA)签名。
证书验证
- 客户端验证服务器的数字证书的有效性和真实性。如果证书有效,客户端继续执行。
- 客户端生成一个随机的“pre-master secret”并用服务器的公钥加密,然后发送给服务器。这个“pre-master secret”将在后续的步骤中用于生成对称加密密钥。
密钥交换
- 服务器使用其私钥解密“pre-master secret”并生成一个对称加密密钥(session key)。
- 客户端和服务器使用“pre-master secret”生成相同的对称加密密钥(session key),用于加密后续的通信数据。
完成握手
- 双方用对称加密密钥加密并交换“Finished”消息,表示握手过程完成。
- 从这一点开始,客户端和服务器使用对称加密密钥加密和解密数据。
3. 数据加密和传输
- 对称加密:客户端和服务器使用之前生成的对称加密密钥来加密和解密数据。对称加密算法确保数据在传输过程中是机密的。
- 数据完整性:除了加密,TLS 还使用消息认证码(MAC)来确保数据在传输过程中没有被篡改。常见的 MAC 算法包括 HMAC(Hash-based Message Authentication Code)。
4. 连接关闭
- 优雅关闭:当通信完成时,客户端和服务器通过发送“close_notify”消息来优雅地关闭连接,确保所有的数据都被正确传输。
常见的加密套件
在 TLS 握手过程中,客户端和服务器会协商一个加密套件(cipher suite),这是一个加密算法的组合。常见的加密套件包括:
- TLS_AES_128_GCM_SHA256:使用 AES 进行对称加密,GCM(Galois/Counter Mode)用于加密模式,SHA-256 用于消息认证。
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:使用 ECDHE(Elliptic Curve Diffie-Hellman Ephemeral)进行密钥交换,RSA 进行身份验证,AES-256-GCM 进行对称加密,SHA-384 进行消息认证。
21. css模块化
题目要点
- CSS模块化解决样式全局污染和冲突问题
- 常用方案:CSS Modules、BEM、Scoped CSS、CSS-in-JS
- 模块化提高样式复用和可维护性,适合组件化开发
- 实践中需注意命名规范和构建支持
参考答案
考察点
- 理解CSS模块化的概念及目的
- 掌握常见的CSS模块化实现方案(如CSS Modules、BEM、Scoped CSS)
- 理解CSS模块化在大型项目中的实际应用价值
- 熟悉如何避免样式冲突、提升样式复用性和维护性
参考答案
一、CSS模块化的核心概念
- CSS模块化是将样式进行拆分、封装,避免全局命名冲突和样式污染,提升代码可维护性和复用性。
- 通过模块边界限定样式作用范围,减少副作用,提高团队协作效率。
二、常见的CSS模块化实现方式
CSS Modules
- 利用构建工具(如Webpack)支持,将CSS文件作为模块导入。
- 通过自动生成唯一的类名哈希,保证样式局部作用域,避免全局污染。
- 示例:
import styles from './Button.module.css';
<button className={styles.primary}>Click</button>
```<br>
- 优点:完全避免命名冲突,写法自然。
BEM(块-元素-修饰符)命名规范
- 通过规范的类名命名规则,区分模块内块(block)、元素(element)和修饰符(modifier)。
- 例:
.button, .button__icon, .button--primary
- 优点:语义清晰,便于多人协作,纯CSS,无需额外工具支持。
Scoped CSS(如Vue的<style scoped>)
- 框架层面支持,将样式限定于当前组件,自动加属性选择器。
- 优点:简单方便,适合组件化开发。
CSS-in-JS
- 通过JS管理样式,动态生成类名和样式规则(如styled-components、Emotion)。
- 优点:样式和逻辑紧密结合,支持动态样式。
三、CSS模块化的应用价值
- 避免全局样式冲突和覆盖,减少调试难度。
- 促进组件独立和复用,提升团队开发效率。
- 有利于按需加载和样式隔离,优化性能。
- 提升代码可维护性,特别是大型复杂项目。
四、实践中注意点
- 对已有项目引入模块化需渐进式改造,避免大量样式失效。
- 保持命名规范,避免无意义的类名。
- 注意构建配置支持,确保模块化样式正确加载。
- 与设计系统结合,统一样式标准。
题目要点
<header>是HTML5语义化标签,明确头部区域含义- 语义化标签有助于SEO和辅助技术识别
- 提高代码可读性和维护性,减少依赖类名赋予语义
- 推荐优先使用语义标签代替无语义的
<div>+类名组合
参考答案
考察点
- 理解HTML语义化标签的重要性及作用
- 掌握语义化标签对SEO和无障碍的影响
- 了解语义化标签在开发协作和维护中的优势
参考答案
一、语义化标签的核心概念
- HTML5引入了如
<header>、<nav>、<article>、<footer>等语义化标签,明确表达页面结构和内容含义 - 语义化标签比普通
<div>搭配类名更具意义,利于浏览器、搜索引擎、辅助设备等理解页面
二、使用 <header> 的优势
明确语义
<header>表示页面或区块的头部区域,包含标题、导航等信息- 比
<div class="header">直接通过类名赋予语义更标准、易识别
提升可访问性
- 语义化标签被屏幕阅读器等辅助技术识别,帮助视障用户理解页面结构
<header>使辅助设备快速定位页眉内容,提升用户体验
- 语义化标签被屏幕阅读器等辅助技术识别,帮助视障用户理解页面结构
SEO友好
- 搜索引擎利用语义标签准确抓取页面结构和重点内容
- 语义标签利于内容分类和权重判断,可能提高搜索排名
- 搜索引擎利用语义标签准确抓取页面结构和重点内容
代码规范与维护
- 语义化标签增强代码自解释性,方便团队协作和代码维护
- 减少对类名、ID等非结构性属性的依赖,代码更简洁清晰
- 语义化标签增强代码自解释性,方便团队协作和代码维护
三、使用场景对比
<header>适合定义页面或区块的头部区域<div class="header">仅作为普通容器,没有内置语义- 两者均可通过CSS样式控制外观,但语义化标签具备结构语义
2. SEO和可访问性有何影响
题目要点
- 语义化标签提升SEO抓取效率和内容权重分配
- 结构化页面内容,增强搜索引擎对重点信息的识别能力
- 语义标签是辅助技术识别页面结构的关键,提升可访问性
- 有助于残障用户快速导航和理解页面
- 符合无障碍标准,降低法律和合规风险
参考答案
考察点
- 理解SEO(搜索引擎优化)对网页结构和内容的重要性
- 掌握语义化标签对搜索引擎抓取的正面影响
- 了解可访问性(Accessibility)概念及辅助技术如何利用语义标签
- 能说明如何通过结构优化提升用户体验及业务价值
参考答案
一、SEO影响
语义化标签助力搜索引擎理解页面结构
- 搜索引擎依赖HTML结构判断内容层级和重点
- 使用
<header>、<article>、<nav>等标签,明确标识内容区块,提升爬虫抓取效率
- 搜索引擎依赖HTML结构判断内容层级和重点
提升关键词和内容相关性权重
- 语义标签让搜索引擎区分正文、导航、页脚等区域,避免误判
- 正确结构化内容帮助搜索引擎更精准地抓取和索引,提高排名可能性
- 语义标签让搜索引擎区分正文、导航、页脚等区域,避免误判
丰富搜索结果展示
- 结构化良好的页面更容易获得富媒体摘要(如站点链接、快速导航)
- 结构化良好的页面更容易获得富媒体摘要(如站点链接、快速导航)
二、可访问性影响
辅助技术依赖语义信息提供用户导航
- 屏幕阅读器、盲人设备通过语义标签识别页面结构,便于快速定位
<header>、<nav>等标签帮助用户了解页面布局和功能区
- 屏幕阅读器、盲人设备通过语义标签识别页面结构,便于快速定位
提升残障用户体验
- 语义清晰的页面减少理解和操作成本,提升交互效率和满意度
- 语义清晰的页面减少理解和操作成本,提升交互效率和满意度
符合法律法规和行业标准
- 许多国家和地区对无障碍有强制性要求,合理使用语义标签有助合规
3. 屏幕阅读器如何解析语义化标签
题目要点
- 屏幕阅读器依赖HTML语义化标签构建页面结构模型
- 语义化标签帮助划分区域、提升导航效率和内容理解
- 结合ARIA属性,辅助技术识别交互元素的角色和状态
- 非语义标签缺乏结构信息,降低辅助设备的可用性
- 合理使用语义化标签是提升无障碍体验的关键
参考答案
考察点
- 理解屏幕阅读器的工作原理及语义化标签的重要性
- 掌握语义化标签如何辅助辅助技术(如屏幕阅读器)识别页面结构
- 了解辅助技术与HTML结构的交互方式及用户体验影响
参考答案
一、屏幕阅读器的工作原理简述
- 屏幕阅读器通过解析网页的DOM结构和HTML标签,读取文本内容并以语音或盲文输出
- 其核心依赖网页的语义信息(标签、ARIA属性等)来构建页面的“语义树”
- 语义树反映页面结构,帮助用户理解内容层次、导航方式及控件功能
二、语义化标签在屏幕阅读器中的作用
明确页面结构
<header>、<nav>、<main>、<footer>等标签标识不同页面区域,屏幕阅读器据此划分内容块- 用户可以跳转到这些语义块,快速浏览和定位
提高内容可理解性
- 语义标签提供内容的上下文信息,比如
<article>表示独立文章,<section>表示逻辑区块 - 这有助于用户理解页面内容的组织方式
- 语义标签提供内容的上下文信息,比如
辅助交互控件识别
- 语义标签结合ARIA属性,帮助屏幕阅读器识别按钮、菜单、表单等交互元素
- 提供正确的角色(role)、状态(state)和属性(property)信息
- 语义标签结合ARIA属性,帮助屏幕阅读器识别按钮、菜单、表单等交互元素
三、实际解析流程
- 屏幕阅读器监听DOM变化,构建和维护一棵语义树
- 访问者使用快捷键导航时,屏幕阅读器基于语义树跳转对应标签区域
- 阅读顺序由语义结构决定,非语义标签(如无意义
<div>)无法提供有效导航点 - 开发者通过合理语义标签和ARIA属性增强用户体验
4. 浏览器解析HTML时遇到 <script defer> 和 <script async> 会如何影响渲染?如何优化
题目要点
defer延迟执行,保证执行顺序,不阻塞HTML解析async异步执行,执行顺序不可控,可能阻塞渲染- 优先用
defer加载依赖顺序脚本,async用于独立脚本 - 合理拆分脚本体积,配合网络优化提升性能
- 选择合适属性结合业务需求,达到最佳加载与渲染平衡
参考答案
考察点
- 理解
<script>标签中defer和async属性的区别 - 掌握浏览器HTML解析与脚本加载、执行的流程
- 了解不同脚本加载方式对页面渲染性能和用户体验的影响
- 熟悉合理使用
defer和async的优化策略
参考答案
一、<script defer> 和 <script async> 的核心概念
<script defer>:脚本会在HTML文档解析完成后,按顺序执行,保证脚本不会阻塞DOM的构建<script async>:脚本会在下载完成后立即执行(异步执行),不保证执行顺序,可能打断HTML解析
二、浏览器解析流程及影响
| 阶段 | 普通 <script> | <script defer> | <script async> |
|---|---|---|---|
| HTML解析 | 解析遇到脚本立即暂停,下载并执行脚本,阻塞渲染 | 解析时不阻塞,脚本异步下载,等文档解析完后执行 | 解析时不阻塞,脚本异步下载,下载完成立即执行 |
| 执行顺序 | 按出现顺序依次执行 | 按出现顺序依次执行 | 不保证顺序,哪个先下载完成先执行 |
| 页面渲染阻塞 | 可能阻塞,造成页面白屏 | 不阻塞HTML解析,减小渲染阻塞 | 可能在解析中断,影响渲染 |
三、优化建议
优先使用
defer加载顺序敏感的脚本- 确保脚本不会阻塞DOM构建
- 保持脚本执行顺序,避免因异步执行引发依赖问题
- 确保脚本不会阻塞DOM构建
使用
async加载独立、非依赖性脚本- 适合广告、分析、统计等独立功能脚本
- 提升加载效率,减少页面阻塞时间
- 适合广告、分析、统计等独立功能脚本
合理拆分脚本,避免单个大文件阻塞
- 利用代码拆分(Code Splitting)减少单个文件体积
- 配合
defer/async提高加载和渲染速度
- 利用代码拆分(Code Splitting)减少单个文件体积
结合其他性能优化手段
- 使用HTTP/2多路复用,提升脚本加载并发能力
- 合理设置缓存,减少重复加载
- 将非关键脚本延迟加载,保证首屏渲染速度
- 使用HTTP/2多路复用,提升脚本加载并发能力
5. CSS中的 “flex:1;” 是什么意思?
题目要点
在CSS中,flex: 1; 是一个简写属性,用于设置一个元素为Flexbox子项时的布局行为。具体来说,它包括以下几个部分:
- flex-grow:定义了当父容器有多余空间时,子项如何增长。flex-grow: 1 表示子项将尽可能多地占据可用空间。
- flex-shrink:定义了当父容器空间不足时,子项如何缩小。默认值是 1,表示子项可以等比例缩小以适应容器空间。
- flex-basis:定义了在分配多余空间之前,子项的默认大小。flex-basis: 1 通常意味着子项的初始大小是相对于父容器的1%(如果父容器设置了flex-basis)。
flex: 1; 通常用于确保Flexbox子项能够充满其父容器,同时在空间不足时能够适当缩小,保持布局的灵活性。
参考答案
flex 是 flex-grow, flex-shrink 和 flex-basis的简写。
除了auto (1 1 auto) 和 none (0 0 auto)这两个快捷值外,还有以下设置方式:
- 当 flex 取值为一个非负数字,则该数字为 flex-grow 值,flex-shrink 取 1,flex-basis 取 0%,如下是等同的:
.item {flex: 1;}
.item {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
}
- 当 flex 取值为 0 时,对应的三个值分别为 0 1 0%
.item {flex: 0;}
.item {
flex-grow: 0;
flex-shrink: 1;
flex-basis: 0%;
}
- 当 flex 取值为一个长度或百分比,则视为 flex-basis 值,flex-grow 取 1,flex-shrink 取 1,有如下等同情况(注意 0% 是一个百分比而不是一个非负数字)
.item-1 {flex: 0%;}
.item-1 {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
}
.item-2 {flex: 24px;}
.item-2 {
flex-grow: 1;
flex-shrink: 1;
flex-basis: 24px;
}
- 当 flex 取值为两个非负数字,则分别视为 flex-grow 和 flex-shrink 的值,flex-basis 取 0%,如下是等同的:
.item {flex: 2 3;}
.item {
flex-grow: 2;
flex-shrink: 3;
flex-basis: 0%;
}
- 当 flex 取值为一个非负数字和一个长度或百分比,则分别视为 flex-grow 和 flex-basis 的值,flex-shrink 取 1,如下是等同的:
.item {flex: 11 32px;}
.item {
flex-grow: 11;
flex-shrink: 1;
flex-basis: 32px;
}
6. 解释以下代码输出并说明内存风险:
function createCounter() {
let count = 0;
return function() { count++; console.log(count) };
}
const counter = createCounter();
题目要点
counter是闭包函数,持有对count变量的引用- 每次调用
counter()都会累加并输出count值 - 闭包导致变量生命周期延长,可能引发内存泄漏风险
- 需谨慎管理闭包,避免无谓占用大量内存
- 通过合理释放引用,防止闭包引起的内存泄漏
参考答案
考察点
- 理解闭包(Closure)及其实现原理
- 掌握闭包对变量生命周期的影响
- 识别闭包可能带来的内存泄漏风险及优化方法
- 理解JavaScript函数作用域与执行上下文的关系
参考答案
一、代码输出解释
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 输出 1
counter(); // 输出 2
createCounter返回了一个闭包函数,该函数引用了其外部作用域中的变量count- 每调用一次
counter(),count自增并输出当前值,体现了闭包对外部变量的持久引用 count不会在createCounter执行结束后销毁,因为闭包保持对它的引用
二、闭包的原理和作用
- 闭包是函数与其相关作用域环境的组合,允许函数访问定义时的作用域
- JavaScript中,函数执行时会形成执行上下文,变量在上下文中存活
- 返回的内部函数持续持有对外部变量的引用,防止变量被垃圾回收
三、内存风险分析
- 潜在内存泄漏:闭包会使其引用的变量长时间保留在内存,尤其当闭包持有大量数据或大量闭包被持续创建时
- 如果闭包中引用了大对象或DOM节点,且未及时释放,可能导致内存泄漏
- 反复创建闭包函数且不释放引用,会持续占用内存,影响性能
四、如何避免风险
- 合理设计闭包,避免闭包无意义地持有大量变量或对象
- 当闭包不再使用时,解除对变量的引用(如将闭包置为null)以辅助GC回收
- 注意避免闭包与DOM节点互相引用,导致循环引用
7. 如何用WeakMap重构避免内存泄漏?闭包中变量存储在堆的哪个区域
题目要点
- 闭包变量存储在堆内存,受引用关系影响生命周期
WeakMap利用弱引用特性,键对象无引用时自动回收- 通过
WeakMap存储闭包私有数据,避免闭包导致的内存泄漏 - 用外部对象作为键,数据与闭包解耦,方便管理和释放
- 适合复杂状态管理,提升内存效率和应用性能
参考答案
考察点
- 理解闭包变量的内存存储位置及生命周期
- 掌握使用
WeakMap管理闭包数据,防止内存泄漏的原理 - 理解JavaScript垃圾回收机制及弱引用的作用
- 能结合实际场景说明
WeakMap替代闭包变量存储的优化方案
参考答案
一、闭包中变量的内存存储位置
- JavaScript中,函数执行时创建执行上下文,局部变量存储在堆内存中
- 闭包使函数持续持有对外层作用域变量的引用,导致这些变量无法被垃圾回收
- 堆内存(Heap)是动态分配区域,用于存储对象和闭包捕获的变量,生命周期取决于引用关系
- 只要闭包存在,对应的变量就存活在堆中
二、WeakMap避免内存泄漏的原理
WeakMap的键是弱引用对象,不计入垃圾回收的根引用- 当键对象不再被引用时,
WeakMap中的键值对会自动被GC回收 - 适合存储和管理闭包私有数据,避免闭包对象持有过长生命周期
- 通过将闭包中状态或数据存储在
WeakMap,利用外部对象作为键,实现自动释放
三、用 WeakMap 重构闭包示例
传统闭包写法(存在闭包持久引用风险):
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
使用 WeakMap 存储私有状态:
const countMap = new WeakMap();
function createCounter() {
const obj = {}; // 用作WeakMap的键
countMap.set(obj, 0);
return function() {
let count = countMap.get(obj) || 0;
count++;
countMap.set(obj, count);
console.log(count);
};
}
- 这里状态
count不直接存在闭包内,而是存在WeakMap中,绑定到外部对象obj - 当闭包不再引用
obj时,该键值对会被自动回收,防止内存泄漏
四、应用场景及优势
- 适合管理大量实例私有状态,避免闭包链过长导致内存长驻
- 在类或函数组件中,存储不方便直接挂载的私有数据
- 减少因闭包持有大量变量导致的内存占用,提升性能
8. 写出以下代码的输出顺序并解释:
setTimeout(() => console.log(1), 0);
Promise.resolve().then(() => console.log(2));
console.log(3);
题目要点
- 同步代码先执行,立即输出
3 - Promise.then 属于微任务,执行优先级高于setTimeout的宏任务
- setTimeout 属于宏任务,最后执行
- 事件循环机制确保先清空微任务队列再执行下一个宏任务
- 理解微任务与宏任务的调度顺序是分析异步输出顺序的关键
参考答案
考察点
- 理解JavaScript事件循环(Event Loop)机制
- 掌握宏任务(Macrotask)和微任务(Microtask)的区别及执行顺序
- 能分析异步代码的执行流程及输出顺序
参考答案
一、代码输出顺序
setTimeout(() => console.log(1), 0);
Promise.resolve().then(() => console.log(2));
console.log(3);
输出顺序:
3
2
1
二、原因解析
console.log(3)是同步代码,立即执行,第一步输出3。Promise.resolve().then(...)注册了一个微任务(微任务队列),不会立即执行,而是等待当前同步代码执行完毕后执行。setTimeout(..., 0)注册了一个宏任务,宏任务队列中排队等待执行,且会在微任务执行完后才执行。执行顺序遵循事件循环模型:
- 先执行同步代码(输出3)
- 执行所有微任务(Promise.then,输出2)
- 执行宏任务(setTimeout,输出1)
9. new操作符内部执行步骤
题目要点
new创建空对象并设置原型链- 构造函数内部
this指向该对象执行初始化 - 返回非对象类型则返回新对象,返回对象类型则以返回对象为准
new实现了构造函数实例化和继承机制的结合
参考答案
考察点
- 理解JavaScript中
new操作符的底层执行流程 - 掌握构造函数实例化对象的原理
- 理解
new与函数调用的区别及其对this的绑定机制 - 能解释构造函数返回值对
new结果的影响
参考答案
一、new操作符的内部执行步骤
创建一个空对象
- 创建一个全新的空对象,作为将要返回的实例对象
- 创建一个全新的空对象,作为将要返回的实例对象
设置原型链
- 将新对象的内部
[[Prototype]](即__proto__)指向构造函数的prototype属性 - 实现继承构造函数原型上的属性和方法
- 将新对象的内部
绑定函数执行上下文
- 将构造函数内部的
this指向新创建的对象 - 执行构造函数代码,给新对象添加属性或方法
- 将构造函数内部的
返回对象
- 如果构造函数返回了一个对象类型(非基本类型),则返回该对象
- 否则,返回步骤1创建的新对象
- 如果构造函数返回了一个对象类型(非基本类型),则返回该对象
二、举例说明
function Person(name) {
this.name = name;
return { custom: 'object' }; // 返回对象,则new结果是此对象
}
const p = new Person('Tom');
console.log(p); // { custom: 'object' }
function Animal(name) {
this.name = name;
return 123; // 返回基本类型,忽略返回值
}
const a = new Animal('Cat');
console.log(a.name); // 'Cat'
三、new的关键点总结
- 创建新对象且继承构造函数原型链
this绑定新对象执行构造函数- 返回值规则(对象类型覆盖默认返回)
10. Vue3中reactive()与ref()的区别?为何需要ref包装基本类型
题目要点
reactive基于Proxy实现,深度代理对象,实现复杂对象响应式ref用于包装基本类型,实现响应式的包装对象,访问需用.value- 基本类型无法用
reactive代理,必须用ref包装 - 组合式API中,
ref适合单值管理,reactive适合对象管理 - 设计目的是保证响应式系统能统一追踪和更新数据变化
参考答案
考察点
- 理解 Vue3 响应式核心API
reactive和ref的区别与设计初衷 - 掌握响应式数据的实现机制及底层原理
- 理解基本类型响应式的特殊处理原因及解决方案
- 能结合实际项目场景,说明两者的使用时机和注意点
参考答案
一、reactive() 与 ref() 的核心区别
reactive()- 用于将一个对象(包括数组、普通对象)包装成响应式对象
- 通过
Proxy实现深度响应式,拦截所有属性访问和修改 - 访问属性时直接操作响应式对象,无需
.value访问 - 不支持基本类型(如字符串、数字、布尔)作为参数,传入基本类型会原样返回,不具备响应式
- 用于将一个对象(包括数组、普通对象)包装成响应式对象
ref()- 用于包装单一基本类型或对象,返回一个包含
.value属性的响应式引用 - 通过内部对象包装,实现对基本类型的响应式支持
- 使用时需要通过
.value访问或赋值 - 对象类型作为参数时,
ref只做浅包装,不会递归转换为响应式对象
- 用于包装单一基本类型或对象,返回一个包含
二、为何需要 ref 包装基本类型
- 基本类型(如字符串、数字、布尔值)是不可被
Proxy直接代理的 reactive是基于Proxy的对象代理机制,无法监听基本类型的变化ref使用了一个包裹对象,利用对象的属性访问拦截,实现对基本类型的响应式包装- 通过
.value访问,保证了响应式系统能正确追踪和触发依赖更新
三、使用场景与注意点
| 场景 | 适用API | 访问方式 | 说明 |
|---|---|---|---|
| 响应式对象(复杂数据) | reactive | 直接访问属性 | 深度响应式,适合大多数对象数据 |
| 响应式基本类型数据 | ref | 通过 .value 访问 | 只能用 ref 包装,方便响应式追踪 |
| 组合式API中单值响应式管理 | ref | .value | 方便解构、传递与监听单值 |
四、示例代码
import { reactive, ref } from 'vue';
const state = reactive({ count: 0 }); // 对象响应式
state.count++; // 响应式更新
const num = ref(0); // 基本类型响应式
num.value++; // 响应式更新
const objRef = ref({ name: 'Tom' });
console.log(objRef.value.name); // 访问对象的属性
11. 依赖收集时为何用WeakMap存储target->deps
题目要点
- 依赖收集存储
target -> deps关系核心是管理响应式对象和副作用绑定 WeakMap的键为弱引用,避免强引用导致的内存泄漏- 响应式对象不再使用时,
WeakMap条目可自动被垃圾回收 - 普通
Map会造成响应式对象无法释放,易导致内存泄漏 - 使用
WeakMap是响应式系统设计中的内存安全和性能保障
参考答案
考察点
- 理解依赖收集在响应式系统中的核心作用
- 掌握响应式系统中数据结构设计原理及内存管理策略
- 理解 JavaScript 中
WeakMap的特点及其在依赖管理中的应用 - 能结合Vue等框架实际场景,说明为何使用
WeakMap而非普通Map
参考答案
一、依赖收集简介
- 响应式系统通过“依赖收集”追踪数据(target)与副作用(effects)之间的关系
- 当数据变化时,能通知相关副作用重新执行,保持视图与数据同步
- 依赖关系通常存储为:
target(响应式对象) ->key(属性名) ->deps(依赖的副作用函数集合)
二、为何使用 WeakMap 存储 target -> deps
- 避免内存泄漏
- 响应式对象
target作为键,副作用作为值 WeakMap的键是弱引用,不阻止垃圾回收- 当某个响应式对象不再被引用时,
WeakMap中对应的条目会被自动回收,避免内存泄漏
- 响应式对象
- 性能优化
- 不必手动清理依赖,减少维护成本和复杂度
- 不必手动清理依赖,减少维护成本和复杂度
- 设计契合
- 响应式对象是引用类型,适合做
WeakMap键 WeakMap无法枚举键,天然保证了依赖管理的封装性和安全性
- 响应式对象是引用类型,适合做
三、普通 Map 的不足
- 使用普通
Map会导致键对象被强引用 - 响应式对象即使不再使用,也无法被回收,造成内存泄漏
- 必须额外编写清理逻辑,增加复杂度和出错风险
四、总结
- 依赖收集中的
target对象生命周期与依赖关系绑定 - 利用
WeakMap的弱引用特性,自动管理内存 - 保证响应式系统既高效又安全
12. 说说vue中,key的原理
题库原题:说说vue中,key的原理
题目要点
在 Vue 中,key 是用于帮助 Vue 识别和跟踪虚拟 DOM 的变化的特殊属性。当 Vue 更新渲染真实 DOM 时,它使用 key 属性来比较新旧节点,并尽可能地复用已存在的真实 DOM 节点,以提高性能。
参考答案
在 Vue 中,key 是用于帮助 Vue 识别和跟踪虚拟 DOM 的变化的特殊属性。当 Vue 更新渲染真实 DOM 时,它使用 key 属性来比较新旧节点,并尽可能地复用已存在的真实 DOM 节点,以提高性能。
Vue 在进行虚拟 DOM 的 diff 算法时,会使用 key 来匹配新旧节点,以确定节点的更新、移动或删除。它通过 key 属性来判断两个节点是否代表相同的实体,而不仅仅是根据它们的内容是否相同。这样可以保留节点的状态和避免不必要的 DOM 操作。
key 的工作原理如下:
- 当 Vue 更新渲染真实 DOM 时,它会对新旧节点进行比较,找出它们之间的差异。
- 如果两个节点具有相同的
key值,则 Vue 认为它们是相同的节点,会尝试复用已存在的真实 DOM 节点。 - 如果节点具有不同的
key值,Vue 会将其视为不同的节点,并进行适当的更新、移动或删除操作。
使用 key 可以提供更准确的节点识别和跟踪,避免出现一些常见的问题,比如在列表中重新排序时导致的元素闪烁、输入框内容丢失等。
key 必须是唯一且稳定的,最好使用具有唯一标识的值,例如使用数据的唯一 ID。同时,不推荐使用随机数作为 key,因为在每次更新时都会生成新的 key,导致所有节点都重新渲染,无法复用已有的节点,降低性能。
13. 详细讲一下vue的diff算法
题目要点
- Vue Diff通过同层对比新旧虚拟DOM,实现最小DOM更新
- 采用双端指针策略快速处理列表头尾节点
- 通过Key建立映射,实现节点复用和移动优化
- 深度递归比较节点属性和子节点,细粒度更新
- 多种优化策略(跳过静态节点、缓存等)提升性能
- 复杂度由
O(n³)降为O(n),确保实际高效运行
参考答案
考察点
- 理解虚拟DOM中Diff算法的核心原理
- 掌握Vue中Diff算法的具体实现流程及优化手段
- 能分析Diff算法的性能瓶颈及其应对策略
- 熟悉Diff算法在组件更新和渲染中的实际应用场景
参考答案
一、Diff算法核心原理
定义
Diff算法是虚拟DOM核心技术,用于比较新旧虚拟DOM树的差异,从而最小化实际DOM操作,实现高效渲染。目标
通过比较,找到最少的节点变更,避免全量重绘,提高性能。关键思想
- 只对比同一层级的节点,跨层比较复杂度高,Vue和React都遵循此原则。
- 利用节点的唯一标识(Key)进行快速定位和复用。
- 只对比同一层级的节点,跨层比较复杂度高,Vue和React都遵循此原则。
二、Vue的Diff算法实现流程
同层比较
- 只比较同一级的子节点,递归比较节点树结构。
类型判断
- 如果新旧节点类型不同,直接销毁旧节点,创建新节点。
Key与无Key比较策略
- 有Key列表
- 使用双端指针策略快速匹配前后端节点(头尾指针)。
- 通过Key构建映射表,快速定位新节点在旧节点中的位置,实现移动和复用。
- 使用双端指针策略快速匹配前后端节点(头尾指针)。
- 无Key列表
- 按顺序比较,对比对应索引位置的节点,新增或删除尾部节点。
- 有Key列表
具体算法细节
双端指针优化
- 用四个指针指向新旧节点的头尾,逐步往中间推进。
- 依次比较头头、尾尾、头尾、尾头节点,若匹配则移动指针。
- 通过这种方式快速定位节点复用或移动,减少查找。
- 用四个指针指向新旧节点的头尾,逐步往中间推进。
中间节点处理
- 处理完双端指针后剩余的中间节点,通过Key建立映射表。
- 新节点遍历映射旧节点,决定是复用、移动还是新增。
- 处理完双端指针后剩余的中间节点,通过Key建立映射表。
递归深度对比
- 对比节点属性、事件监听、文本内容等,更新发生变化的部分。
- 对子节点递归执行Diff。
- 对比节点属性、事件监听、文本内容等,更新发生变化的部分。
三、Diff算法的优化策略
避免不必要的DOM操作
- 复用旧节点,减少销毁和创建。
- 最小化移动操作,性能更优。
- 复用旧节点,减少销毁和创建。
双端指针减少查找次数
- 在大量列表更新时显著提升性能。
使用Key提高准确性和效率
- Key提供节点稳定身份,避免错误复用。
跳过静态节点
- Vue3编译时会标记静态节点,避免每次更新时重复比较。
缓存节点和Patch策略
- 针对组件和Fragment等结构,采用不同的Patch策略。
四、应用场景
- 组件更新时,确保状态改变只影响对应部分DOM。
- 列表渲染(
v-for)中的节点复用和移动。 - 动态节点插入、删除、替换。
五、Diff算法复杂度分析
- 传统树的Diff算法复杂度为
O(n³),不可行。 - Vue采用同层比较和Key映射,复杂度降至
O(n)。 - 双端指针优化进一步减少实际查找次数。
14. 讲一下Vue3从到DOM渲染的完整流程
题目要点
- 模板字符串解析成AST
- AST编译成渲染函数,生成VNode树
- 渲染函数执行时收集响应式依赖
- 生成新虚拟DOM,进行Diff与旧DOM比较
- 最小化真实DOM操作,进行增量更新
- 静态节点提升和指令优化提升性能
- 响应式数据变化触发更新,保持视图一致
参考答案
考察点
- 掌握 Vue3 编译与渲染的整体流程
- 理解模板编译、虚拟DOM生成与渲染机制
- 了解响应式系统在渲染中的作用
- 熟悉从模板到真实DOM的转换细节与优化点
参考答案
一、Vue3从 <template> 到 DOM 渲染的完整流程
模板解析(Template Parsing)
- Vue3 编译器读取单文件组件(SFC)中的
<template>模板字符串。 - 将模板字符串解析成抽象语法树(AST)。
- 处理指令(如
v-if、v-for)、插值、事件绑定等,构建树状结构。
- Vue3 编译器读取单文件组件(SFC)中的
模板编译(Template Compilation)
- 将 AST 转换为渲染函数(render function)。
- 编译器生成的渲染函数是一个返回虚拟DOM节点(VNode)的函数,使用 Vue3 的虚拟DOM API。
- 编译阶段进行优化:静态节点标记、事件绑定优化、代码分块等。
- 将 AST 转换为渲染函数(render function)。
响应式依赖收集(Reactive Dependency Collection)
- Vue3 的响应式系统基于 Proxy。
- 组件实例创建时,会执行渲染函数,读取响应式数据,触发依赖收集。
- 这样数据变化时,能精准触发对应组件重新渲染。
- Vue3 的响应式系统基于 Proxy。
虚拟DOM生成(VNode Creation)
- 渲染函数执行,生成虚拟DOM树(VNode树)。
- VNode是一个描述DOM节点类型、属性、子节点的JS对象。
- 该过程是纯函数式,不操作真实DOM,性能开销较小。
- 渲染函数执行,生成虚拟DOM树(VNode树)。
虚拟DOM与真实DOM同步(Patch过程)
- Vue3 根据上次渲染的旧VNode树与本次新VNode树做Diff对比。
- 通过Diff算法计算出最小变更集(插入、更新、删除)。
- 进行真实DOM的增量更新,避免全量重绘。
- Vue3 根据上次渲染的旧VNode树与本次新VNode树做Diff对比。
DOM渲染与挂载
- 新生成或更新的真实DOM节点挂载到页面指定容器中。
- 完成用户可见的UI更新。
- 新生成或更新的真实DOM节点挂载到页面指定容器中。
后续更新
- 响应式数据变化后,重新执行渲染函数生成新VNode,触发Patch流程。
- 维持界面和数据状态同步。
- 响应式数据变化后,重新执行渲染函数生成新VNode,触发Patch流程。
二、流程中的关键优化点
- 静态节点提升:静态节点不参与后续重新渲染,减少无用计算。
- 指令预编译和事件缓存:减少运行时开销。
- 碎片化渲染:支持Fragment,减少额外DOM层级。
- 高效Diff算法:双端指针、Key优化,确保复杂界面快速更新。
15. beforeUpdate和updated钩子中修改数据会引发什么问题?如何解决
题目要点
- 在
beforeUpdate和updated钩子修改响应式数据会触发无限更新循环 - 无限循环原因是钩子修改数据触发新一轮更新,钩子再次执行
- 导致浏览器卡死、性能严重下降
- 避免在这两个钩子中直接修改数据,或者使用异步方式延迟修改
- 优选在合适的生命周期或业务事件中修改数据,确保渲染流程稳定
参考答案
考察点
- 理解 Vue 生命周期钩子的执行时机和机制
- 掌握在
beforeUpdate和updated钩子中修改数据带来的副作用 - 能说明引发无限更新循环或渲染异常的原因
- 了解避免此类问题的最佳实践和解决方案
参考答案
一、生命周期钩子简介
beforeUpdate:组件数据更新,虚拟DOM重新渲染之前调用updated:组件更新完成,真实DOM渲染完成后调用
二、在 beforeUpdate 和 updated 中修改数据的问题
引发无限渲染循环
- 修改响应式数据会触发视图重新渲染
- 钩子中修改数据导致数据变更,触发更新,钩子再次执行
- 形成死循环,浏览器卡顿甚至崩溃
- 修改响应式数据会触发视图重新渲染
渲染异常与性能问题
- 不断重复渲染增加性能开销
- UI状态可能不稳定,用户体验差
- 不断重复渲染增加性能开销
三、产生问题的根本原因
- Vue的响应式系统是“脏检查”机制
- 钩子执行时修改数据,Vue监听到变化,自动重新触发更新
beforeUpdate和updated钩子处于更新周期内,频繁触发更新导致无限循环
四、解决方案与最佳实践
- 避免在这两个钩子中直接修改响应式数据
- 若必须修改,确保修改是幂等且不会引起额外变化
- 若必须修改,确保修改是幂等且不会引起额外变化
- 将修改操作延迟到下一个事件循环(
nextTick)之外执行- 例如使用
this.$nextTick(() => { ... })或setTimeout
- 例如使用
- 将数据修改放在更合适的生命周期钩子中,如
mounted或事件回调中 - 使用状态管理或业务逻辑层分离,避免在渲染钩子中写业务逻辑
- 监控渲染性能,避免不必要的数据更新
16. 讲一下你在项目中如何使用Chrome DevTools Performance面板分析FPS卡顿的
题目要点
- 利用 Performance 面板录制并捕获性能数据
- 观察 FPS 曲线,定位帧率低下的时间段
- 通过主线程时间轴和火焰图分析长任务与瓶颈函数
- 重点关注重排、重绘、合成及JS执行耗时
- 结合渲染机制调整DOM操作和动画实现方式
- 采用异步拆分任务和GPU加速优化渲染效率
参考答案
考察点
- 熟悉 Chrome DevTools Performance 面板的使用方法
- 理解浏览器渲染流程及影响 FPS 的关键环节
- 能分析帧率卡顿的具体原因(如长任务、重排重绘等)
- 掌握性能瓶颈定位和优化思路
参考答案
一、Chrome DevTools Performance 面板简介
- Performance 面板用于捕获网页的性能数据,展示 CPU 时间线、帧率(FPS)、事件分布等
- 能分析脚本执行、样式计算、布局、绘制、合成等阶段耗时情况
- 通过录制一段操作过程,捕获渲染性能快照
二、使用流程
开启性能录制
- 打开 DevTools,切换到 Performance 面板
- 点击“Record”按钮,模拟或复现页面卡顿场景
- 结束录制,生成性能分析报告
- 打开 DevTools,切换到 Performance 面板
查看 FPS 曲线
- 观察顶部 FPS 图,理想帧率是 60 FPS(每帧 ~16.6ms)
- 低帧率区域标红,重点关注这些区域
- 观察顶部 FPS 图,理想帧率是 60 FPS(每帧 ~16.6ms)
分析主线程耗时
- 在 Main Thread 时间轴中查看长任务(黄色条)
- 重点检查 JavaScript 执行、样式计算(Recalculate Style)、布局(Layout)、绘制(Paint)时间
- 长任务导致浏览器无法及时刷新,FPS 下降
- 在 Main Thread 时间轴中查看长任务(黄色条)
检查渲染阻塞原因
- 通过 Flame Chart(火焰图)查看调用栈,定位具体函数或操作
- 识别是否存在大量同步DOM操作、频繁触发重排或重绘
- 通过 Flame Chart(火焰图)查看调用栈,定位具体函数或操作
查看合成层与GPU活动
- 关注 Composite Layers 阶段,判断是否合理使用了 GPU 加速
- 查看是否存在不必要的图层合成或频繁触发合成导致开销大
- 关注 Composite Layers 阶段,判断是否合理使用了 GPU 加速
捕获事件与帧边界
- 对比用户输入事件(如滚动、点击)与帧渲染时间,判断响应延迟
- 确认是否有任务阻塞帧边界导致卡顿
- 对比用户输入事件(如滚动、点击)与帧渲染时间,判断响应延迟
三、项目中定位FPS卡顿的实例步骤
- 录制一段页面滚动或动画的过程
- 观察 FPS 下降时间点,锁定主线程长任务
- 分析火焰图定位耗时函数,找到性能瓶颈(如大量 DOM 操作或复杂计算)
- 配合代码审查,确认是否存在同步阻塞操作
- 针对重排重绘优化,如减少DOM修改频率、使用transform代替位移等
- 优化 JS 执行效率,异步拆分大任务
- 使用 GPU 加速合理拆分图层,减少合成压力
17. requestAnimationFrame优化动画?对比CSS动画的GPU加速原理
题目要点
- RAF 是浏览器刷新同步的动画回调,能减少丢帧和卡顿
- CSS动画借助合成层实现GPU加速,避免重排重绘,提高性能
- RAF灵活但依赖JS执行,CSS动画性能更优但灵活性有限
- 两者根据需求场景合理选择与配合使用
- 现代动画优化中,推荐用CSS动画做轻量效果,用RAF做复杂动画控制
参考答案
考察点
- 理解
requestAnimationFrame(RAF)的工作机制及优势 - 掌握如何使用 RAF 优化动画性能
- 了解 CSS 动画的 GPU 加速原理及其区别
- 能比较 JS 动画与 CSS 动画的优劣及使用场景
参考答案
一、requestAnimationFrame 优化动画原理
requestAnimationFrame是浏览器提供的接口,用于在浏览器下一次重绘前执行回调函数。- 它能保证动画回调在浏览器的刷新周期(一般60FPS,即16.6ms)内执行,避免不必要的多余帧更新。
- 使用 RAF 可以让动画与浏览器渲染同步,减少掉帧、卡顿现象。
- RAF 具有自动节流功能:当标签页不可见时,RAF 会暂停执行,节省资源。
- 典型用法:
function animate() { // 更新动画状态,比如元素位置 updateElementPosition(); requestAnimationFrame(animate); } requestAnimationFrame(animate);
#### 二、CSS动画的GPU加速原理
* CSS 动画(如 `transform`, `opacity`)通常会触发 GPU 加速。
* 浏览器会将这类动画单独放到合成层(Composite Layer)进行处理,利用 GPU 进行图层合成,避免触发主线程中的重排(reflow)和重绘(repaint)。
* 这样,动画只需合成图层,不改变布局或绘制,性能开销小,流畅度高。
* 典型GPU加速的CSS属性有:`transform`, `opacity`, `filter` 等。
#### 三、对比分析
| 维度 | requestAnimationFrame (JS 动画) | CSS 动画 (GPU 加速) |
| ----- | ----------------------------- | --------------- |
| 执行机制 | JS 脚本控制,每帧执行回调,需手动更新状态 | 浏览器声明式执行,底层自动优化 |
| 渲染同步 | 与浏览器刷新同步,减少丢帧 | 直接触发合成层,绕过布局绘制 |
| 性能 | 较好,但大量 JS 计算依然可能阻塞主线程 | 极高性能,轻量,适合常见动画 |
| 控制灵活度 | 灵活,可执行复杂逻辑 | 灵活性受限于 CSS 动画能力 |
| 兼容性 | 所有现代浏览器支持 | 所有现代浏览器支持 |
| 节流机制 | 自动节流,标签页隐藏时暂停 | 同样支持节流 |
#### 四、使用场景
* **`requestAnimationFrame` 适合**:
* 需要复杂动画逻辑、物理模拟、动画与业务逻辑高度耦合的场景
* 动态计算位置、动画帧控制等
* **CSS动画适合**:
* 简单的 UI 交互动效,如按钮点击反馈、元素进出场动画
* 需要高性能、低主线程占用的动画
</details>
## 18. 虚拟列表实现中如何计算渲染区间?滚动时如何避免布局抖动 {#question-subjective-d4c9eb523d3e}
### 题目要点
- 计算渲染区间基于 scrollTop、容器高度和单项高度加缓冲区<br>
- 不固定高度需维护高度缓存并结合二分查找定位索引<br>
- 抖动多因占位高度不准确和频繁重排导致<br>
- 通过占位元素撑起正确高度、设置缓冲区和减少同步布局避免抖动<br>
- transform 位移与异步渲染优化性能和体验
<details>
<summary>参考答案</summary>
### 考察点
- 理解虚拟列表(Virtual List)的核心思想及其性能优势<br>
- 掌握如何根据滚动位置和容器高度计算渲染区间(startIndex、endIndex)<br>
- 了解滚动过程中布局抖动(闪烁、跳动)产生的原因<br>
- 掌握避免布局抖动的技术手段和优化策略<br>
---
### 参考答案
#### 一、虚拟列表核心原理
- 虚拟列表通过只渲染视口内及附近一定范围内的列表项,避免渲染全部数据,极大降低DOM节点数量,提升性能。<br>
- 通过动态计算“渲染区间”,即当前需要渲染的起始和结束索引,实现按需渲染。<br>
#### 二、渲染区间的计算方法
1. **关键数据**<br>
- `scrollTop`:当前滚动容器的垂直滚动距离<br>
- `viewportHeight`:容器可视高度<br>
- `itemHeight`:单个列表项固定高度(或估算平均高度)<br>
- `buffer`:预渲染的额外项数,防止快速滚动时白屏<br>
2. **基本计算**<br>
- 起始索引 `startIndex = Math.floor(scrollTop / itemHeight) - buffer`,不能小于0<br>
- 结束索引 `endIndex = Math.ceil((scrollTop + viewportHeight) / itemHeight) + buffer`,不能超过总条数<br>
3. **动态高度处理**<br>
- 若列表项高度不固定,需维护每项高度数组,累计计算滚动偏移<br>
- 结合二分查找快速定位当前滚动位置对应的起始项索引<br>
4. **渲染时**<br>
- 仅渲染 `[startIndex, endIndex]` 区间内的元素<br>
- 通过占位元素(padding 或 margin)撑开总高度,保持滚动条准确<br>
#### 三、滚动时布局抖动产生原因
- 由于动态渲染元素高度计算不准确,导致占位高度与实际渲染元素高度不符<br>
- 频繁增减 DOM 节点引起的重排,造成滚动跳动或闪烁<br>
- 直接操作 DOM 或频繁触发同步布局,导致渲染卡顿和界面抖动<br>
#### 四、避免布局抖动的优化方案
1. **准确计算占位高度**<br>
- 通过缓存每个元素真实高度,动态调整占位容器高度<br>
- 使用占位容器(如上、下 padding)保持滚动高度稳定<br>
2. **合理设置缓冲区(buffer)**<br>
- 预渲染上下多余项,平滑快速滚动,减少白屏<br>
3. **减少同步布局触发**<br>
- 避免频繁读取布局属性(如offsetHeight),减少重排<br>
- 使用异步手段(如 requestAnimationFrame)批量处理渲染更新<br>
4. **使用transform位移代替修改top/scrollTop**<br>
- transform GPU 加速且不触发重排,减少抖动<br>
5. **懒加载图片和资源,防止加载时尺寸变化**
</details>
## 19. 动态导入(`import()`)如何影响Chunk生成?如何预加载分割的Chunk {#question-subjective-5b01366a776a}
### 题目要点
* 动态导入触发构建工具代码拆分,生成独立Chunk文件
* Chunk命名可通过魔法注释自定义,便于缓存和调试
* 预加载(preload)抢占式加载关键Chunk,预取(prefetch)空闲时加载未来可能用的Chunk
* 合理使用预加载/预取提升体验,避免无谓资源占用
* 适用于路由懒加载、模块按需加载等场景,降低首屏体积,提升加载速度
<details>
<summary>参考答案</summary>
### 考察点
- 理解动态导入(`import()`)的工作机制及其对代码分割的影响<br>
- 掌握动态导入如何触发Chunk(代码块)自动拆分<br>
- 熟悉Webpack或类似构建工具中Chunk的生成与命名策略<br>
- 了解如何使用预加载(preload)和预取(prefetch)技术优化分割Chunk的加载时机和性能<br>
---
### 参考答案
#### 一、动态导入(`import()`)及Chunk生成原理
- `import()` 是ES提案中的动态模块加载语法,返回一个Promise,异步加载对应模块。<br>
- 构建工具(如Webpack)检测到 `import()` 后,会将被动态导入的模块及其依赖单独打包成一个Chunk(代码块)。<br>
- 动态导入实现了代码按需加载,减小首屏包体积,提高页面初始加载速度。<br>
- 每次 `import()` 调用都会对应一个单独的Chunk文件(如果模块被多处动态导入,构建工具会合并成一个Chunk)。<br>
#### 二、Chunk生成细节与命名
- Webpack默认以模块路径和内容哈希生成Chunk名称,方便缓存管理。<br>
- 可通过魔法注释自定义Chunk名称,如:<br>
```js
import(/* webpackChunkName: "myChunk" */ './moduleA')
- 这种命名帮助在网络请求中更易识别和调试,也便于缓存策略配置。
三、预加载与预取Chunk
预加载(Preload)
通过
<link rel="preload" as="script" href="chunk.js">,浏览器提前加载Chunk资源,保证后续动态导入时能快速响应。预加载适用于即将用到的重要资源,通常优先级较高。
Webpack支持通过魔法注释自动生成预加载标签,例如:
import(/* webpackPreload: true */ './moduleA')
预取(Prefetch)
通过
<link rel="prefetch" href="chunk.js">,浏览器在空闲时加载资源,准备未来可能需要的代码。预取优先级低,不会影响当前页面性能,适合用户后续操作可能触发的模块。
Webpack对应魔法注释:
import(/* webpackPrefetch: true */ './moduleA')
区别总结
特性 预加载 (Preload) 预取 (Prefetch) 加载时机 尽快加载,抢占带宽 空闲时加载,低优先级 使用场景 页面当前或马上需要的模块 未来可能会用到的模块 浏览器行为 会阻塞其他资源加载 不阻塞,异步后台加载
四、使用场景与注意事项
- 动态导入减少首屏体积,提高性能,适合大型应用和路由懒加载场景。
- 通过预加载保证关键动态Chunk在用户操作前已下载,提升交互流畅度。
- 预取减少用户等待,但不能保证即时可用,适合预测用户后续行为。
- 预加载和预取结合使用,需合理评估网络环境,避免资源浪费和带宽竞争。
20. HTTPS 加密算法和加解密过程是啥?
题库原题:HTTPS 加密算法和加解密过程是啥?
题目要点
HTTPS 通过结合非对称加密和对称加密来确保通信的安全。非对称加密用于握手过程中的身份验证和密钥交换,而对称加密则用于实际的数据传输。哈希算法用于确保数据的完整性。这个加解密过程确保了数据在客户端和服务器之间的安全传输。
参考答案
HTTPS 使用 SSL/TLS 协议来加密传输的数据,确保数据的机密性和完整性。HTTPS 的加解密过程主要包括以下几个步骤和算法:
1. 加密算法和密钥类型
- 对称加密算法:使用相同的密钥进行加密和解密。常见的对称加密算法包括 AES(Advanced Encryption Standard)和 3DES(Triple Data Encryption Standard)。
- 非对称加密算法:使用一对密钥(公钥和私钥),公钥用于加密,私钥用于解密。常见的非对称加密算法包括 RSA(Rivest-Shamir-Adleman)和 ECC(Elliptic Curve Cryptography)。
- 哈希算法:用于生成数据的摘要,以确保数据完整性。常见的哈希算法包括 SHA-256(Secure Hash Algorithm 256-bit)和 MD5(Message Digest Algorithm 5,虽然 MD5 不再安全)。
2. SSL/TLS 握手过程
握手阶段
客户端发起连接
- 客户端发送一个 “ClientHello” 消息,包含客户端支持的 TLS 版本、加密套件(加密算法组合)、压缩方法和其他信息。
服务器响应
- 服务器回应一个 “ServerHello” 消息,选择客户端支持的加密套件和 TLS 版本。
- 服务器发送其数字证书给客户端。数字证书包含服务器的公钥,由证书颁发机构(CA)签名。
证书验证
- 客户端验证服务器的数字证书的有效性和真实性。如果证书有效,客户端继续执行。
- 客户端生成一个随机的“pre-master secret”并用服务器的公钥加密,然后发送给服务器。这个“pre-master secret”将在后续的步骤中用于生成对称加密密钥。
密钥交换
- 服务器使用其私钥解密“pre-master secret”并生成一个对称加密密钥(session key)。
- 客户端和服务器使用“pre-master secret”生成相同的对称加密密钥(session key),用于加密后续的通信数据。
完成握手
- 双方用对称加密密钥加密并交换“Finished”消息,表示握手过程完成。
- 从这一点开始,客户端和服务器使用对称加密密钥加密和解密数据。
3. 数据加密和传输
- 对称加密:客户端和服务器使用之前生成的对称加密密钥来加密和解密数据。对称加密算法确保数据在传输过程中是机密的。
- 数据完整性:除了加密,TLS 还使用消息认证码(MAC)来确保数据在传输过程中没有被篡改。常见的 MAC 算法包括 HMAC(Hash-based Message Authentication Code)。
4. 连接关闭
- 优雅关闭:当通信完成时,客户端和服务器通过发送“close_notify”消息来优雅地关闭连接,确保所有的数据都被正确传输。
常见的加密套件
在 TLS 握手过程中,客户端和服务器会协商一个加密套件(cipher suite),这是一个加密算法的组合。常见的加密套件包括:
- TLS_AES_128_GCM_SHA256:使用 AES 进行对称加密,GCM(Galois/Counter Mode)用于加密模式,SHA-256 用于消息认证。
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:使用 ECDHE(Elliptic Curve Diffie-Hellman Ephemeral)进行密钥交换,RSA 进行身份验证,AES-256-GCM 进行对称加密,SHA-384 进行消息认证。
21. css模块化
题目要点
- CSS模块化解决样式全局污染和冲突问题
- 常用方案:CSS Modules、BEM、Scoped CSS、CSS-in-JS
- 模块化提高样式复用和可维护性,适合组件化开发
- 实践中需注意命名规范和构建支持
参考答案
考察点
- 理解CSS模块化的概念及目的
- 掌握常见的CSS模块化实现方案(如CSS Modules、BEM、Scoped CSS)
- 理解CSS模块化在大型项目中的实际应用价值
- 熟悉如何避免样式冲突、提升样式复用性和维护性
参考答案
一、CSS模块化的核心概念
- CSS模块化是将样式进行拆分、封装,避免全局命名冲突和样式污染,提升代码可维护性和复用性。
- 通过模块边界限定样式作用范围,减少副作用,提高团队协作效率。
二、常见的CSS模块化实现方式
CSS Modules
- 利用构建工具(如Webpack)支持,将CSS文件作为模块导入。
- 通过自动生成唯一的类名哈希,保证样式局部作用域,避免全局污染。
- 示例:
import styles from './Button.module.css'; <button className={styles.primary}>Click</button> ```<br> - 优点:完全避免命名冲突,写法自然。
- 利用构建工具(如Webpack)支持,将CSS文件作为模块导入。
BEM(块-元素-修饰符)命名规范
- 通过规范的类名命名规则,区分模块内块(block)、元素(element)和修饰符(modifier)。
- 例:
.button,.button__icon,.button--primary - 优点:语义清晰,便于多人协作,纯CSS,无需额外工具支持。
- 通过规范的类名命名规则,区分模块内块(block)、元素(element)和修饰符(modifier)。
Scoped CSS(如Vue的
<style scoped>)- 框架层面支持,将样式限定于当前组件,自动加属性选择器。
- 优点:简单方便,适合组件化开发。
- 框架层面支持,将样式限定于当前组件,自动加属性选择器。
CSS-in-JS
- 通过JS管理样式,动态生成类名和样式规则(如styled-components、Emotion)。
- 优点:样式和逻辑紧密结合,支持动态样式。
- 通过JS管理样式,动态生成类名和样式规则(如styled-components、Emotion)。
三、CSS模块化的应用价值
- 避免全局样式冲突和覆盖,减少调试难度。
- 促进组件独立和复用,提升团队开发效率。
- 有利于按需加载和样式隔离,优化性能。
- 提升代码可维护性,特别是大型复杂项目。
四、实践中注意点
- 对已有项目引入模块化需渐进式改造,避免大量样式失效。
- 保持命名规范,避免无意义的类名。
- 注意构建配置支持,确保模块化样式正确加载。
- 与设计系统结合,统一样式标准。