vue----- watch监听$attrs 的注意事项

目录

前言

原因分析 

解决方案

总结


前言

在 Vue 开发过程中,如遇到祖先组件需要传值到孙子组件时,需要在儿子组件接收 props ,然后再传递给孙子组件,通过使用 v-bind="$attrs" 则会带来极大的便利,但同时也会有一些隐患在其中。

可以看到,当我们在输入框输入值的时候,只有修改到 input 字段,从而更新父组件,而子组件的 props test 则是没有修改的,按照 谁更新,更新谁 的标准来看,子组件是不应该更新触发 updated 方法的, 

原因分析 

首先介绍一个前提,就是 Vue 在更新组件的时候是更新对应的 data 和 props 触发 Watcher 通知来更新渲染的。
每一个组件都有一个唯一对应的 Watcher ,所以在子组件上的 props 没有更新的时候,是不会触发子组件的更新的。当我们去掉子组件上的v-bind="$attrs"时可以发现, updated 钩子不会再执行,所以可以发现问题就出现在这里。


Vue 源码中搜索 $attrs ,找到 src/core/instance/render.js 文件:

export function initRender (vm: Component) {
  // ...
  defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
  defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
}

可以看到在 initRender 方法中,将 $attrs 属性绑定到了 this 上,并且设置成响应式对象

解决方案

方案一 判断值是否完全相等

<template>
  <Child v-bind="attrsCopy" />
</template>

<script>
import _ from 'lodash';
import Child from './Child';

export default {
  name: 'Child',
  components: {
    Child,
  },
  data() {
    return {
      attrsCopy: {},
    };
  },
  watch: {
    $attrs: {
      handler(newVal, value) {
        if (!_.isEqual(newVal, value)) {
          this.attrsCopy = _.cloneDeep(newVal);
        }
      },
      immediate: true,
    },
  },
};
</script>

2、通过props 接收info,并对info进行监听,就不存在这个问题

info:{
   handler(newV,old) {
    console.log('info改变了,触发')
  },
  deep: true,
  immediate: true,
}
    
1、 基本数据类型
只有值改变时才触发watch
2、引用数据类型
同一个引用,内容改变,触发;
引用地址变化,触发;
watch内部的判断是:
不同的对象都不相等, 即使内部数据一样,对象(引用数据类型,如数组)只有引用地址一样才是相同的

比如:对象浅拷贝,内部数据是同一个引用
var obj1 = {
    'a':{name: 123},
    'b':{name: 45}
}
var obj2 = {...obj1}
console.log(obj1.a === obj2.a)  // true

总结

在子组件有v-bind="$attrs",就会在 initRender 方法中,将 $attrs 属性绑定到了 this 上,并且设置成响应式对象。Vue 通过 Object.defineProperty 方法进行依赖收集, 我们在访问 $attrs 时,它( dep)会将 $attrs 所在的 Watcher 收集到 dep 的 subs 里面,从而在设置时进行派发更新notify(),通知视图渲染。

 Object.defineProperty(obj, key, {
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    }
  })

$attrs的依赖收集发生在v-bind中, 通过vue-template-compiler 编译源代码即可发现。
所以当 input 中 v-model 的值更新时,触发 set 通知更新,而在更新组件时调用的 updateChildComponent 方法中会对 $attrs 进行赋值。

const compiler = require('vue-template-compiler');

const result = compiler.compile(
  // `
  //   <div :test="test">
  //     <p>测试内容</p>
  //   </div>
  // `
  `
  <div v-bind="$attrs">
    <p>测试内容</p>
  </div>
`
);

console.log(result.render);

// with (this) {
//   return _c(
//     'div',
//      { attrs: { test: test } },
//      [
//        _c('p', [_v('测试内容')])
//       ]
//   );
// }

// with (this) {
//   return _c(
//     'div',
//      _b({}, 'div', $attrs, false),
//      [
//        _c('p', [_v('测试内容')])
//       ]
//   );
// }

所以会触发 $attrs 的 set ,导致它所在的 Watcher 进行更新,也就会导致子组件更新了。而如果没有绑定 v-bind="$attrs" ,则虽然也会到这一步,但是没有依赖收集的过程,就无法去更新子组件了。

相关推荐

  1. vue watch监听多种使用

    2024-05-10 22:12:08       39 阅读
  2. vue 事件$on,$off注意事项

    2024-05-10 22:12:08       40 阅读
  3. VUEwatch 监听失效

    2024-05-10 22:12:08       58 阅读
  4. vue watch监听

    2024-05-10 22:12:08       59 阅读
  5. vue-pdf注意事项

    2024-05-10 22:12:08       45 阅读
  6. Vue中使用watch监听Vuex数据变化

    2024-05-10 22:12:08       35 阅读
  7. watch监听vue2与vue3写法

    2024-05-10 22:12:08       25 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-10 22:12:08       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-10 22:12:08       100 阅读
  3. 在Django里面运行非项目文件

    2024-05-10 22:12:08       82 阅读
  4. Python语言-面向对象

    2024-05-10 22:12:08       91 阅读

热门阅读

  1. 行列视能解决哪些问题

    2024-05-10 22:12:08       33 阅读
  2. 乐观锁、悲观锁、互斥锁、读写锁

    2024-05-10 22:12:08       30 阅读
  3. 括号序列(栈)

    2024-05-10 22:12:08       33 阅读
  4. 20大新质生产力产业链龙头企业名单!

    2024-05-10 22:12:08       27 阅读
  5. 面向对象基础概念

    2024-05-10 22:12:08       33 阅读
  6. 【力扣】143. 重排链表

    2024-05-10 22:12:08       31 阅读
  7. c#笔记(调用exe,xmal,gif)

    2024-05-10 22:12:08       35 阅读