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

初学Vue(全家桶)-第七天:深度理解Vue监视数据的原理,第1张

初学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>Documenttitle>
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>Documenttitle>
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的根数据对象添加属性

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1322802.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存