# 1. 不开启 deep

1. 不开启 deep 情况下 (默认 watch 只会监听数据本身,而内部数据改变不会监听)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>not-deep-watch</title>
</head>
<body>
  <div id="app"></div>
  <template id="my-app">
    <div>
      <h2>{{ info }}</h2>
      <button @click="changeInfo">改变info</button>
      <button @click="changeInfoName">改变info.name</button>
      <button @click="changeInfoFriendName">改变
      info.friend.name</button>
      <button @click="changeInfoArray">改变info.array</button>
    </div>
  </template>
  <!-- 测试记得引用 vue.js -->
  <script src="./vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          info: {
            name: 'aimer',
            age: 16,
            friend: { name: 'Neko', age: 16 },
            array: [1, 2, 3]
          }
        }
      },
      watch: {
        // 默认情况下 watch 只会监听数据本身,而内部数据改变不会监听
        info(newInfo, oldValue) {
          // 模板字符串方式在 control 看不到具体效果
          //console.log (`新值:${newInfo}, 旧值:${oldValue}`);
          console.log('新值:', newInfo, '旧值:', oldValue);
        }
      },
      methods: {
       // 未启用 deep 模式下 点击这几个按钮
        changeInfo() {
          // 直接将 info 重新赋值 原数据改变 watch 会触发
          this.info = 123 // 结果参考 result1 
        },
        changeInfoName() {
          // 给 info 里面的属性 name 重新赋值,不会触发 watch
          this.info.name = 'Neko' // 结果参考 result2
        },
        changeInfoFriend() {
          // 给 info 里面的属性 对象类型 (friend) 重新赋值 不会触发 watch
          this.info.friend.name = 'Aimer' // 结果参考 result3
        },
         changeInfoArray() {
          // 给 info 里面的属性 array 添加元素 不会触发 watch
          this.info.array.push('123') // 结果参考 result4
        }
      }
    }
    Vue.createApp(App).mount('#app');
  </script>
</body>
</html>

结果如下:
数据未发生改变前原图:watch

result1:watch

result2:watch

result3:watch

result4:watch

# 2. 开启 deep

  1. 在开启 deep 情况下 (watch 无论基本或复杂数据类型都会监听)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>deep_watch</title>
</head>
<body>
  <div id="app"></div>
  <template id="my-app">
    <div>
      <h2>{{ info }}</h2>
      <button @click="changeInfo">改变info</button>
      <button @click="changeInfoName">改变info.name</button>
      <button @click="changeInfoFriendName">改变info.friend.name</button>
      <button @click="changeInfoArray">改变info.array</button>
    </div>
  </template>
  <script src="./vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          info: {
            name: 'aimer',
            age: 16,
            friend: { name: 'Neko', age: 16 }
          }
        }
      },
      watch: {
        //deep 为 true 情况下 watch 会监听数据本身,而内部数据改变也会监听
        info: {
          handler: function (newInfo, oldValue) {
            console.log('新值:', newInfo, '旧值:', oldValue);
          },
          deep: true // 深度监听
        },
      },
      methods: {
        // 启用 deep 模式下 点击这几个按钮
        changeInfo() {
          // 直接改变 info watch 会触发
          this.info = 123 // 结果参考 result1
        },
        changeInfoName() {
          this.info.name = 'Neko' // 会触发 watch 结果参考 result2
        },
        changeInfoFriendName() {
          // 给 info 里面的 对象 (friend) 里面的 name 重新赋值会触发 watch
          this.info.friend.name = 'Aimer' // 结果参考 result3
        },
        // 给 info 里面的数组 添加元素时 会触发 watch
        changeInfoArray() {
          this.info.array.push('123') // 结果参考 result4
        }
      }
    }
    Vue.createApp(App).mount('#app');
  </script>
</body>
result1:![watch](deep_result1.png)
</html>

结果如下:
数据未发生改变前原图:watch

result1⇩:注意旧值与新值完全改变,是可以拿到不一样的新值和旧值

watch

result2⇩:注意旧值与新值的 name 属性,却是完全一样的,也就是说拿不到旧值了,拿到的旧值也是和新值一样的数据了,这是因为他们的指向引用地址是一样的!如果想取旧值可能需要其他操作,例如先拷贝一份!请参考原图对比

watch

result3⇩:此时你已经发现了不对劲,和 result2 的结果类似,info.friend 里面的 name 属性新值与旧值也是一样的 请参考原图对比

watch

result4⇩:同理!请参考原图对比

watch

# 3. 结论

  1. 在未开启 deep 时,除非直接修改 info (例:info = ???) ,其余任何内部修改属性或赋值都不会监听

  2. 在开启 deep 时,无论是修改其内部基本数据类型或复杂数据类型,修改属性或赋值都会被监听。而且在修改了内部的属性,新值与旧值是一致的,是因为他是引用类型,他们的引用也都是指向同一个对象的,这就涉及了浅拷贝的相关知识!

# 4.immediate

1.immediate 就是不需要 API 操作,数据是否有变化,侦听的函数都会有限执行一次,也就是希望一开始就会立即执行一次 watch

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>immediate_watch</title>
</head>
<body>
  <div id="app"></div>
  <template id="my-app">
    <div>
      <h2>{{ info }}</h2>
    </div>
  </template>
  <script src="./vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          info: {
            name: 'aimer',
            age: 16,
            friend: { name: 'Neko', age: 16 }
          }
        }
      },
      watch: {
        info: {
          handler: function (newInfo, oldValue) {
            console.log(`新值:${newInfo}, 旧值:${oldValue}`);
          },
          deep: true, // 深度监听
          immediate: true // 立即执行
        },
      },
    }
    Vue.createApp(App).mount('#app');
  </script>
</body>
</html>

结果如下:

result:watch

默认执行一次旧值为 undefined,是因为没有对 info 进行任何操作就执行了~

# 5. 监听对象里的某个属性(deep: true)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app"></div>
  <template id="my-app">
    <div>
      <h2>{{ info }}</h2>
      <button @click="changeInfo">改变info</button>
      <button @click="changeInfoName">改变info.name</button>
      <button @click="changeInfoFriendFriendName">改变info.friend.friend.name</button>
      <button @click="changeInfoFriendFriendArray">改变info.friend.friend.array</button>
    </div>
  </template>
  <script src="./vue.js"></script>
  <script>
    const App = {
      template: '#my-app',
      data() {
        return {
          info: {
            name: 'aimer',
            age: 16,
            friend: { name: 'Neko', age: 16, friend: { name: 'Annie', array: [1, 2, 3] } },
          },
        }
      },
      watch: {
        "info.friend": {
          handler: function (newName, oldName) {
            console.log('新值:', newName, '旧值:', oldName);
          },
          deep: true
        }
      },
      methods: {
        changeInfo() {
          // 此时肯定是可以监听到的 参考 result1
          this.info = 123
        },
        changeInfoName() {
          // 此时是监听不到的 参考 result2
          this.info.name = 'Neko'
        },
        changeInfoFriendFriendName() {
          // 此时也是可以监听到的 参考 result3
          this.info.friend.friend.name = 'Alison'
        },
        changeInfoFriendFriendArray() {
          // 此时也是可以监听到的 参考 result4
          this.info.friend.friend.array.push('123')
        }
      }
    }
    Vue.createApp(App).mount('#app');
  </script>
</body>
</html>

结果如下:
数据未发生改变前原图:watch

result1⇩:此时是监听到变化的,但是拿不到新值。是因为改变的是整个 info,而不是 info.friend 里面的属性,所以有监听到变化,但是 info.friend 里面已经不存在值,故为 undefined

watch

result2⇩:因为改变的不是所监听的属性,故无反应 请参考原图对比

watch

result3⇩:新值与旧值与上述也是一样的 请参考原图对比

watch

result4⇩:同理! 请参考原图对比

watch

至于没有开启deep情况下和上面类似,也可以自行去测!另外补充一下,created里面watch可以有返回值(函数),调用函数可以取消!
vue
created () {                         
        const unwatch = this.$watch ("info",function (newInfo, oldInfo) {
          console.log (newInfo, oldInfo);
        }, {
          deep: true,
          immediate: true
        })
        console.log (unwatch); // 输出是箭头函数
        //unwatch () // 取消
      }
    }
Update on Views times

Give me a cup of [coffee]~( ̄▽ ̄)~*

Nico Niconi WeChat Pay

WeChat Pay

Nico Niconi Alipay

Alipay