Vue3笔记(一)data,v-bind,v-on,methods,computed,watch

574 阅读15分钟
一.data属性

data属性是传入一个函数,并且该函数需要返回一个对象:

在Vue2.x的时候,也可以传入一个对象 (虽然官方推荐是一个函数)。

在Vue3.x的时候,必须传入一个函数,否则就会直接在浏览器中报错。

data中返回的对象会被Vue的响应式系统劫持,之后对该对象的修改或者访问都会在劫持中被处理。

二.methods属性

是一个对象,通常我们会在这个对象中定义很多的方法:

这些方法可以被绑定到模板中;

在该方法中,我们可以使用this关键字来直接访问到data中返回的对象的属性;

image-20221229203425894.png

事实上Vue的源码当中就是对methods中的所有函数进行了遍历,并且通过bind绑定了this:

1、将传入的methods进行了遍历,通过种种方式,会拿到key,再通过key从methods里面拿到methodHandler这个真实的函数。

2、再通过menthodsHandler,用bind函数绑定publicThis(当前实例的代理对象)

image-20221229204058713.png

三.v--bind

1、绑定属性用法:动态绑定一个或多个attribute,或者向另一个组件传递props值。


    <div id="app">
     <!-- v-bind绑定属性 -->
        <!-- 1.绑定img的src属性 -->
        <img v-bind:src="imgUrl" alt="">
        <!-- 2.绑定a的超连接 href属性-->
        
        <a :href="href">百度一下</a>
        <h2>{{message}}</h2>
    </div>
​
    <script src="./lib/vue.js"></script>
    <script>
        // 1.创建app
        const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
            data: function () {
                return {
                    message: "hello vue",
                    imgUrl: "https://gimg3.baidu.com/search/src=http%3A%2F%2Fpics6.baidu.com%2Ffeed%2Ff31fbe096b63f624a0b8d792a22897f31b4ca336.jpeg%40f_auto%3Ftoken%3D340c4cf24c2b0013b1753942bde64287&refer=http%3A%2F%2Fwww.baidu.com&app=2021&size=f360,240&n=0&g=0n&q=75&fmt=auto?sec=1672506000&t=fbc712de714ce3673a2226e733a3fbb1",
                    href: "http://baidu.com"
                }
            },
        })
        // 2.挂载app
        app.mount("#app");//app调用mount函数,挂载到#app里面。底层通过querySelector找到div元素

2、v-bind绑定class属性

对象语法:

 <div id="app">
            <!-- 1.基本绑定clalss -->
            <h2 :class="classes">{{message}}</h2>
            <!-- 2. 动态绑定class可以写对象语法-->
            <button :class=" isActive ? 'active':'' " @click="btnClick">我是按钮</button>
            <!-- 2.1对象语法的基本使用 -->
            <button :class=" {active:isActive} " @click="btnClick">我是按钮二</button>
            <!-- 2.2对象语法的多个键值对 -->
            <button :class="{active:isActive,why:true,kobe:false}" @click="btnClick">按钮</button>
            <!-- 2.3动态绑定的class的可以和普通的class的并列使用 -->
            <button class="abc cba" :class="{active:isActive,why:true,kobe:false}" @click="btnClick">按钮</button>
            <!-- 2.4直接调用getDynaicClass函数,拿到对应对象的结果 -->
            <button class="abc cba" :class="getDynaicClass()" @click="btnClick">按钮</button>
        </div>
    ​
        <script src="./lib/vue.js"></script>
        <script>
            // 1.创建app
            const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
                data: function () {
                    return {
                        message: "hello vue",
                        classes: "abc cba nba",
                        isActive: false,
                    }
                },
                methods: {
                    btnClick() {
                        this.isActive = !this.isActive;
                        console.log(this.isActive);
                    },
                    getDynaicClass() {
                        // 根据boolean类型为true还是false决定class是否要绑定过来。
                        return { active: this.isActive, why: true, kobe: false }
                    }
                },
            })
            // 2.挂载app
            app.mount("#app");//app调用mount函数,挂载到#app里面。底层通过querySelector找到div元素

数组语法:


   <!-- 3.动态绑定的classs是可以写数组语法 -->
 <h2 :class="['abc','sbc']">Hello Array</h2>
 <h2 :class="['abc',classNmae]">Hello Array</h2>
 <h2 :class="['abc',classNmae,isActive?'active':'']">Hello Array</h2>
            <!--优化 -->
<h2 :class="['abc',classNmae,{active:isActive}]">Hello Array</h2>

3、v-bind绑定style属性:CSS property名可以用驼峰式 (camelCase)或短横线分隔(kebab-case,记得用引号括起来来命名)。

 <div id="app">
            <!-- 1.普通html写法 -->
            <h2 style="color: red;font-size: 30px;">哈哈哈1</h2>
            <!-- 2.style中的某些值,来自data中 -->
            <!-- 2.1动态绑定style,在后面更上对象类型,单引号的使用 -->
            <h2 :style="{color:fontcolor,'font-size':'30px'}">哈哈哈1</h2>
            <!-- 2.2动态的绑定属性,这个属性是一个对象 -->
            <h2 :style="objStyle">呵呵哈哈哈</h2>
            <!-- 3.style的数组语法 -->
            <h2 :style="[objStyle,{backgroudColor:'purple'}]">kkkk</h2>
        </div>

静态绑定内联样式

v-bind:style 的对象语法十分直观,CSS属性名可以用 驼峰式 (camelCase)  或 短横线分隔 (kebab-case,记得用引号括起来) 来命名:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
  activeColor: 'red',
  fontSize: 30
}

三元判断的对象

<div :style="{color:(index == 0 ? conFontColor : '#000')}"></div>

  • 数组

三元多属性

2025年2月19日更新

:style="[
{height:(warnData.length > 0?'calc(100% - 54px - 42px)':'calc(100% - 54px)')},
{color:'#fff'}
]"

写成计算属性 :style="boxStyle"

<template>
  <div class="PlatformDisplay">
    <dv-border-box-11 title="平台展示" :style="boxStyle">
      <div class="icon" @click="$router.push('/')" title="返回首页">
        <i class="el-icon-s-home" style="color: white"></i>
      </div>
      <div class="main">
        <div class="header">
          <div class="left">
            <div v-for="(item, index) in displayOptions1" :key="index">
              <el-button type="primary" size="small" @click="changeIndex(item.index)" :plain="showIndex !== item.index">
                {{ item.label }}
              </el-button>
            </div>
          </div>
        </div>
      </div>
    </dv-border-box-11>
  </div>
</template>

<script>
export default {
  data() {
    return {
      showIndex: 1,
      displayOptions1: [
        // 假设这里是你的按钮选项数组
        { index: 1, label: '选项1' },
        { index: 2, label: '选项2' },
        // 更多选项...
      ],
    };
  },
  computed: {
  //在computed中写计算属性
    boxStyle() {
      return {
        height: this.showIndex === 1 ? 'calc(100vh + 190px)' : 'calc(100vh + 90px)',
        position: 'relative',
      };
    },
  },
  methods: {
    changeIndex(index) {
      this.showIndex = index;
    },
  },
};
</script>

其他使用案例

  • 将样式绑定到元素上

在模板中,使用 :style 绑定 dynamicHeightStyle 到 div 元素上,这样 div 的高度就会根据 headerHeight 和 footerHeight 的值动态计算。

  • 封装成独立的函数

如果你希望将计算高度的逻辑封装成一个独立的函数,可以在 methods 中定义一个函数,然后在 computed 或模板中调用它。

<template>
  <div :style="getDynamicHeightStyle(headerHeight, footerHeight)">
    <!-- 你的内容 -->
  </div>
</template>

<script>
export default {
  data() {
    return {
      headerHeight: 100,
      footerHeight: 50,
    };
  },
  methods: {
    getDynamicHeightStyle(headerHeight, footerHeight) {
      return {
        height: `calc(100vh - ${headerHeight + footerHeight}px)`,
      };
    },
  },
};
</script>

<style scoped>
/* 你的样式 */
</style>

4、v-bind绑定对象

 <div id="app">
            <h2 :name="name"  :age="18" :height="1.88">Hello World</h2>
            <!-- v-bind对象 ,会自动的遍历对象的所有属性,把他们作为属性,添加到当前元素上面-->
            <h2 v-bind="props">hello 222</h2>
        </div>

5、v-memo的使用

   <div id="app">
            <!-- v-memo,当name发生改变时,数据才发生更新。用于性能优化 -->
            <div v-memo="[name]">
                <h2>姓名:{{name}}</h2>
                <h2>年龄:{{age}}</h2>
                <h2>身高:{{height}}</h2>
            </div>
            <button @click="updatechange">改变</button>
        </div>
四.声明式编程和命令式编程的区别

1、命令式编程关注的是 “how to do”自己完成整个how的过程。我们每完成一个操作,都需要通过JavaScript编写一条代码,来给浏览器一个指令,这样的编写代码的过程,我们称之为命令式编程。在早期的原生JavaScript和jQuery开发的过程中,我们都是通过这种命令式的方式在编写代码的。

2、声明式编程关注的是 “what to do”,由框架(机器)完成 “how”的过程。们会在createApp传入的对象中声明需要的内容,模板template、数据data、方法methods,这样的编写代码的过程,我们称之为是声明式编程。目前Vue、React、Angular、小程序的编程模式,我们称之为声明式编程。

五.v-on绑定事件

1、v-on的使用:

缩写:@

预期:Function | Inline Statement | Object

参数:event

修饰符v-on支持修饰符,修饰符相当于对事件进行了一些特殊的处理:

   <div class="box" @click="divClick">
                <!-- .stop添加修饰符,阻止事件冒泡 内部调用 event.stopPropagation(),-->
                <button @click.stop="btn1Click">按钮</button>
            </div>

.stop - 调用 event.stopPropagation(),阻止事件冒泡。

.prevent - 调用 event.preventDefault(),阻止默认行为。

.capture - 添加事件侦听器时使用 capture 模式。

.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。

.{keyAlias} - 仅当事件是从特定键触发时才触发回调。

.once - 只触发一次回调。

.left - 只当点击鼠标左键时触发。

.right - 只当点击鼠标右键时触发。

.middle - 只当点击鼠标中键时触发。

.passive - { passive: true } 模式添加侦听器

用法:绑定事件监听

     <div id="app">
            <!-- 1.基本的写法 -->
            <div class="box" v-on:click="divClick"></div>
            <!-- 2.语法糖写法 -->
            <div class="box" @click="divClick"></div>
            <!-- 3.绑定的方法的位置,也可以写成一个表达式 -->
            <h2>{{counter}}</h2>
            <button @click="increment">+1</button>
            <button @click="counter++">+1</button>
            <!-- 4.绑定其他方法 -->
            <div class="box" @click="divClick" @mousemove="divmouseMove"></div>
            <!-- 5.元素绑定多个事件(绑定的事件名称和方法) -->
            <div class="box" v-on="{click:divClick,mousemove:divmouseMove}"></div>
        </div>

2、默认传递event对象

    <div id="app">
            <!-- 1.默认传递event事件 -->
            <button @click="btn1Click">按钮</button>
            <!-- 2.明确的参数 -->
            <!-- 'why',18作为参数传递到methods中的btn2Click() -->
            <button @click="btn2Click('why',18)">按钮</button>
            <!-- 3.自己的参数和event对象 -->
            <!-- 在模板中想要明确的获取event对象:$event -->
            <button @click="btn3Click('why',age,$event)">按钮</button>
        </div>
    ​
        <script src="../lib/vue.js"></script>
        <script>
            // 1.创建app
            const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
                data: function () {
                    return {
                        message: "hello vue",
                        age: 19,
                    }
                },
                methods: {
                    // 1.默认参数event对象
                    // 总结:如果在绑定事件的时候,没有传递任何参数,那么event对象会默认传递进来
                    btn1Click(event) {
                        console.log("btn1Click", event);
                    },
                    btn2Click(name, age) {
                        console.log("btn2Click", name, age);
                    },
                    btn3Click(name, age,event) {
                        console.log("btn3Click", name, age,event);
                    }
                },
            })
六.条件渲染

1、v-if的基本使用

        <!-- v-if="条件"-->
            <!-- 判断info有没有key,返回一个数组 -->
            <div class="info" v-if="Object.keys(info).length > 0">
                <h2>个人信息</h2>
                <ul>
                    <li>姓名:{{info.name}}</li>
                    <li>年龄:{{info.age}}</li>
                </ul>
            </div>

v-if的渲染原理:

v-if是惰性的;

当条件为false时,其判断的内容完全不会被渲染或者会被销毁掉;

当条件为true时,才会真正渲染条件块中的内容;

2、v-show和v-if的用法看起来是一致的,也是根据一个条件决定是否显示元素或者组件:

首先,在用法上的区别:

v-show是不支持template;

v-show不可以和v-else一起使用;

其次,本质的区别:

v-show元素无论是否需要显示到浏览器上,它的DOM实际都是有存在的,只是通过CSS的display属性来进行切换;

v-if当条件为false时,其对应的原生压根不会被渲染到DOM中,根本不存在元素;

开发中如何进行选择呢?

如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用v-show;

如果不会频繁的发生切换,那么使用v-if;

3、template元素可以当做不可见的包裹元素,并且在v-if上使用,但是最终template不会被渲染出来:

有点类似于小程序中的block

七.列表渲染

1、v-for的基本格式是"item in 数组":

数组通常是来自data或者prop,也可以是其他方式;

item是我们给每项元素起的一个别名,这个别名可以自定来定义;

我们知道,在遍历一个数组的时候会经常需要拿到数组的索引

如果我们需要索引,可以使用格式: "(item, index) in 数组";

注意上面的顺序:数组元素项item是在前面的,索引项index是在后面的;

2、v-for也支持遍历对象,并且支持有一二三个参数:

一个参数: "value in object",为value;

二个参数: "(value, key) in object",value和key;

三个参数: "(value, key, index) in object",value、key和索引index;

     <div id="app">
            <!-- 遍历对象 -->
            <ul>
                <li v-for="(value,key ,index) in info">{{value}}-{{key}}-{{index}}</li>
            </ul>
        </div>
        data() {
                    return {
                        message: "hello vue",
                        info: { name: "why", age: 19, height: 1.99 }
                    }
                },

image-20221231154003528.png

v-for同时也支持数字的遍历:

每一个item都是一个数字;

v-for也可以遍历其他可迭代对象(Iterable)

八.数组更新检测

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。

这些被包裹过的方法包括:

push()

pop()

shift()

unshift()

splice()

sort()

reverse()

替换数组的方法

上面的方法会直接修改原来的数组;

但是某些方法不会替换原来的数组,而是会生成新的数组,比如 filter()、concat() 和 slice();

九.v-for中的key是什么作用?

这个key属性有什么作用呢?我们先来看一下官方的解释:

key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes;

如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法;

而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;

1、认识VNode

我们先来解释一下VNode的概念:

因为目前我们还没有比较完整的学习组件的概念,所以目前我们先理解HTML元素创建出来的VNode;

VNode的全称是Virtual Node,也就是虚拟节点;

事实上,无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode;

VNode的本质是一个JavaScript的对象;

image-20221231161521983.png (1).将div元素解析成VNode,如下图所示:(本质上还是一个javascript对象)

image-20221231161533949.png

(2).将模板里的每一个元素,转化成VNode,再将VNode,进行解析,创建对应元素,并添加属性。最终将VNode转化成真实的DOM元素。

image-20221231161804878.png

2、虚拟DOM

如果我们不只是一个简单的div ,而是有一大堆的元素,那么它们应该会形成一个VNode Tree:

image-20221231162345733.png

里面的每一个元素,都会创建对应的vnode,再让children属性形成嵌套关系,最终vnode对vnode进行解析,会形成一棵树结构。

vnode-->树结构-->虚拟(virtual)DOM-->真实DOM树

image-20221231162610820.png

添加虚拟DOM的主要原因:

(1)、方便进行diff算法。

(2)、有虚拟DOM,方便对我们的当前所写的代码进行跨平台。

虚拟DOM的作用之一-跨平台1.png

3、key的作用

(1)、第一次形成的虚拟DOM,一旦数据发送改变,就会根据最新的数据创建第二个虚拟DOM,创建完第二个虚拟DOM之后,新旧虚拟DOM对比,有没有发送变化,如果没有发生变化,等到准备操作真实DOM的时候,没有必要去改变原来的数据。

如果没有设置key的话,(不知道可以重复利用元素)会重复利用元素,改变元素(比如li标签中的换成F),接下来的所有元素也需要改变,最后少一个元素,需要新建一个元素,算法效率低。

绑定key的目的,让vue可以更加快速的识别新旧节点是否是同一个东西。

key的作用在虚拟DOM中diff算法.png

(2)、如下图所示我们可以确定的是,这次更新对于ulbutton是不需要进行更新,需要更新的是我们li的列表:

    <div id="app">
            <button @click="insert">插入F</button>
            <ul>
                <!-- 动态绑定key,key要求唯一:id -->
                <li v-for="item in letters" :key="item">{{item}}</li>
            </ul>
        </div>
    ​
        <script src="../lib/vue.js"></script>
        <script>
            // 1.创建app
            const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
                data() {
                    return {
                        letters: ["a", "b", "c", "d", "e"]
    ​
                    }
                },
                methods: {
                    insert() {
                        this.letters.splice(2, 0, "f");//在2(b)的位置后面插入
                    }
                },
            })
            // 2.挂载app
            app.mount("#app");//app调用mount函数,挂载到#app里面。底层通过querySelector找到div元素
        </script>

在Vue中,对于相同父元素的子元素节点并不会重新渲染整个列表;

因为对于列表中 a、b、c、d它们都是没有变化的;

在操作真实DOM的时候,我们只需要在中间插入一个f的li即可;

(4)、那么Vue中对于列表的更新究竟是如何操作的呢?

Vue事实上会对于有key和没有key会调用两个不同的方法;

(1)、有key,那么就使用 patchKeyedChildren方法,会尽可能的复用所有的节点,性能更高;

image-20221231211717009.png

image-20221231212140190.png

第一步的操作是从头开始进行遍历、比较:

a和b是一致的会继续进行比较;

c和f因为key不一致,所以就会break跳出循环;

image-20221231212330550.png

第二步的操作是从尾部开始进行遍历、比较

image-20221231212400662.png

第三步是如果旧节点遍历完毕,但是依然有新的节点,那么就新增节点:

a节点,b节点进行复用,c,d节点也进行复用,直接在相应的地方新增节点,并且在真实DOM里面新增节点。

第四步是如果新的节点遍历完毕,但是依然有旧的节点,那么就移除旧节点:

image-20221231212709005.png

第五步是最特色的情况,中间还有很多未知的或者乱序的节点:

如果是乱序的话,也会就可能的复用节点。

image-20221231213050811.png

(2)、没有key,那么久使用 patchUnkeyedChildren方法;

image-20221231211741898.png

我们会发现上面的diff算法效率并不高:

c和d来说它们事实上并不需要有任何的改动;

但是因为我们的c被f所使用了,所有后续所有的内容都要一次进行改动,并且最后进行新增;

a复用,b复用,c改成新的值,d也改成新的值,最后新增一个节点。

image-20221231211853890.png

十.computed计算属性

(1)、什么是计算属性呢?

官方并没有给出直接的概念解释;

而是说:对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性

计算属性将被混入到组件实例中

所有 getter 和 setter 的 this 上下文自动地绑定为组件实例;

(2)、计算属性的用法:

选项: computed

类型: { [key: string]: Function | { get: Function, set: Function } }

    <div id="app">
            <!-- 通过插值语法进行拼接 -->
            <!-- 1.拼接名字 ,computed计算属性fullName不用加小括号-->
            <h2>{{fullNmae}}</h2>
            <h2>{{fullNmae}}</h2>
            <!-- 2.显示分数等级 -->
            <h2>{{scoreLevel}}</h2>
            <!-- 3.反转单词显示文本 -->
            <h2>{{reverseMessage}}</h2>
        </div>
    ​
        <script src="../lib/vue.js"></script>
        <script>
            // 1.创建app
            const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
                data() {
                    return {
                        firstName: "kobe",
                        listName: "brant",
                        score: 80,
                        message: "my name is www",
                    }
                },
                methods: {
                },
                computed: {
                    fullNmae: function () {
                        return this.firstName + " " + this.listName;
                    },
                    scoreLevel() {
                        return this.score >= 60 ? "及格" : "不及格"
                    },
                    reverseMessage(){
                        return this.message.split(" ").reverse().join(" ");
                    }
                }
            })

(3)、computed计算属性VS methods的。

官方说明:涉及到响应式复杂逻辑统一使用计算属性computed。 计算属性有缓存的,这是因为计算属性会基于它们的依赖关系进行缓存;

在数据不发生变化时,计算属性是不需要重新计算的;

但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算;

 <div id="app">
            <!-- 1.methods -->
            <h2>{{getFullName()}}</h2>
            <h2>{{getFullName()}}</h2>
            <h2>{{getFullName()}}</h2>
            <!-- 2.computed -->
            <h2>{{fullName}}</h2>
            <h2>{{fullName}}</h2>
            <h2>{{fullName}}</h2>
    ​
        </div>
    ​
        <script src="../lib/vue.js"></script>
        <script>
            // 1.创建app
            const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
                data() {
                    return {
                        message: "hello vue",
                        firstName: "kobe",
                        listName: "brant",
                        score: 80,
                        message: "my name is www",
                    }
                },
                methods: {
                    getFullName() {
                        console.log(" getFullName()-----");
                        return this.firstName + " " + this.listName
                    }
                },
                computed: {
                    fullName() {
                        console.log("computed-----");
                        return this.firstName + " " + this.listName
                    }
                }
            })

d9f36fba1f05230fbc0257187bd2137.png (4)、计算属性的get和set

  <div id="app">
            <h2>{{fullName}}</h2>
            <button @click="btnClick">设置fullName</button>
        </div>

        <script src="../lib/vue.js"></script>
        <script>
            // 1.创建app
            const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
                data() {
                    return {
                        message: "hello vue",
                        firstName: "kobe",
                        listName: "brant",
                        score: 80,
                        message: "my name is www",
                    }
                },
                computed: {
                    // 语法糖
                    fullname() {
                        return this.firstName + " " + this.listName
                    },
                    // 完整写法
                    fullName: {
                        get: function () {
                            return this.firstName + " " + this.listName
                        },
                        set: function (value) {
                            const names = value.split(" ");
                            this.firstName = names[0];
                            this.listName = names[1];
                        }
                    },
                },
                methods: {
                    btnClick() {
                        // 可以打印,更改fullname的值
                        console.log(this.fullName);
                        this.fullName = "www bbb"

                    }
                },
            })
十一.watch侦听器

watch在javascript中逻辑中监听到当前的数据发生了变化

1、侦听器的用法如下:

选项: watch

类型: { [key: string]: string | Function | Object | Array}

      <script>
            // 1.创建app
            const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
                data() {
                    return {
                        message: "hello vue",
                        info: { name: "why", age: 19 },
                    }
                },
                methods: {
                    changeMessage() {
                        this.message = "你好啊!"
                        this.info = { name: "kobe" }
                    }
                },
                watch: {
                    // 1.默认有两个参数newVlaue,oldVlaue
                    message(newValaue, oldVlaue) {
                        console.log(newValaue, oldVlaue);
                        console.log("message数据发生了变化~", this.message);//通过this.message获取当前的最新的message
    ​
                    },
                    info(newValaue, oldVlaue) {//newValaue本质上是一个proxy对象
                        // 2.如果是对象类型,那么拿到的是代理对象
                        console.log("info发生变化~", newValaue, oldVlaue);
                        console.log(newValaue.name, oldVlaue.name);//获取代理对象的值
                        // 3.获取原始对象
                        console.log({ ...newValaue });//方法一:对原来的对象做一个展开,将所有属性展开,将所有属性取出来,放到一个新的对象里面。
                        console.log(Vue.toRaw(newValaue));//方法二:通过toRaw拿到最原始的数据
                    }
                }
            })
            // 2.挂载app
            app.mount("#app");

image-20230111223510457.png

2、侦听器的配置选项

1、默认情况下,watch只是在侦听info的引用变化 ,对于内部属性的变化是不会做出响应的:

将deep设置为true进行深度监听。

     <div id="app">
            <h2>{{info.name}}</h2>
            <button @click="changeInfo">按钮</button>
        </div>
    ​
        <script src="../lib/vue.js"></script>
        <script>
            // 1.创建app
            const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
                data() {
                    return {
                        message: "hello vue",
                        info: { name: "why", age: 19 },
                    }
                },
                methods: {
                    changeInfo() {
                        // 1.创建新对象复制给info
                        // this.info = { name: "kobe" }
                        // 2.直接修改原对象某一个属性
                        this.info.name = "kobe";
                    }
                },
                watch: {
                    // 1、默认watch不会进行深度监听
                    // info(newValaue, oldVlaue) {
                    //     console.log(newValaue, oldVlaue);
                    //     console.log("info数据发生了变化~");//通过this.message获取当前的最新的message
                    // },
                    //2、 进行深度监听
                    info: {//只是在改变info的属性
                        handler(newVlaue, oldVlaue) {
                            console.log("info改变", newVlaue, oldVlaue);
    ​
                        },
                        // 监听器选项
                        deep: true,//info进行深度监听
                        // 第一次渲染直接执行一次监听器
                        immediate: true,
                    }
                }
            })
            // 2.挂载app
            app.mount("#app");

2、Vue的$watch监听

     // 1.创建app
            const app = Vue.createApp({//vue全局对象。调用Vue.createApp(),返回一个函数。
                data() {
                    return {
                        message: "hello vue",
                    }
                },
                methods: {
                    changeMessage() {
                        this.message = "你好啊~"
                    }
                },
                // 生命周期的回调函数:当前的组件被创建时自动执行
                // 一般在该函数中,会进行网络请求
                created() {
                    console.log("created");
                    this.$watch("message", (newValue, oldVlaue) => {
                        console.log("message变化~",newValue, oldVlaue);
                    }, { deeep: true })
                }
            })