在Vue中踩过的坑

91 阅读1分钟

一、响应式数据相关坑点

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. 数组变异方法失效
  • 现象:使用非变异方法(如 filtermap)更新数组后视图不更新。
  • 解决方案
    // 错误写法(非变异)
    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` 优化。