前端高频面试题day2
如何在vue3中使用defineAsyncComponent实现异步组件加载
在 Vue 3 中,使用 defineAsyncComponent
实现异步组件加载的步骤如下:
- 引入方法:从 Vue 中导入
defineAsyncComponent
。 - 定义异步组件:通过
defineAsyncComponent
包装一个返回 Promise 的工厂函数,Promise 解析后返回组件。 - 使用组件:在模板中正常使用该异步组件。
代码示例:
import { defineAsyncComponent } from 'vue';const AsyncComp = defineAsyncComponent(() => import('./MyComponent.vue')
);export default {components: { AsyncComp }
};
特点:
- 支持懒加载,优化性能。
- 可选配置如
loadingComponent
和errorComponent
提升用户体验。
v-show和v-if有什么区别?使用场景分别是什么
区别:
- 实现方式:
v-show
通过display: none
控制显示,元素始终在DOM中;v-if
条件为假时直接移除元素,为真时重新渲染。 - 性能:
v-show
适合频繁切换(仅改CSS);v-if
适合不常变化的条件(初始渲染更轻量)。 - 特性:
v-if
支持v-else
/v-else-if
,切换会触发组件销毁/创建钩子;v-show
不支持且不会。
场景:
v-show
:需频繁切换显示的元素(如快速切换的标签页)。v-if
:条件极少变化、复杂组件或初始无需渲染的场景。
vue计算属性的函数名和data中的属性可以同名吗?为什么?
在 Vue 中,计算属性的函数名和 data
中的属性 不可以同名,原因如下:
原因:
-
属性覆盖机制
Vue 将data
、computed
等属性都挂载到 Vue 实例上。当名称冲突时,后定义的属性会覆盖先定义的属性。- 初始化顺序为:
props → methods → data → computed → watch
。 - 因此,若计算属性与
data
同名,计算属性会覆盖data
中的同名属性,导致data
中的原始数据无法被访问。
- 初始化顺序为:
-
不可预测的逻辑错误
- 在模板中使用该名称时,实际调用的是计算属性的结果,而非
data
中的原始数据。 - 若尝试修改
data
中的同名属性,会因计算属性的只读性导致错误(除非计算属性有setter
)。
- 在模板中使用该名称时,实际调用的是计算属性的结果,而非
Vue 的警告机制
- 当发生命名冲突时,Vue 会通过控制台输出警告,例如:
[Vue warn]: The computed property "xxx" is already defined in data.
最佳实践
-
命名规范
data
属性与计算属性使用不同名称:data() {return {originalMessage: 'Hello' // 原始数据}; }, computed: {uppercaseMessage() { // 计算属性名与 data 区分return this.originalMessage.toUpperCase();} }
-
代码审查
- 通过 ESLint 或 TypeScript 类型检查避免同名冲突(如定义接口时明确区分字段)。
总结
- 技术上允许同名,但会导致属性覆盖和逻辑混乱。
- 强烈建议避免同名,通过清晰的命名规范提升代码可维护性。
如何监听Vuex数据的变化?
在 Vue 中监听 Vuex 数据的变化,可以通过以下几种方法实现,根据场景选择最合适的方案:
1. 计算属性 + watch
- 适用场景:在组件内监听特定状态的变化。
- 步骤:
- 通过计算属性映射 Vuex 状态。
- 在
watch
中监听计算属性的变化。
2. 直接使用 watch
监听 Vuex 状态
- 适用场景:直接监听 Vuex 状态,无需计算属性。
- 步骤:
- 在
watch
中直接指定 Vuex 状态的路径。 - 可选
deep
选项处理嵌套对象/数组。
- 在
3. Vuex 的 store.watch
方法
- 适用场景:在非组件环境(如工具函数、服务层)监听状态变化。
- 步骤:
- 通过
store.watch
传入状态获取函数和回调。 - 返回的取消函数用于停止监听。
- 通过
性能注意事项
- 嵌套对象/数组:使用
deep: true
时性能开销较大,尽量避免深层监听。 - 取消监听:在组件销毁前调用
unwatch()
或unsubscribe()
,防止内存泄漏。 - 优先级:频繁变化的状态建议用计算属性,减少
watch
的回调执行次数。
Vue Router路由守卫
-
什么是路由守卫?
路由守卫是 Vue Router 提供的钩子函数,用于在路由跳转的不同阶段插入控制逻辑(如权限验证、数据预加载、页面标题切换等)。它允许开发者在导航过程中拦截、检查或修改路由行为。 -
路由守卫的分类
Vue Router 的路由守卫分为三类:- 全局守卫:对所有路由生效,如
beforeEach
、beforeResolve
、afterEach
。 - 路由独享守卫:仅对特定路由生效,定义在路由配置的
beforeEnter
属性中。 - 组件内守卫:定义在组件内部,如
beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
。
- 全局守卫:对所有路由生效,如
如何解决前端接口大规模并发问题
一、前端优化方案
-
减少请求次数
- 合并请求(Batch API)
- 场景:多个小请求合并为一个请求(如实时搜索、分页加载)。
- 实现:使用防抖/节流或自定义批量接口。
- 代码示例(防抖合并请求):
let timer = null; const requests = []; function debounceRequest() {if (timer) clearTimeout(timer);timer = setTimeout(() => {// 合并请求并发送const batchRequest = requests.map(req => req.params);fetch('/batch-endpoint', { method: 'POST', body: JSON.stringify(batchRequest) }).then(data => handleResponse(data));requests.length = 0; // 清空队列}, 300); } // 每次触发请求时加入队列 function addRequest(params) {requests.push({ params });debounceRequest(); }
- 接口聚合(BFF层)
- 原理:通过后端为前端(BFF)聚合多个接口,减少前端请求次数。
- 示例:将多个独立接口合并为一个聚合接口
/api/v1/combined
。
- 数据缓存
- 浏览器缓存:通过
Cache-Control
和ETag
减少重复请求。- 协商缓存(动态数据):
ETag: "abc123"
。
- 协商缓存(动态数据):
- 本地存储:使用
localStorage
或IndexedDB
缓存低频数据。const CACHE_KEY = 'user-profile'; const cachedData = localStorage.getItem(CACHE_KEY); if (cachedData) {return JSON.parse(cachedData); } else {return fetch('/api/user').then(data => {localStorage.setItem(CACHE_KEY, JSON.stringify(data));return data;}); }
- 浏览器缓存:通过
- 请求去重
- 实现:使用
Map
存储进行中的请求,避免重复发送。const requestMap = new Map(); async function sendRequest(url, params) {const key = `${url}-${JSON.stringify(params)}`;if (requestMap.has(key)) {return requestMap.get(key);}const promise = fetch(url, { method: 'POST', body: params }).then(response => response.json()).finally(() => requestMap.delete(key));requestMap.set(key, promise);return promise; }
- 实现:使用
-
控制并发请求
- 请求队列(限流)
- 原理:通过队列控制同时发起的请求数量,避免浏览器资源耗尽。
- 代码示例(自定义请求队列):
class RequestQueue {constructor(maxConcurrency = 5) {this.maxConcurrency = maxConcurrency;this.current = 0;this.queue = [];}add(promise) {return new Promise((resolve, reject) => {this.queue.push({ promise, resolve, reject });this.process();});}async process() {if (this.current >= this.maxConcurrency || this.queue.length === 0) return;const task = this.queue.shift();this.current++;try {const result = await task.promise;task.resolve(result);this.current--;this.process();} catch (error) {task.reject(error);this.current--;this.process();}} } // 使用示例 const queue = new RequestQueue(3); const requests = [fetch('/api/1'), fetch('/api/2'), ...]; requests.forEach(req => queue.add(req));
- 懒加载与分页
- 场景:对非首屏数据延迟加载(如表格分页、图片懒加载)。
- 请求队列(限流)
-
优化请求效率
- 静态资源优化
- CDN加速:将静态资源托管到 CDN,减少服务器压力。
- 资源合并:合并 CSS/JS 文件,减少请求数(如通过 Webpack 的
optimization.splitChunks
)。 - 图片优化:使用 WebP 格式、懒加载、按需加载不同分辨率图片。
- 使用 HTTP/2
- 优势:支持多路复用,减少请求开销。
- 静态资源优化
- 合并请求(Batch API)
# typeof和instanceof有什么区别
用 typeof
的场景:
检测基本类型(如 number
、string
、boolean
)、函数或 undefined
。
用 instanceof
的场景:
检测对象是否为某个构造函数的实例(如数组、自定义对象、Date
对象等)。
避免陷阱:
typeof null
返回"object"
(需额外判断=== null
)。- 数组用
Array.isArray()
替代instanceof Array
更可靠。 - 跨窗口环境(如
iframe
)中instanceof
可能失效。
js中null和undefined的区别
null
和 undefined
的区别
null
:表示空值,需手动赋值(如let user = null
),类型为Null
。undefined
:表示变量未声明/未赋值或属性不存在,类型为Undefined
。
关键差异:
- 类型检测:
typeof null
返回"object"
(历史遗留问题),typeof undefined
返回"undefined"
。
- 比较结果:
null == undefined
→true
,但null === undefined
→false
。
- 用途:
null
是主动设置的“无值”,undefined
是系统默认的“未定义”。
如何判断js变量是数组
判断变量是否为数组的最简方法:
-
Array.isArray()
Array.isArray([1,2,3]); // true Array.isArray({}); // false
-
Object.prototype.toString
Object.prototype.toString.call(arr) === "[object Array]";
-
instanceof
(需谨慎)arr instanceof Array; // 可能受跨窗口影响
关键点:
- 推荐:优先用
Array.isArray()
(简洁可靠)或toString
(跨环境通用)。 - 慎用:
instanceof
和constructor
可能因环境问题出错。 - 禁用:
typeof
返回object
,无法准确判断数组。
Js有哪些数据类型,它们的区别是什么
JavaScript 数据类型及区别
原始类型(7种)
直接存值,不可变,存栈内存,用 typeof
检测:
undefined
:未定义(let a;
)。null
:空值(类型检测异常:typeof null → "object"
)。boolean
:布尔值(true/false
)。number
:数值(含整数/浮点/NaN/Infinity
)。string
:字符串(不可变,如"abc"
)。symbol
:唯一标识符(ES6,防属性冲突)。bigint
:大整数(ES10,后缀n
,如123n
)。
引用类型(对象)
存堆内存,通过引用访问,可变,用 instanceof
或 Object.prototype.toString
检测:
Object
:键值对容器({key: value}
)。Array
:有序集合([1, "a", true]
)。Function
:可执行函数(function() {}
)。Date
、RegExp
等:内置对象(如日期、正则)。- ES6 新增:
Map
、Set
、Promise
等。
核心区别
特征 | 原始类型 | 引用类型 |
---|---|---|
存储位置 | 栈内存(直接存值) | 堆内存(存引用地址) |
值传递方式 | 按值拷贝(独立) | 按引用拷贝(指向同一内存) |
可变性 | 不可变 | 可变(如数组、对象可修改) |
类型检测 | typeof (null 异常) | instanceof / Object.prototype.toString |
关键注意:
typeof null → "object"
是 JavaScript 的历史遗留问题。- 判断数组用
Array.isArray()
,而非typeof
。