初学Vue(全家桶)-第7天(vue2):深度理解Vue监视数据的原理

初学Vue

1、错误更新数据的一种方式

看如下示例:

<!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">
    <script src="../../JS/vue.js"></script>
    <title>Document</title>
</head>
<body>
    <div id="root">
        <h2>人员列表</h2>
        <button @click="changeZhao">将赵金麦改为宋祖儿</button>
        <ul>
            <li v-for="(p, index) in persons" :key="p.id">
                {
   
   {p.name}}-{
   
   {p.age}}-{
   
   {p.sex}}
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el:"#root",
            data:{
      
      
                persons:[
                    {
      
      id:"001",name:"赵金麦",age:20,sex:"女"},
                    {
      
      id:"002",name:"周冬雨",age:27,sex:"女"},
                    {
      
      id:"003",name:"周杰伦",age:37,sex:"男"},
                    {
      
      id:"004",name:"张译",age:37,sex:"男"},
                ],
            },
            methods:{
      
      
                changeZhao(){
      
      
                    // 能奏效的方式:将数组中某个对象中的属性分别修改
                    // this.persons[0].name = "宋祖儿",
                    // this.persons[0].age = 21;
                    // this.persons[0].sex = "女",

                    // 不生效的方式:将数组某个对象整体修改
                    this.persons[0] = {
      
      id:"001",name:"宋祖儿",age:21,sex:"女"}
                }
            }
        });
    </script>
</body>
</html>

在这里插入图片描述

this.persons[0] = {id:"001",name:"宋祖儿",age:21,sex:"女"}使用这种方式更新数据浏览器页面不会显示修改后的数据,但内存中的数据确实被修改了,查看vm示例可知,如下:在这里插入图片描述
为什么浏览器上的数据没有改变呢,因为使用这种方式时Vue没有监视到修改。

2、Vue监视对象的原理

2.1 Vue监视对象-模拟底层代码实现-简单分析

<body>
    <script>
        Vue.config.productionTip = false;

        let data = {
      
      
            name:"小明",
            address:"北京"
        }

        // 创建一个监视的实例对象,用于监视data中属性的变化
        const obs = new Observer(data);
        console.log(obs);

        // 准备一个vm实例对象
        let vm = {
      
      };

        // 让下面三值相等
        vm._data = data = obs;

        function Observer(obj){
      
      
            // 汇总对象中所有的属性形成一个数组
            const keys = Object.keys(obj);
            // 遍历
            keys.forEach((k)=>{
      
      
                Object.defineProperty(this,k,{
      
       // 这里this指的是Observe的实例对象obs
                    // 给属性添加了get函数和set函数,使其属性值能够被获取和修改
                    get(){
      
      
                        return obj[k];
                    },
                    set(val){
      
      
                        console.log(`${ 
        k}被改了,重新解析模板,生成虚拟DOM,新旧DOM进行比较`)
                        obj[k] = val;
                    }
                })
            })
        }
    </script>
</body>

分析:监测的最重要部分就是每次调用set函数进行修改时,都会重新进行模板解析,生成新DOM和旧DOM进行比较,最后刷新页面,展示更新后的内容。


补充:
这只是Vue底层代码的部分实现,这里只能通过vm._data.属性="新属性值"的方式对属性值进行修改,而完整代码因实现了数据代理,而可以使用vm.属性名的方式修改属性值;此外,这里data中的数据是只有一层的,而Vue底层代码中通过实现深拷贝,可以将数据中嵌套的内容也找出来,并且添加get和set函数。

2.2 API --Vue.set()的使用

看如下代码:

<body>
    <div id="root">
        <h3>学校名称:{
   
   {schoolName}}</h3>
        <h3>学校地址:{
   
   {address}}</h3>
        <hr>
        <h3>姓名:{
   
   {student.name}}</h3>
        <h3>性别:{
   
   {student.sex}}</h3>
        <h3>年龄:真实{
   
   {student.age.rAge}},对外{
   
   {student.age.sAge}}</h3>

        <h3>朋友们:</h3>
        <ul>
            <li v-for="(f,index) in student.friends" :key="index">
                {
   
   {f.name}}--{
   
   {f.age}}
            </li>
        </ul>
    </div>

    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el:"#root",
            data:{
      
      
                schoolName:"清华",
                address:"北京",
                student:{
      
      
                    name:"tom",
                    age:{
      
      
                        rAge:40,
                        sAge:29
                    },
                    // 准备一个数组
                    friends:[
                        {
      
      name:"jerry",age:35},
                        {
      
      name:"jack",age:33}
                    ]
                },
            }
        });
    </script>
</body>
</html>

在模板中出现了“性别”,但在data中却并没有性别这个属性和属性值,该如何添加这个属性并且设置值呢?
可不可以这么想,直接操作vm实例中的_data,创建一个属性sex,并且给它设置值呢?答案是否定的,结果和原因如下:
在这里插入图片描述
在这里插入图片描述
最影响这个结果的就是添加的属性sex中没有set函数,这个set方式是刷新页面最主要的方法,也是页面不显示结果的原因。

  • Vue.set()方法
    Vue的API中提供了这么一种方法,Vue.set()方法可以给data中添加属性和属性值。
    语法如下:Vue.set(target,propertyName/index,value)
    参数:target是目标对下个你,propertyName/index是属性名,value是属性值
    例如:
    在这里插入图片描述
    因为Vue实现了数据代理,所以上面的代码还可以简写
    在这里插入图片描述
    下面再代码中实现添加性别属性的值的操作,给一个按钮绑定点击事件,点击后给性别属性值添加属性值。
<body>
    <div id="root">
        <h3>学校名称:{
   
   {schoolName}}</h3>
        <h3>学校地址:{
   
   {address}}</h3>
        <hr>
        <button @click="addSexValue">给性别添加一个值</button>
        <h3>姓名:{
   
   {student.name}}</h3>
        <h3 v-if="student.sex">性别:{
   
   {student.sex}}</h3>
        <h3>年龄:真实{
   
   {student.age.rAge}},对外{
   
   {student.age.sAge}}</h3>

        <h3>朋友们:</h3>
        <ul>
            <li v-for="(f,index) in student.friends" :key="index">
                {
   
   {f.name}}--{
   
   {f.age}}
            </li>
        </ul>
    </div>

    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el:"#root",
            data:{
      
      
                schoolName:"清华",
                address:"北京",
                student:{
      
      
                    name:"tom",
                    age:{
      
      
                        rAge:40,
                        sAge:29
                    },
                    // 准备一个数组
                    friends:[
                        {
      
      name:"jerry",age:35},
                        {
      
      name:"jack",age:33}
                    ]
                },
            },
            methods:{
      
      
                addSexValue(){
      
      
                   	// 使用Vue对象的set方法添加
                    Vue.set(this.student,"sex","男");
                    // 还有第二种方式:使用vm实例添加
                    this.$set(this.student,"sex","男");
                    
                }
            }
        });
    </script>
</body>

在这里插入图片描述

需要注意的是,Vue.set()方法有局限,(1)不能给data中添加属性(2)不能给vm实例上添加属性
例如:Vue.set(vm.data,"leader","帅男孩")肯定会报错的,再例如:vue.set(vm,"leader","老男孩")同样会报错

3、Vue检测数组的原理

简单看下如下的案例:

<!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">
    <script src="../../JS/vue.js"></script>
    <title>Document</title>
</head>
<body>
    <div id="root">
        <h3>学生信息</h3>
        <h4>姓名:{
   
   {name}}</h4>
        <h4>性别:{
   
   {sex}}</h4>
        <h4>爱好:</h4>
        <ul>
            <li v-for="(h,index) in hobbies" :key="index">
                {
   
   {h}}
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
      
      
            el:"#root",
            data:{
      
      
                name:"tom",
                sex:"男",
                hobbies:["游戏","电影","音乐"],
            }
        });
    </script>
</body>
</html>

查看浏览器控制台可以发现,并没有为数组中的元素单独提供get/set方法,也就意味着不能通过数组索引来赋值从而对元素数据进行修改
在这里插入图片描述
在这里插入图片描述
不能通过数组索引来改变数组中元素的值,因为vue不承认。

正确操作数组的方式

  • 第一种方式:

利用能改变原数组的API来对数组操作时才可以更改数组的值(例如:数组API中的push,pop,shift,unshifit,splice,sort,reverse方法),因为使用这类API时会让模板重新解析,从而成功修改数组。
在这里插入图片描述
为什么通过数组API的这期中方法可以对数组数组进行操作呢?因为Vue对数组API中的方法进行了包装,也就是说Vue中的数组API方法并不完全等同于普通数组API中的方法,证明如下:
在这里插入图片描述
vue中的push同样能够发挥数组API中push的作用,并且在发挥作用之后,会对模板重新进行解析,这就是与数组API中的push的区别。

  • 第二种方式:
    利用Vue.set()方法来进行数据修改,如下:
    在这里插入图片描述

4、简单总结

简单总结:
1、vue会监视data中所有层次的数据
2、如何检测对象中的数据:
(1)对象中后追加的属性,Vue默认不做响应式处理
(2)如需给后添加的属性做响应式处理,可以使用下面的方式:Vue.set(target,propertyName/index,value)或者vm.$set(target,propertyName/index,value)
3、如何监视数组中的数据:
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新
(2)重新解析模板,进而更新页面
4、在Vue中修改数组中的某个元素时可以使用如下方法:
(1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2)Vue.set()或者vm.$set()

需要注意的是Vue.set()或者vm.$set()不能给vm或者vm的根数据对象添加属性

猜你喜欢

转载自blog.csdn.net/lalala_dxf/article/details/124931972
今日推荐