2025-11-10
一,Vue3 中如何优化大型列表渲染性能?结合实际场景说明
实际项目中遇过表格数据量超 1000 条时的卡顿问题,解决方案:
- 用
v-virtual-scroller(如 vue-virtual-scroller 库)实现虚拟滚动,只渲染可视区 DOM; - 列表项用
defineAsyncComponent异步加载,拆分组件减少初始渲染压力; - 避免用
v-for同时绑定v-if,改用计算属性过滤数据后再渲染。 <template><!-- 虚拟滚动:只渲染可视区DOM --><virtual-scroller :items="filteredList" :item-height="60"class="scroller"><template #default="{ item }"><!-- 异步组件:拆分列表项,减少初始加载压力 --><AsyncListItem :data="item" /></template></virtual-scroller> </template><script setup> import { computed } from 'vue'; import { VirtualScroller } from 'vue-virtual-scroller'; // 异步引入列表项组件 const AsyncListItem = defineAsyncComponent(() => import('./ListItem.vue'));const props = defineProps({ list: { type: Array, default: () => [] } }); // 用计算属性提前过滤数据,避免v-for与v-if共存 const filteredList = computed(() => props.list.filter(item => item.status !== 'deleted')); </script>
二,远程示教项目中,如何实现师生端与学生端的实时操作同步?(基于 Vue3+WebSocket)
场景:老师在白板绘图时,学生端需实时显示同步轨迹。
<!-- 白板组件 Whiteboard.vue -->
<script setup>
import { ref, onMounted } from 'vue';
import { useWebSocket } from './useWebSocket'; // 封装的WS hookconst { sendMsg } = useWebSocket('wss://your-server.com/teach');
const lines = ref([]); // 存储绘图轨迹
let isTeacher = ref(false); // 区分角色// 老师端:绘制时发送轨迹到服务端
const handleDraw = (point) => {if (isTeacher.value) {lines.value.push(point);sendMsg({ type: 'draw', data: point }); // 发送轨迹数据}
};// 学生端:接收服务端推送的轨迹
onMounted(() => {useWebSocket().onMessage((msg) => {if (msg.type === 'draw' && !isTeacher.value) {lines.value.push(msg.data); // 同步显示}});
});
</script>核心:用 WebSocket 实现双向通信,Vue 响应式数据lines驱动两端视图同步,通过角色区分控制发送权限。
三,远程示教中,如何优化大文件(如课件)分片上传体验?(Vue3+Axios)
场景:老师上传 100MB + 课件,需支持断点续传、进度显示。
<!-- 上传组件 FileUpload.vue -->
<template><input type="file" @change="handleFileSelect" /><div>进度:{{ progress }}%</div>
</template><script setup>
import { ref } from 'vue';
import axios from 'axios';
import { getFileHash } from './fileUtils'; // 计算文件唯一hashconst progress = ref(0);
const chunkSize = 5 * 1024 * 1024; // 5MB/片const handleFileSelect = async (e) => {const file = e.target.files[0];const fileHash = await getFileHash(file); // 生成文件唯一标识// 1. 先查询已上传分片const { data: uploadedChunks } = await axios.get(`/check?hash=${fileHash}`);// 2. 分片上传剩余部分const totalChunks = Math.ceil(file.size / chunkSize);for (let i = 0; i < totalChunks; i++) {if (uploadedChunks.includes(i)) continue; // 跳过已上传分片const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);const formData = new FormData();formData.append('chunk', chunk);formData.append('hash', fileHash);formData.append('index', i);await axios.post('/upload', formData, {onUploadProgress: (e) => {// 计算整体进度progress.value = Math.round((i + e.loaded / e.total) / totalChunks * 100);}});}// 3. 通知服务端合并分片await axios.post('/merge', { hash: fileHash, filename: file.name });
};
</script>优势:通过文件 hash 实现断点续传,分片上传减少单次请求压力,实时进度反馈提升远程教学中的用户体验。
四,Vue3 中如何实现组件间通信?结合实际场景说明不同方案的选型(考频极高)
核心方案:根据组件关系选择(父子 / 跨级 / 兄弟),实际项目中需结合场景灵活运用。
(1)父子组件:props + emit(最基础场景,如表单组件)
<!-- 父组件 Parent.vue -->
<template><Child :maxLength="10" @input-change="handleInput" />
</template>
<script setup>
const handleInput = (val) => console.log('子组件输入:', val)
</script><!-- 子组件 Child.vue -->
<template><input @input="$emit('input-change', $event.target.value)":maxlength="maxLength">
</template>
<script setup>
const props = defineProps({ maxLength: Number })
</script>(2)跨级 / 全局:provide + inject(如主题配置、权限信息)
<!-- 根组件 App.vue -->
<script setup>
import { provide } from 'vue'
const theme = ref('dark')
provide('theme', { theme, setTheme: (t) => theme.value = t })
</script><!-- 深层子组件 DeepChild.vue -->
<script setup>
import { inject } from 'vue'
const { theme, setTheme } = inject('theme') // 直接获取,无需逐层传递
</script>(3)兄弟 / 任意组件:Pinia 状态管理(如购物车与导航栏的数量同步)
// stores/cart.js
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', {state: () => ({ count: 0 }),actions: { increment() { this.count++ } }
})// 组件 A(添加商品)
<script setup>
const cart = useCartStore()
cart.increment()
</script>// 组件 B(显示数量)
<script setup>
const cart = useCartStore()
</script>
<template>{{ cart.count }}</template>选型原则:
- 父子优先用
props/emit,简单直接 - 跨级且状态稳定用
provide/inject(避免滥用导致数据流混乱) - 复杂交互或全局状态必用 Pinia
五,Vue3 中如何优化组件性能?结合实际项目举例(考频高)
核心方案:减少不必要的渲染与计算,实际项目中常用于列表、表单等高频交互场景。
(1)v-memo 缓存列表渲染(如商品列表筛选后复用 DOM)
<template><div v-for="item in list" :key="item.id"><!-- 仅当 item.id 或 item.price 变化时才重新渲染 --><div v-memo="[item.id, item.price]">{{ item.name }} - ¥{{ item.price }}</div></div>
</template>(2)computed 缓存计算结果(如购物车总价计算)
<script setup>
import { computed } from 'vue'
const cart = useCartStore()
// 仅当 cart.items 变化时重新计算,避免每次渲染执行
const total = computed(() => cart.items.reduce((sum, item) => sum + item.price, 0))
</script>(3)shallowRef/shallowReactive 减少响应式开销(如大型表格数据)
<script setup>
import { shallowRef } from 'vue'
// 仅监听数据引用变化,不追踪内部属性(适合纯展示、不修改内部的大数据)
const bigTableData = shallowRef([]) // 初始化时替换引用触发更新
const fetchData = async () => {bigTableData.value = await api.getLargeData()
}
</script>(4)组件拆分与 defineAsyncComponent(路由懒加载)
// router/index.js
import { defineAsyncComponent } from 'vue'
const OrderDetail = defineAsyncComponent(() => import('@/views/OrderDetail.vue'))// 路由配置
const routes = [{ path: '/order/:id', component: OrderDetail }]优化核心:明确响应式边界,避免 “牵一发而动全身”,针对渲染瓶颈(列表、大数据)优先使用缓存策略。
