一、响应式数据相关坑点
1. 对象属性新增/删除未触发更新
- 现象:
this.obj = { a: 1 } this.obj.b = 2 // 新增属性b,视图不更新 delete this.obj.a // 删除属性a,视图不更新 - 原因:
Vue 通过Object.defineProperty监听属性变化,新增/删除属性不会触发响应式系统。 - 解决方案:
// 新增属性 this.$set(this.obj, 'b', 2) // Vue.set 方法 Object.assign(this.obj, { b: 2 }) // 批量更新 // 删除属性 this.$delete(this.obj, 'a') // 触发视图更新
2. 数组变异方法失效
- 现象:使用非变异方法(如
filter、map)更新数组后视图不更新。 - 解决方案:
// 错误写法(非变异) this.list = this.list.filter(item => item.id > 10) // 正确写法(触发更新) this.list = this.list.filter(item => item.id > 10) // 重新赋值触发响应式 this.list.splice(0, this.list.length, ...newList) // 用变异方法更新
二、组件通信与生命周期坑点
1. 父子组件更新顺序导致的问题
- 现象:父组件更新后,子组件未及时获取新 props。
- 解决方案:
// 子组件中监听props变化 watch: { parentProp: { handler(newVal, oldVal) { // 处理更新逻辑 }, immediate: true // 初始化时执行 } } // 或使用nextTick this.$nextTick(() => { // 父组件更新完成后执行 })
2. 跨组件通信导致的内存泄漏
- 现象:使用事件总线(EventBus)后未销毁事件监听,导致组件卸载后仍触发回调。
- 解决方案:
// 组件挂载时 this.$bus.$on('event', this.handleEvent) // 组件卸载时 beforeDestroy() { this.$bus.$off('event', this.handleEvent) }
三、性能与渲染坑点
1. 大量列表渲染导致卡顿
- 现象:长列表更新时页面卡顿,FPS 下降。
- 解决方案:
// 1. 虚拟滚动(Vue-virtual-scroll-list) <virtual-list :data-key="'id'" :data-sources="hugeList" /> // 2. 分批渲染(示例逻辑) mounted() { this.loadDataByChunk() }, methods: { loadDataByChunk() { const chunkSize = 20 const total = this.totalData.length let offset = 0 const timer = setInterval(() => { this.displayList.push(...this.totalData.slice(offset, offset + chunkSize)) offset += chunkSize if (offset >= total) { clearInterval(timer) } }, 16) // 16ms约等于60FPS } }
2. v-for 缺少唯一 key 导致的重渲染
- 现象:列表项顺序变化时,diff 算法误判节点,导致不必要的 DOM 操作。
- 解决方案:
<!-- 错误写法(用index作key) --> <div v-for="(item, index) in list" :key="index">...</div> <!-- 正确写法(用唯一id作key) --> <div v-for="item in list" :key="item.id">...</div>
四、路由与异步请求坑点
1. 路由守卫中异步操作未处理
- 现象:在
beforeRouteEnter中发起异步请求,导致路由跳转阻塞。 - 解决方案:
beforeRouteEnter(to, from, next) { axios.get('/api/data').then(res => { next(vm => { vm.data = res.data }) }).catch(err => { next({ name: 'ErrorPage' }) }) }
2. 异步组件加载失败未处理
- 现象:动态导入组件时网络错误,页面显示空白。
- 解决方案:
const AsyncComponent = () => import(/* webpackError: '组件加载失败' */ './AsyncComponent.vue') // 或手动处理错误 const loadComponent = () => { return import('./AsyncComponent.vue').catch(err => { // 加载失败时显示错误组件 return { template: '<div>组件加载失败,请刷新页面</div>' } }) }
五、工程化与构建坑点
1. CSS 作用域冲突
- 现象:组件样式影响其他组件,或被其他组件样式覆盖。
- 解决方案:
<!-- 1. 使用scoped属性 --> <style scoped> .container { color: red; } </style> <!-- 2. 命名空间约定(BEM规范) --> <div class="component-name__container">...</div>
2. 打包后文件体积过大
- 解决方案:
// webpack配置示例(Vue CLI项目修改vue.config.js) module.exports = { chainWebpack: config => { // 1. 分包处理 config.optimization.splitChunks({ chunks: 'all', maxInitialRequests: 5, minSize: 0, cacheGroups: { vendor: { name: 'vendor', chunks: 'initial', priority: 10, test: /node_modules/ } } }) // 2. 按需引入组件(以Element Plus为例) config.plugin('component').use(Component, { libraries: [{ libraryName: 'element-plus', style: true }] }) } }
六、问题
1. 问:为什么Vue.set才能触发响应式?
Vue 通过 `Object.defineProperty` 监听已有属性,新增属性时需手动调用 `Vue.set`(或 `this.$set`)来为新属性添加 getter/setter,从而触发响应式系统。
2. 问:v-for和v-if同时使用有什么问题?
- **性能问题**:v-if 会先于 v-for 执行,导致每次循环都要判断条件;
- **正确做法**:先通过计算属性过滤列表,再使用 v-for 渲染:
```js
computed: {
filteredList() {
return this.list.filter(item => item.visible)
}
}
```
3. 问:如何监控Vue应用的性能瓶颈?
- 使用 `vue-devtools` 监控组件更新频率和耗时;
- 通过浏览器 Performance 面板分析渲染帧速率(理想保持60FPS);
- 对频繁更新的组件使用 `shouldComponentUpdate` 或 `v-once` 优化。