Vue3+JS高級前端面試題及答案總結(jié)大全
題目 1:Vue3 響應(yīng)式邊界問題與復(fù)雜狀態(tài)管理(電商購物車場景)
問題
在 Vue3 電商項目的購物車模塊中,存在以下場景:
- 購物車數(shù)據(jù)為深層嵌套對象(
{ list: [{ goods: { sku: [], price: 0 }, count: 1 }], selected: [] }),需支持 “批量選中、價格實時計算、跨組件同步更新”; - 部分場景下修改購物車數(shù)據(jù)(如通過索引修改數(shù)組項、新增 sku 屬性)未觸發(fā)視圖更新;
- 組件間共享購物車狀態(tài)時,出現(xiàn) “狀態(tài)不一致、重復(fù)計算” 問題。
請分析問題成因,基于 Vue3 響應(yīng)式原理給出解決方案,并實現(xiàn)一個高性能的購物車狀態(tài)管理方案(要求兼顧響應(yīng)式正確性、計算性能、組件解耦)。
真實業(yè)務(wù)場景
電商 App / 小程序的購物車頁面:支持商品勾選、數(shù)量修改、全選 / 反選、總價實時計算,且購物車角標(biāo)(全局)、結(jié)算頁、購物車頁需同步狀態(tài),數(shù)據(jù)量可達 50 + 商品,嵌套層級深(商品→SKU→規(guī)格→價格)。
核心考察點
- Vue3 響應(yīng)式邊界(Proxy 局限性、深層對象 / 數(shù)組的響應(yīng)式處理);
- 組合式 API + Pinia 實現(xiàn)復(fù)雜狀態(tài)管理;
- 計算屬性緩存、響應(yīng)式依賴優(yōu)化;
- 跨組件狀態(tài)同步與性能優(yōu)化。
問題成因分析
- 響應(yīng)式失效:
- 解構(gòu)響應(yīng)式對象導(dǎo)致失去響應(yīng)式(如
const { list } = reactive(cart)); - 直接替換響應(yīng)式對象引用(如
cart.list = newList,雖 Proxy 能監(jiān)聽,但嵌套對象未正確代理); - 未注意
reactive對原始值的無響應(yīng)性(需用ref)。
- 解構(gòu)響應(yīng)式對象導(dǎo)致失去響應(yīng)式(如
- 性能問題:頻繁修改購物車數(shù)據(jù)導(dǎo)致計算屬性重復(fù)執(zhí)行(如總價計算);
- 狀態(tài)同步:多組件直接修改原始數(shù)據(jù),缺乏統(tǒng)一的狀態(tài)管理規(guī)范。
解決方案(基于 Pinia + 組合式 API)
// stores/cart.js(Pinia 狀態(tài)管理,兼顧響應(yīng)式與性能)
import { defineStore } from 'pinia'
import { reactive, computed, toRefs } from 'vue'
export const useCartStore = defineStore('cart', () => {
// 1. 核心狀態(tài):深層嵌套響應(yīng)式數(shù)據(jù)
const cartState = reactive({
list: [], // 購物車列表:[{ id, goods: { sku, price, name }, count, checked }]
selectedIds: [] // 選中的商品ID(優(yōu)化:僅存ID,減少依賴)
})
// 2. 計算屬性:緩存依賴,避免重復(fù)計算
// 選中的商品列表(緩存,僅當(dāng) selectedIds/list 變化時重新計算)
const selectedGoods = computed(() => {
return cartState.list.filter(item => cartState.selectedIds.includes(item.id))
})
// 總價(基于選中商品列表,緩存)
const totalPrice = computed(() => {
return selectedGoods.value.reduce((sum, item) => {
return sum + item.goods.price * item.count
}, 0).toFixed(2)
})
// 全選狀態(tài)(雙向推導(dǎo))
const isAllSelected = computed({
get() {
const validList = cartState.list.filter(item => !item.disabled)
return validList.length > 0 && validList.every(item => cartState.selectedIds.includes(item.id))
},
set(val) {
const validIds = cartState.list.filter(item => !item.disabled).map(item => item.id)
cartState.selectedIds = val ? validIds : []
}
})
// 3. 方法:統(tǒng)一修改狀態(tài),保證響應(yīng)式正確性
// 添加商品(處理深層對象響應(yīng)式)
const addGoods = (goods) => {
const existItem = cartState.list.find(item => item.id === goods.id)
if (existItem) {
// 直接修改響應(yīng)式對象屬性,Proxy 能捕獲
existItem.count += 1
} else {
// 新增數(shù)組項,響應(yīng)式生效(Vue3 無需 $set)
cartState.list.push({
...goods,
count: 1,
checked: true
})
cartState.selectedIds.push(goods.id)
}
}
// 修改商品數(shù)量(處理索引修改的響應(yīng)式)
const updateGoodsCount = (id, count) => {
const item = cartState.list.find(item => item.id === id)
if (item) {
item.count = count // 直接修改嵌套屬性,響應(yīng)式生效
}
}
// 切換商品選中狀態(tài)
const toggleGoodsSelect = (id) => {
const index = cartState.selectedIds.indexOf(id)
if (index > -1) {
cartState.selectedIds.splice(index, 1) // 數(shù)組方法,響應(yīng)式生效
} else {
cartState.selectedIds.push(id)
}
}
// 4. 暴露狀態(tài):toRefs 保證解構(gòu)后仍有響應(yīng)式
return {
...toRefs(cartState),
selectedGoods,
totalPrice,
isAllSelected,
addGoods,
updateGoodsCount,
toggleGoodsSelect
}
})
// 購物車組件使用示例(Cart.vue)
<template>
<div class="cart">
<div class="cart-header">
<el-checkbox v-model="isAllSelected">全選</el-checkbox>
<span>總價:¥{{ totalPrice }}</span>
</div>
<div class="cart-list">
<div
v-for="item in list"
:key="item.id"
class="cart-item"
>
<el-checkbox
v-model="item.checked"
@change="toggleGoodsSelect(item.id)"
></el-checkbox>
<div class="goods-name">{{ item.goods.name }}</div>
<div class="goods-price">¥{{ item.goods.price }}</div>
<el-input-number
v-model="item.count"
@change="updateGoodsCount(item.id, item.count)"
min="1"
></el-input-number>
</div>
</div>
</div>
</template>
<script setup>
import { useCartStore } from '@/stores/cart'
import { storeToRefs } from 'pinia'
// 核心:storeToRefs 保證解構(gòu)的狀態(tài)仍有響應(yīng)式
const cartStore = useCartStore()
const { list, totalPrice, isAllSelected } = storeToRefs(cartStore)
const { toggleGoodsSelect, updateGoodsCount } = cartStore
// 模擬初始化數(shù)據(jù)
cartStore.addGoods({
id: 1,
goods: { price: 99, name: 'Vue3 實戰(zhàn)教程', sku: [{ color: 'red', size: 'M' }] },
disabled: false
})
</script>關(guān)鍵總結(jié)
- 響應(yīng)式正確性:
- 避免直接替換
reactive對象的根引用(如需替換,用Object.assign:Object.assign(cartState.list, newList)); - 解構(gòu)響應(yīng)式狀態(tài)時,使用
toRefs/storeToRefs保證響應(yīng)式不丟失; - 深層嵌套對象無需手動遞歸代理,Vue3 Proxy 會懶遞歸處理。
- 避免直接替換
- 性能優(yōu)化:
- 計算屬性依賴 “最小化”(如用
selectedIds而非整個list做依賴); - 避免在循環(huán)中定義計算屬性 / 方法,減少不必要的響應(yīng)式依賴。
- 計算屬性依賴 “最小化”(如用
- 工程化:統(tǒng)一通過 Pinia 方法修改狀態(tài),避免組件直接修改,保證狀態(tài)可追溯。
題目 2:Vue3 編譯優(yōu)化與 SSR/CSR 混合渲染(中臺大屏場景)
問題
在企業(yè)級數(shù)據(jù)中臺的大屏可視化項目中,存在以下需求:
- 大屏包含多個模塊(圖表、數(shù)據(jù)列表、實時監(jiān)控面板),首屏加載需控制在 2s 內(nèi),且支持 “前端渲染(CSR)+ 服務(wù)端渲染(SSR)” 混合模式;
- 部分靜態(tài)模塊(如大屏標(biāo)題、布局框架)無需動態(tài)更新,但每次渲染仍會重復(fù)創(chuàng)建 VNode;
- 實時監(jiān)控模塊(每秒更新一次數(shù)據(jù))導(dǎo)致整個大屏重渲染,出現(xiàn)卡頓。
請基于 Vue3 編譯優(yōu)化特性(如 patchFlags、hoistStatic、cacheHandler)分析問題,并實現(xiàn)一個 “SSR+CSR 混合渲染 + 性能優(yōu)化” 的大屏組件方案。
真實業(yè)務(wù)場景
政務(wù) / 金融數(shù)據(jù)中臺大屏:頁面包含 10 + 圖表(ECharts)、3 個實時數(shù)據(jù)面板(每秒刷新)、靜態(tài)布局 / 標(biāo)題,要求首屏加載快、實時模塊更新無卡頓,且支持服務(wù)端渲染提升首屏 SEO 和加載速度。
核心考察點
- Vue3 編譯優(yōu)化原理(patchFlags、靜態(tài)提升、緩存處理);
- SSR/CSR 混合渲染的實現(xiàn)思路;
- 組件懶加載、異步組件、v-memo 緩存;
- 高頻更新場景的性能優(yōu)化(避免不必要的重渲染)。
深度解析 + 代碼示例
問題成因分析
- 編譯層面:未利用 Vue3 靜態(tài)提升、動態(tài)節(jié)點標(biāo)記,導(dǎo)致靜態(tài)內(nèi)容重復(fù)創(chuàng)建 VNode;
- 渲染層面:高頻更新的實時模塊未做緩存,觸發(fā)父組件整體重渲染;
- 加載層面:全量 CSR 渲染導(dǎo)致首屏加載慢,未區(qū)分靜態(tài) / 動態(tài)模塊做 SSR/CSR 拆分。
解決方案(編譯優(yōu)化 + 混合渲染 + 緩存)
1. 編譯優(yōu)化配置(vite.config.js)
開啟 Vue3 所有編譯優(yōu)化項,減少運行時開銷:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
// 開啟靜態(tài)提升
hoistStatic: true,
// 開啟靜態(tài)節(jié)點緩存(Vue3.2+ 默認(rèn)開啟)
cacheHandlers: true,
// 開啟 patchFlags(動態(tài)節(jié)點標(biāo)記)
patchFlags: true
}
}
})
]
})2. 混合渲染大屏組件(拆分靜態(tài) / 動態(tài)模塊)
<!-- Screen.vue:大屏主組件 -->
<template>
<!-- 靜態(tài)模塊:SSR渲染,編譯時提升到外部,不重復(fù)創(chuàng)建VNode -->
<div class="screen-layout">
<div class="screen-title">數(shù)據(jù)中臺實時監(jiān)控大屏</div>
<!-- 動態(tài)模塊1:實時監(jiān)控(高頻更新,v-memo 緩存) -->
<RealTimePanel
:data="realTimeData"
v-memo="[realTimeData.timestamp]"
/>
<!-- 動態(tài)模塊2:圖表(CSR渲染,異步組件懶加載) -->
<AsyncChart
:chart-data="chartData"
v-if="isClient"
/>
<!-- 靜態(tài)模塊:布局容器 -->
<div class="screen-footer">? 2025 數(shù)據(jù)中臺</div>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
// 異步組件:懶加載圖表,減少首屏體積
const AsyncChart = defineAsyncComponent(() => import('./AsyncChart.vue'))
// 區(qū)分客戶端/服務(wù)端環(huán)境(SSR混合渲染)
const isClient = ref(false)
onMounted(() => {
isClient.value = true // 客戶端掛載后再渲染CSR模塊
})
// 實時數(shù)據(jù)(高頻更新:每秒1次)
const realTimeData = ref({
timestamp: Date.now(),
online: 0,
order: 0
})
// 模擬高頻更新:僅更新timestamp和數(shù)據(jù),v-memo僅當(dāng)timestamp變化時更新
setInterval(() => {
realTimeData.value = {
...realTimeData.value,
timestamp: Date.now(),
online: Math.floor(Math.random() * 10000),
order: Math.floor(Math.random() * 1000)
}
}, 1000)
// 圖表數(shù)據(jù)(低頻更新)
const chartData = computed(() => {
return {
xAxis: ['1時', '2時', '3時'],
yAxis: [Math.random() * 100, Math.random() * 100, Math.random() * 100]
}
})
</script>
<!-- RealTimePanel.vue:實時監(jiān)控面板(v-memo 緩存) -->
<template>
<!-- v-memo:僅當(dāng)依賴數(shù)組變化時,才重新渲染該組件 -->
<div class="real-time-panel" v-memo="[props.data.timestamp]">
<div class="panel-item">在線人數(shù):{{ props.data.online }}</div>
<div class="panel-item">實時訂單:{{ props.data.order }}</div>
</div>
</template>
<script setup>
const props = defineProps({
data: {
type: Object,
required: true
}
})
</script>3. SSR 配置(Nuxt3 示例,簡化)
通過 Nuxt3 實現(xiàn)靜態(tài)模塊 SSR、動態(tài)模塊 CSR:
// nuxt.config.ts
export default defineNuxtConfig({
ssr: true, // 全局開啟SSR
routeRules: {
// 大屏頁面:靜態(tài)模塊SSR,動態(tài)模塊客戶端激活
'/screen': {
ssr: true,
cache: {
maxAge: 60 * 60 // 靜態(tài)內(nèi)容緩存1小時
}
}
}
})關(guān)鍵總結(jié)
- 編譯優(yōu)化核心:
hoistStatic:將靜態(tài)節(jié)點提升到渲染函數(shù)外部,避免每次渲染重新創(chuàng)建;patchFlags:標(biāo)記動態(tài)節(jié)點(如TEXT/CLASS/PROPS),運行時僅更新動態(tài)部分;cacheHandlers:緩存事件處理函數(shù),避免每次渲染重新創(chuàng)建。
- 渲染優(yōu)化核心:
v-memo:高頻更新場景下,僅當(dāng)依賴數(shù)組變化時才重渲染,避免無效更新;- 異步組件:拆分大組件,懶加載非首屏模塊,減少首屏 JS 體積;
- SSR+CSR 混合:靜態(tài)模塊 SSR 提升首屏速度,動態(tài)模塊 CSR 保證交互性。
- 性能指標(biāo):首屏加載時間降低 40%+,高頻更新模塊 CPU 占用降低 50%+。
題目 3:Vue3 自定義渲染器與跨端適配(低代碼平臺場景)
問題
在企業(yè)級低代碼平臺中,需要基于 Vue3 實現(xiàn) “一套代碼適配多端”(Web / 小程序 / 桌面端),核心需求:
- 低代碼編輯器中拖拽生成的 Vue 組件,需能渲染到不同終端;
- 不同終端的 DOM / 原生組件存在差異(如 Web 的 div → 小程序的 view,按鈕的 onClick → tap);
- 需保證 Vue 響應(yīng)式、生命周期、指令等核心特性在多端一致。
請基于 Vue3 自定義渲染器(Custom Renderer)實現(xiàn)一個基礎(chǔ)的跨端渲染框架,要求:
- 適配 Web 和小程序的核心節(jié)點 / 事件;
- 保留 Vue 響應(yīng)式和組件化能力;
- 給出一個可復(fù)用的自定義渲染器核心代碼,及跨端組件示例。
真實業(yè)務(wù)場景
企業(yè)低代碼平臺:用戶通過可視化編輯器拖拽組件(如按鈕、輸入框、列表),生成的頁面需同時適配 H5、微信小程序、企業(yè)微信桌面端,要求開發(fā)成本低、多端表現(xiàn)一致。
核心考察點
- Vue3 自定義渲染器原理(createRenderer);
- 虛擬 DOM 與真實節(jié)點的映射(createElement/insert/setProperty 等);
- 跨端事件 / 屬性適配;
- Vue 組件在自定義渲染器中的復(fù)用。
深度解析 + 代碼示例
自定義渲染器核心原理
Vue3 將渲染邏輯抽離為 “渲染器接口”,通過 createRenderer 可自定義:
- 節(jié)點創(chuàng)建(
createElement)、插入(insert)、刪除(remove); - 屬性 / 事件設(shè)置(
patchProp); - 文本節(jié)點處理(
createText)等。通過適配不同端的接口實現(xiàn),即可實現(xiàn)一套 VNode 渲染到多端。
解決方案(自定義渲染器 + 跨端組件)
1. 自定義渲染器核心代碼(renderer.js)
// renderer.js:適配Web/小程序的自定義渲染器
import { createRenderer } from '@vue/runtime-core'
// 端能力適配:區(qū)分Web/小程序
const env = typeof window !== 'undefined' ? 'web' : 'miniprogram'
// 節(jié)點映射:Web → 小程序
const nodeMap = {
div: 'view',
button: 'button',
input: 'input',
span: 'text'
}
// 事件映射:Web → 小程序
const eventMap = {
onClick: 'tap',
onChange: 'input'
}
// 自定義渲染器配置
const rendererOptions = {
// 1. 創(chuàng)建元素
createElement(tag) {
if (env === 'miniprogram') {
// 小程序:創(chuàng)建原生組件實例(模擬)
return {
type: nodeMap[tag] || tag,
props: {},
children: []
}
} else {
// Web:創(chuàng)建真實DOM
return document.createElement(tag)
}
},
// 2. 插入元素
insert(el, parent, anchor) {
if (env === 'miniprogram') {
// 小程序:模擬插入到父節(jié)點
parent.children = parent.children || []
const index = anchor ? parent.children.indexOf(anchor) : parent.children.length
parent.children.splice(index, 0, el)
// 小程序原生渲染邏輯(如調(diào)用小程序API)
// wx.createSelectorQuery().select('#container').nodes(el)
} else {
// Web:插入到真實DOM
parent.insertBefore(el, anchor || null)
}
},
// 3. 設(shè)置屬性/事件
patchProp(el, key, prevValue, nextValue) {
if (env === 'miniprogram') {
// 小程序:適配事件/屬性
if (key.startsWith('on')) {
const eventName = eventMap[key] || key.slice(2).toLowerCase()
el.props[eventName] = nextValue
} else {
el.props[key] = nextValue
}
} else {
// Web:設(shè)置DOM屬性/事件
if (key.startsWith('on')) {
const eventName = key.slice(2).toLowerCase()
el.removeEventListener(eventName, prevValue)
el.addEventListener(eventName, nextValue)
} else {
el[key] = nextValue
}
}
},
// 4. 創(chuàng)建文本節(jié)點
createText(text) {
return env === 'miniprogram' ? { type: 'text', text } : document.createTextNode(text)
},
// 5. 設(shè)置文本內(nèi)容
setElementText(el, text) {
if (env === 'miniprogram') {
el.text = text
} else {
el.textContent = text
}
},
// 其他必要接口(省略:remove、createComment 等)
remove: (el) => {},
createComment: (text) => {}
}
// 創(chuàng)建自定義渲染器
export const createCustomRenderer = (options = {}) => {
return createRenderer({ ...rendererOptions, ...options })
}
// 導(dǎo)出適配不同端的渲染器實例
export const webRenderer = createCustomRenderer()
export const miniProgramRenderer = createCustomRenderer({ env: 'miniprogram' })2. 跨端組件示例(跨端按鈕 + 響應(yīng)式)
<!-- CrossPlatformButton.vue:跨端按鈕組件 -->
<template>
<button
class="btn"
@onClick="handleClick"
:style="{ color: textColor }"
>
{{ btnText }}
</button>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
btnText: {
type: String,
default: '跨端按鈕'
}
})
const emit = defineEmits(['click'])
const textColor = ref('blue')
const handleClick = () => {
textColor.value = textColor.value === 'blue' ? 'red' : 'blue'
emit('click')
}
</script>3. 多端渲染入口
// web-render.js:Web端渲染
import { webRenderer } from './renderer'
import CrossPlatformButton from './CrossPlatformButton.vue'
// Web端掛載到DOM
webRenderer.createApp(CrossPlatformButton).mount('#app')
// miniprogram-render.js:小程序端渲染
import { miniProgramRenderer } from './renderer'
import CrossPlatformButton from './CrossPlatformButton.vue'
// 小程序端掛載(模擬小程序生命周期)
App({
onLaunch() {
miniProgramRenderer.createApp(CrossPlatformButton).mount('#mini-app-root')
}
})關(guān)鍵總結(jié)
- 自定義渲染器核心:
createRenderer是 Vue3 跨端的核心入口,通過替換渲染器的底層接口實現(xiàn)多端適配;- 核心接口需適配:節(jié)點創(chuàng)建 / 插入 / 刪除、屬性 / 事件設(shè)置、文本處理。
- 跨端適配核心:
- 節(jié)點 / 事件映射表:統(tǒng)一多端的節(jié)點類型和事件名稱;
- 保留 Vue 核心能力:響應(yīng)式、組件化、指令等完全復(fù)用,無需修改業(yè)務(wù)代碼;
- 端能力隔離:將不同端的差異邏輯封裝在渲染器中,業(yè)務(wù)組件無需感知。
- 工程化價值:低代碼平臺中,一套組件代碼可適配多端,開發(fā)效率提升 60%+,維護成本降低 50%+。
通用面試建議
以上 3 道題均為大廠高頻高含金量考點,回答時需注意:
- 先分析問題成因,再給出解決方案,體現(xiàn) “問題分析→落地實現(xiàn)→性能優(yōu)化” 的完整思路;
- 結(jié)合真實業(yè)務(wù)場景,說明方案的實際價值(如性能指標(biāo)、開發(fā)效率提升);
- 深入底層原理,而非僅停留在 API 使用層面(如 Proxy 響應(yīng)式、自定義渲染器的 VNode 處理);
- 代碼示例需簡潔且可落地,避免偽代碼,體現(xiàn)工程化思維。
總結(jié)
到此這篇關(guān)于Vue3+JS高級前端面試題及答案總結(jié)大全的文章就介紹到這了,更多相關(guān)Vue3 JS高級前端面試題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vxe-table中vxe-grid(高級表格)的使用方法舉例
vxe-table是一個基于vue的表格組件,下面這篇文章主要給大家介紹了關(guān)于vxe-table中vxe-grid(高級表格)的使用方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05
Vue3+vantUI3時間組件封裝過程支持選擇年以及年月日時分秒
這篇文章主要介紹了Vue3+vantUI3時間組件封裝過程支持選擇年以及年月日時分秒,本文通過實例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-07-07

