HTML
HTML5 新增了哪些语义化标签
slot,header,nav,main,article,section,footer,figure
CSS
垂直居中
- Flex 方案
display: flex; justify-content: center; /* 水平居中 */ align-items: center; /* 垂直居中 */ - Grid 方案
.outer { display: grid; } .inner { justify-self: center; /* 水平居中 */ align-self: center; /* 垂直居中 */ } - absolute + transform
position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); - absolute + 负 margin
position: absolute; left: 50%; top: 50%; margin-left: -50px; margin-top: -50px; - absolute + margin: auto
position: absolute; left: 0; right: 0; top: 0; bottom: 0; margin: auto;
重绘和回流(重排)是什么,如何避免?
DOM的变化影响到了元素的几何属性(宽高),浏览器重新计算元素的几何属性,其他元素的几何 属性和位置也会受到影响,浏览器需要重新构造渲染树,这个过程称为重排,浏览器将受到影响的部分 重新绘制到屏幕上的过程称为重绘。 引起重排的原因有
- 添加或者删除可见的DOM元素,
- 元素位置、 尺寸、内容改变,
- 浏览器页面初始化,
- 浏览器窗口尺寸改变,重排一定重绘,重绘不一定重排 减少重绘和重排的方法:
- 不在布局信息改变时做DOM查询,
- 使用cssText或者className一次性改变属性
- 使用fragment,
- 对于多次重排的元素,如动画,使用绝对定位脱离文档流,让他的改变不影响到其他 元素
绝对定位(absolute)和相对定位(position)的区别?
说一下你了解的CSS选择器?
element, class, id, * 属性选择器: attribute
CSS 中 BFC 的触发条件有哪些?它能解决什么实际问题?
子元素设置 float,还包括根元素、设置 overflow 为 hidden/auto/scroll、display 为 inline-block/table-cell/flex、设置 position 为 absolute/fixed。BFC 能解决的问题有:清除浮动导致的父元素高度塌陷、防止 margin 重叠、阻止元素被浮动元素覆盖
伪类选择器?
介绍一下一种css预处理器?
sass/less
Javascript
闭包
所谓闭包就是一个不会被销毁的ec 最常见的闭包就是一个函数嵌套另一个函数并且返回这个函数,而且内层函数使用外层函数的变量,这个变量就会保留在内存中,当执行外层函数,也就创建了一个闭包,闭包的两个作用,一个是保存一个是保护,保存就是延长了,变量的生命周期,保护就是避免污染。
原型链
prototype是函数属性,该属性是一个对象 __proto__是对象属性
const obj = new Fn()
obj.__proto__ = Fn().prototype
Object.prototype.proto 指向 undefined,所以所有对象的原型链终点都指向undefined
内存溢出,内存泄漏
内存泄露:该回收的垃圾对象没有被回收,发生了内存泄露,垃圾对象越堆越多,可用内存越来越少,若可用内存无法存放新的垃圾对象,就导致内存溢出。 内存溢出:当前创建的对象的大小大于可用的内存容量大小,发生内存溢出。 内存泄露会导致内存溢出。
实现ajax 请求
通过实例化一个XMLHttpRequest对象得到一个实例,调用实例的open方法为这次 ajax请求设定相应的http方法、相应的地址和以及是否异步,当然大多数情况下我们都是选异步, 以异步为例,之后调用send方法ajax请求,这个方法可以设定需要发送的报文主体,然后通过 监听readystatechange事件,通过这个实例的readyState属性来判断这个ajax请求的状态,其中分为0,1,2,3,4这四种 状态,当状态为4的时候也就是接收数据完成的时候,这时候可以通过实例的status属性判断这个请求是否成功
var xhr = new XMLHttpRequest();
xhr.open('get', 'aabb.php', true);
xhr.send(null);
xhr.onreadystatechange = function() {
if(xhr.readyState==4) {
if(xhr.status==200) {
console.log(xhr.responseText);
}
}
}
使用new操作符实例化一个对象的具体步骤
- 构造一个新的对象
- 将构造函数的作用域赋给新对象(也就是说this指向了新的对象)
- 执行构造函数中的代码
- 返回新对象
图片加载成功或者失败
使用onload事件运行加载成功,使用onerror事件判断失败
递归和迭代的区别是什么,各有什么优缺点?
程序调用自身称为递归,利用变量的原值推出新值称为迭代,递归的优点 大问题转化为小问题,可以减少代码量,同时应为代码精简,可读性好, 缺点就是,递归调用浪费了空间,而且递归太深容易造成堆栈的溢出。迭代的好处 就是代码运行效率好,因为时间只因循环次数增加而增加,而且没有额外的空间开销, 缺点就是代码不如递归简洁
事件循环(EVENT LOOP)?
js是单线程的,是指js执行引擎是单线程的,除了这个单线程,还有一个 任务队列,在执行js代码的过程中,执行引擎遇到注册的延时方法,如定时器,DOM事件, 会将这些方法交给相应的浏览器模块处理,当这些延时方法有触发条件去触发的时候, 这些延时方法会被添加至任务队列,而这些任务队列中的方法只有js的主线程空闲了才会执行, 这也就是说我们常常用的定时器定的时间参数只是一个触发条件,具体多少时间后执行其实还需要看 js主线程空闲与否
原生JS操作DOM的方法有哪些?
获取节点的方法getElementById、getElementsByClassName、getElementsByTagName、 getElementsByName、querySelector、querySelectorAll,对元素属性进行操作的 getAttribute、 setAttribute、removeAttribute方法,对节点进行增删改的appendChild、insertBefore、replaceChild、removeChild、 createElement等
typeof操作符返回值有哪些,对undefined、null、NaN使用这个操作符分别返回什么
typeof的返回值有undefined、boolean、string、number、object、function、symbol。对undefined 使用返回undefined、null使用返回object,NaN使用返回number
javascript做类型判断的方法有哪些?
typeof、instanceof 、 Object.prototype.toString()
setTimeout和setInterval的区别,包含内存方面的分析?
setTimeout表示间隔一段时间之后执行一次调用,而setInterval则是每间隔一段时间循环调用,直至clearInterval结束。 内存方面,setTimeout只需要进入一次队列,不会造成内存溢出,setInterval因为不计算代码执行时间,有可能同时执行多次代码, 导致内存溢出。
同源策略是什么?
同源策略是指只有具有相同源的页面才能够共享数据,比如cookie,同源是指页面具有相同的协议、域名、端口号,有一项不同就不是同源。 有同源策略能够保证web网页的安全性。
ES6之前JavaScript如何实现继承?
ES6之前的继承是通过原型来实现的,也就是每一个构造函数都会有一个prototype属性,
如何阻止事件冒泡和默认事件?
标准的DOM对象中可以使用事件对象的stopPropagation()方法来阻止事件冒泡; 默认事件的话通过事件对象的preventDefault()方法来阻止。
addEventListener有哪些参数?
有三个参数,第一个是事件的类型,第二个是事件的回调函数,第三个是一个表示事件是冒泡阶段还是捕获阶段捕获的布尔值,true表示捕获,false表示冒泡
介绍一下Promise?
Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fufilled(已成功)、rejected(已失败),只有异步操作的结果才能决定最后的状态。
Promise 对象是一个构造函数,用来生成 Promise 实例,这个实例一旦创建就会立即执行; 实例中接收一个执行函数作为参数,这个执行函数有两个函数类型形参(resolve 和 reject);
resolved 函数的作用是将 Promie 对象的状态从“未完成”到“成功”,在异步函数成功时调用,并将异步操作的结果作为参数传递出去; rejected 函数的作用是将 Promise 对象的状态从“未完成”到“失败”,在异步函数失败时调用,并将异步操作报出的错误作为参数传递出去。
Promise 实例生成后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数。
//接收一个 PromiseList;
//多个 Promise 任务同时执行;
//如果全部成功则以数组的方式返回所有 Promise 任务的执行结果;
//如果有一个 Promise rejected,则只返回 rejected 任务的结果。
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
//接收一个 promiseList;
//多个 Promise 任务同时执行;
//返回最先执行结束的 Promise 任务的结果,不管这个 Promise 是成功还是失败。
Promise.race([p1, p2]).catch(err => {
console.log('err', err); // err error promise2
})
//接收一个 promiseList;
//多个 Promise 任务同时执行;
//只要一个 Promise 执行成功,则返回该执行成功的promise 结果;如果全都失败,则返回AggregateError 实例,这是 Error 的子类,用于把单一的错误集合在一起。
Promise.any([p2, p3, p4]).then((first) => {
console.log(first); // promise4
});
try...catch... 不可以捕获 Promise 异常
什么是深拷贝,什么是浅拷贝?
浅拷贝是指仅仅复制对象的引用,而不是复制对象本身;深拷贝则是把复制对象所引用的全部对象都复制一遍。
async/await 的作用是什么?它们与 Promise 有什么关系
async/await 的作用是简化 Promise 的异步代码写法,让异步逻辑看起来像同步代码,但本质仍是异步执行。关系上,async 函数返回一个 Promise 对象,await 只能用在 async 函数中,后面跟 Promise 对象,会等待 Promise 状态变为 fulfilled 后返回结果,相当于 Promise 的 then 方法的语法糖。
数组的 map 方法和 forEach 方法有什么区别
forEach 无法中断遍历,map 可以通过 return 提前结束当前迭代但不会中断整个遍历
TypeScript
interface 和 type 的主要区别是什么
interface 和 type 都可用于定义类型,核心区别在于扩展方式:interface 通过 extends 扩展,type 通过 & 交叉类型扩展。使用场景上,interface 更适合定义对象、类的结构,支持声明合并;type 可定义基本类型别名、联合类型、元组等,功能更广泛
TypeScript 中,泛型的作用是什么
实现类型复用和类型安全,让组件/函数支持多种类型但保持类型约束
function getLastItem<T>(arr: T []): T
{
return arr [arr.length - 1];
}
TypeScript 中,never 类型和 void 类型的区别是什么
void 类型表示函数没有返回值,或返回 undefined;never 类型表示函数永远不会正常结束,比如抛出错误或无限循环
Vue
Vue 项目中,如何优化首屏加载速度
CDN 加速静态资源,还可以用路由懒加载 const Home = () => import ('./Home.vue'),减少首屏加载的代码量;对非首屏组件用 defineAsyncComponent 异步加载;开启 Gzip 压缩减小文件体积;图片用 WebP 格式并懒加载
Vue2 和 Vue3 的核心生命周期钩子分别是什么?执行顺序?
Vue2 beforeCreate → created → beforeMount → mounted → beforeUpdate → updated → beforeDestroy → destroyed Vue3和 Vue2 基本一致,仅重命名 beforeDestroy→beforeUnmount、destroyed→unmounted; 组合式(setup):setup(替代 beforeCreate/created)→ onBeforeMount → onMounted → onBeforeUpdate → onUpdated → onBeforeUnmount → onUnmounted。
Vue3 中,如何使用 watch 监听多个数据源
监听多个数据源可将数据源放在数组中作为 watch 的第一个参数。代码示例:
import {ref, watch} from 'vue';
const a = ref (1);
const b = ref (2);
watch ([a, b], ([newA, newB], [oldA, oldB]) => {
console.log ('a 或 b 变化了 ', newA, newB);
});
Vue 中,watch 和 computed 的区别是什么?
watch 用于监听数据变化并执行异步或复杂操作,支持深度监听和立即执行;computed 是计算属性,依赖其他数据计算得出结果,会缓存结果,只有依赖变化时才重新计算,适合简单的同步计算。
在 Vue 中,组件通信有哪些方式?
父→子:props(支持类型校验、默认值); 子→父:defineEmits 定义自定义事件,$emit 触发; 父访问子:子组件用defineExpose暴露属性 / 方法,父组件用ref获取; 祖孙 / 跨层级组件:provide/inject(父组件 provide 提供数据,子孙组件 inject 注入,支持响应式); 兄弟 / 无层级组件
- 轻量场景:mitt 库(Vue3 替代 EventBus)
- 通用场景:Pinia/Vuex(状态管理) 全局通信:Pinia(Vue3 推荐)、Vuex(Vue2)、LocalStorage/SessionStorage(非响应式,需手动监听)。
Vue3 中,reactive 和 ref 的区别是什么?分别适用于什么场景
核心都是创建响应式数据,适配不同数据类型,Vue3 基于 Proxy 实现,均支持动态新增属性。 ref 用于定义基本类型或对象类型的响应式数据,访问时需通过.value;reactive 仅用于定义对象或数组类型的响应式数据,访问时直接用属性名。场景上,ref 适合基本类型或需要明确.value 访问的场景,reactive 适合复杂对象 / 数组且希望直接访问属性的场景
Vue 中,v-if 和 v-show 的区别是什么
v-if 是动态添加 / 移除 DOM 元素,初始条件为 false 时不渲染;v-show 是通过 CSS 的 display 属性控制显示 / 隐藏,DOM 始终存在
v-for 中 key 的作用?为什么不能用 index 作为 key?
作用:key 是 Vue 虚拟 DOM 的唯一标识,Vue 通过 key 匹配新旧节点,实现DOM 复用,减少不必要的重渲染。 不能用 index 的原因:数组增删 / 排序时,index 会重新分配,导致 Vue 误判节点关系,出现DOM 渲染错乱(如输入框值绑定异常)。
Vue 中,什么是虚拟 DOM
虚拟 DOM 是用 JavaScript 对象表示真实 DOM 的结构和状态,是真实 DOM 的 “虚拟” 版本。优势有:性能优化,通过 diff 算法比较新旧虚拟 DOM 差异,只更新需要变化的部分,避免不必要的 DOM 操作;简化开发,让开发者专注数据和逻辑,不用直接操作 DOM;跨平台支持,同一套代码可在不同平台渲染,比如 Vue 的虚拟 DOM 能支持 Web 和原生应用。
Vue 中单向数据流的含义?为什么要遵循?
含义:数据从父组件通过 props 流向子组件,子组件不能直接修改 props,若需修改,需通过 $emit(Vue2)/defineEmits(Vue3)通知父组件修改源数据。 原因:避免子组件随意修改父组件数据,导致数据流向混乱,方便追溯数据变化、定位 bug。
v-model 的原理?如何实现自定义组件的v-model(Vue3)?
原理:v-model是语法糖,Vue3 中默认对应 :modelValue + @update:modelValue,Vue2 中对应:value + @input; Vue3 特性:支持多个 v-model,可自定义绑定的属性名,无需修改组件内部逻辑。 自定义组件 v-model 示例(单值 + 多值)
</template>
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'
const inputVal = ref(''), userName = ref('张三')
</script>
<!-- 子组件 CustomInput.vue -->
<template>
<!-- 对应默认v-model -->
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
<!-- 对应自定义v-model:name -->
<input :value="name" @input="$emit('update:name', $event.target.value)" />
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
defineProps(['modelValue', 'name']) // 接收绑定的属性
defineEmits(['update:modelValue', 'update:name']) // 定义更新事件
</script>
keep-alive 的作用?核心属性?
作用:缓存组件实例,避免组件重复创建 / 销毁,提升切换性能(如 tab 页、详情页返回列表),适用于不需要频繁重新渲染的组件。 核心属性
- include:仅缓存匹配名称的组件(组件名 /name 选项);
- exclude:不缓存匹配名称的组件;
- max:最多缓存的组件数量(避免内存溢出); 专属生命周期:被缓存的组件会触发activated(组件激活)/deactivated(组件失活),替代mounted/unmounted做初始化 / 清理。
Vue2 和 Vue3 的响应式原理分别是什么?为什么 Vue3 的响应式更优?
Vue2 响应式原理:基于Object.defineProperty 核心逻辑:遍历data中的对象属性,用Object.defineProperty重写属性的get/set方法,实现属性劫持; 依赖收集 & 更新:读取属性时(get)收集依赖(Watcher),修改属性时(set)触发依赖更新,通知视图渲染; 缺陷:
- 无法劫持数组下标 / 长度修改,仅重写了数组 7 个变异方法(push/pop/shift/unshift/splice/sort/reverse);
- 无法监听对象动态新增 / 删除的属性,需手动调用Vue.set/Vue.delete;
- 初始化时需递归遍历所有属性,性能开销大。 Vue3 响应式原理:基于 ES6Proxy+Reflect 核心逻辑:用Proxy代理整个对象(而非单个属性),拦截对象的所有操作(get/set/deleteProperty 等),通过Reflect执行原始操作; 依赖收集 & 更新:读取属性时(get)通过effect收集依赖,修改属性时(set)触发effect执行,更新视图; 优势(对比 Vue2):
- 支持数组下标 / 长度修改、对象动态新增 / 删除属性,无需手动调用 API;
- 支持Map/Set/WeakMap等复杂数据类型;
- 初始化无需递归遍历,仅在属性被访问时才做劫持,懒加载提升初始化性能;
- 代理整个对象,新增属性自动具备响应性。
Vue 的 diff 算法核心逻辑?(Vue3 做了哪些优化?)
Vue 的 diff 算法是同层比较(不跨层级比较,跨层级直接销毁重建),核心解决新旧 VNode 树的高效对比问题,最终只把差异更新到真实 DOM。 核心通用逻辑
- 节点类型判断:若新旧节点标签不同,直接销毁旧节点,创建新节点,无需继续比较;
- 节点类型相同:对比节点的 props(属性 / 样式),更新差异的 props,再递归比较子节点;
- 列表 diff(v-for 核心):基于 key 建立新旧节点的映射关系,原地复用相同 key 的节点,仅处理新增 / 删除 / 移动的节点,最小化 DOM 操作。 Vue3 对 diff 的核心优化
- 静态提升:将模板中的静态节点(如Hello)提升到渲染函数外部,避免每次渲染都重新创建;
- PatchFlags:给动态节点添加标记,仅标记有变化的部分(如文本、props),对比时只处理带标记的节点,跳过静态节点;
- 事件缓存:将事件处理函数缓存,避免每次渲染都重新创建;
- 碎片优化:支持多根节点,无需包裹根 div,减少多余 DOM 节点。
简述 Vue 的核心特性?
数据驱动:数据变化自动更新视图,无需手动操作 DOM(基于 MVVM 模型); 组件化:将页面拆分为独立、可复用的组件,降低开发复杂度; 指令系统:提供v-if/v-for/v-bind/v-on/v-model等指令,简化模板开发; 虚拟 DOM:用 JS 对象描述真实 DOM,通过 diff 算法减少真实 DOM 操作,提升性能; 双向数据绑定:v-model语法糖,简化表单交互(本质是 props + 事件)。
Vue 项目性能优化有哪些核心手段?
性能优化是面试高频加分项,按开发阶段分类,覆盖前端通用优化 + Vue 专属优化:
- 代码层面(开发时)
- v-for必须加唯一 key,避免和 v-if 混用(v-if 提至外层);
- 用computed缓存推导结果,避免模板中写复杂表达式(如{{ list.filter(item => item.age>18).length }});
- 频繁触发的事件(如搜索、滚动)添加防抖 / 节流;
- 避免频繁修改响应式数据,减少响应式更新次数。
- 组件层面(开发时)
- 用defineAsyncComponent实现组件懒加载,按需加载非首屏组件;
- 用keep-alive缓存频繁切换的组件(如 tab、弹窗);
- 拆分大组件为小组件,降低单个组件的渲染复杂度;
- 非响应式数据用markRaw标记,避免 Vue 做响应式劫持(如第三方库实例、大对象)。
- 打包层面(构建时)
- 路由懒加载:用import()动态导入路由组件,拆分 chunk,减小首屏体积;
- 第三方库按需引入(如 Element Plus、Ant Design Vue),避免全量引入;
- 开启Tree-Shaking(Vue3 默认开启),清除未使用的代码;
- 压缩静态资源(JS/CSS/ 图片),使用 CDN 引入第三方库(如 Vue、Vue Router),减少打包体积;
- 开启Gzip/Brotli 压缩(后端配合),减小文件传输大小。
- 运行时层面(渲染时)
- 大数据列表用虚拟列表(如vue-virtual-scroller),只渲染可视区域的 DOM,避免一次性渲染上万条数据;
- 用v-show替代v-if处理频繁切换的场景;
- 组件销毁时(onUnmounted)清理定时器、取消网络请求、解绑事件,避免内存泄漏;
- 合理使用onBeforeUpdate/onUpdated,减少视图更新后的冗余操作。
Angular
Angular 的核心模块有哪些
- BrowserModule:浏览器环境必备(包含 CommonModule);
- CommonModule:提供 * ngFor、*ngIf 等内置指令;
- FormsModule/ReactiveFormsModule:表单处理;
- HttpClientModule:HTTP 请求;
- RouterModule:路由管理。
Angular 生命周期
执行顺序(核心):
- 初始化阶段:ngOnChanges → ngOnInit → ngDoCheck(含ngAfterContentInit/ngAfterViewInit);
- 更新阶段:ngOnChanges → ngDoCheck → ngAfterContentChecked → ngAfterViewChecked;
- 销毁阶段:ngOnDestroy。 常用:ngOnInit(初始化数据)、ngOnDestroy(销毁订阅 / 定时器)、ngOnChanges(监听输入属性变化)。
什么是Angular模块?
Angular模块是一个由组件、指令、管道和服务组成的逻辑单元。它们用于将应用程序拆分成功能区块,并允许开发人员将代码组织为更可维护的结构。每个Angular应用程序都至少有一个根模块。 模块包含:
- @NgModule 装饰器;
- declarations 数组;
- imports 数组;
- providers 数组;
- bootstrap 数组。
Angular中的指令是什么?
Angular中的指令是一种用于扩展HTML元素或属性的机制。它们允许您添加行为和样式到您的应用程序中,例如根据数据绑定条件显示或隐藏元素。
什么是Angular服务?
Angular服务是一种可注入的类,用于处理与组件无关的应用程序逻辑。它们允许您将应用程序的业务逻辑从组件中抽象出来,使代码更加可维护和可测试。 Angular 服务的核心作用是封装可复用的逻辑或数据,供组件共享,除了跨组件通信,还能处理 HTTP 请求、状态管理等 需用@Injectable()装饰器,并指定providedIn(如root表示根注入器)
什么是Angular的依赖注入(依赖注入简称DI)?
依赖注入是一种设计模式,Angular中使用它来管理组件和服务之间的依赖关系。通过使用依赖注入,您可以将组件和服务分离,并将它们的依赖关系委托给Angular框架,从而使代码更具可读性和可维护性。 依赖注入降低了代码之间的耦合度,增加了代码的可维护性。
什么是Angular的管道?
管道是一种用于将数据转换为特定格式的机制。Angular中提供了多种内置的管道类型,例如日期、货币和百分比。您还可以编写自己的管道来满足特定的需求。
什么是RxJS?
RxJS是一种流式编程库,它允许您使用Observable对象来处理异步和事件驱动的编程任务。在Angular中,RxJS常用于处理HTTP请求、数据流和事件处理等任务。您可以使用Observable和操作符来创建和转换数据流,使用Subject和BehaviorSubject来创建和处理事件流,以及使用Subscription来管理订阅。 常用操作符:
- 转换:map、switchMap(避免请求竞态);
- 过滤:filter、debounceTime(防抖)、distinctUntilChanged;
- 组合:forkJoin(多请求并行)、combineLatest(多数据流联动);
- 错误处理:catchError、retry。
什么是Angular的HTTP模块?
HTTP模块是Angular中用于处理HTTP请求和响应的机制。它提供了一个HttpClient服务,允许您与Web服务器进行通信,并以多种格式(如JSON和XML)处理响应数据。
Angular中的可观察对象是什么?
可观察对象是RxJS中的一种类型,它允许您订阅数据流并以异步方式处理它们。在Angular中,可观察对象常用于处理HTTP请求、数据流和事件处理等任务。
Angular中的NgModule是什么?
NgModule是Angular中的一个机制,用于组织和封装应用程序的代码。它可以将组件、指令、服务和管道等逻辑单元打包成一个逻辑单元,并提供依赖注入机制。
Angular中的Change Detection是什么?如何优化应用程序的性能?
Change Detection是Angular中用于检测模型数据变化并更新视图的机制。它可以自动检测模型数据的变化,并将其同步到视图中。为了优化应用程序的性能,您可以采取多种措施,例如使用OnPush策略、减少模板中的函数调用、避免不必要的变化检测等。
Angular 中,组件间通信有哪些方式
- 父传子:通过 @Input () 装饰器,父组件模板中绑定属性,子组件用 @Input () 接收 (v19支持 input('))
- 子传父:通过 @Output () 和 EventEmitter,子组件 emit 事件,父组件监听 (v19支持 output(''))
- 服务共享:创建单例服务,组件注入后通过服务的属性 (v19支持 inject) / 方法通信
- @ViewChild ():父组件直接访问子组件实例
模板引用变量、*ngFor 中的 trackBy 作用
- 模板引用变量:用 #xxx 定义,可访问 DOM / 组件 / 指令,比如 <input #inp>;
- trackBy:优化 *ngFor 渲染,当数组数据更新时,trackBy 返回唯一标识(如 id),Angular 只会重新渲染变化的项,而非全部,提升性能。
Angular 的变更检测原理?如何手动控制变更检测?
变更检测策略:
- Default:组件及所有子组件都检测;
- OnPush:仅当输入属性(@Input)引用变化、组件自身事件触发、手动触发时检测,性能更优; 手动控制:
- 注入 ChangeDetectorRef;
- markForCheck()(标记需检测)、detectChanges()(立即检测)、detach()(脱离检测)。
Angular 路由懒加载模块的原理和实现?
Angular 编译时将懒加载模块打包为独立的 JS chunk,只有访问对应路由时才加载该 chunk,减小首屏体积;
实现步骤:
- 路由配置中用 loadChildren 替代 component,指定模块路径;
- 懒加载模块不导入到根模块;
- 示例:
{ path: 'user', loadChildren: () => import('./user/user.module').then(m => m.UserModule) }
什么是单向数据流?Angular 中如何避免双向绑定的弊端?
- 单向数据流:数据从父组件流向子组件,子组件不直接修改父组件数据,而是通过事件通知父组件修改,保证数据流向可追溯;
- 双向绑定([(ngModel)])本质:@Input + @Output 语法糖,弊端是易导致数据流向混乱;
- 优化:复杂场景用响应式表单替代模板驱动的双向绑定,手动拆分 [ngModel] 和 (ngModelChange) 控制数据流向。
async 管道的作用?为什么能避免内存泄漏?
- 作用:自动订阅 Observable/Promise,将数据渲染到模板,且自动取消订阅;
- 原理:async 管道会在组件销毁时触发 ngOnDestroy,自动取消订阅,避免手动订阅忘记取消导致的内存泄漏;
Angular 中的 Interceptor 作用?如何实现?
作用:拦截 HTTP 请求 / 响应,统一处理(添加请求头、token、错误处理、加载状态
实现步骤:
- 创建服务实现 HttpInterceptor 接口;
- 实现 intercept 方法,处理请求 / 响应;
- 在根模块中注册(providers: [{ provide: HTTP_INTERCEPTORS, useClass: MyInterceptor, multi: true }])。
Angular 项目性能优化有哪些手段?
- 变更检测:组件使用 ChangeDetectionStrategy.OnPush;
- 模板优化:*ngFor 加 trackBy、避免模板中写复杂表达式;
- 懒加载:路由懒加载、模块懒加载;
- 减少渲染:使用 *ngIf 替代 [hidden](避免 DOM 渲染);
- HTTP 优化:请求防抖 / 节流、缓存请求结果、使用 Interceptor 统一处理;
- 打包优化:开启生产模式(--prod)、tree-shaking、代码分割、压缩静态资源;
- 减少订阅泄漏:组件销毁时取消 RxJS 订阅(takeUntil/async 管道)。