第三节: Vue选项: data数据以及数据响应式

Vue data 数据属性

1.数据属性 Data的了解

通过前面的学习,我们知道vue的数据是写在vue选项对象(也可以理解为配置对象)的data属性中的,我们可以通过Mustache(双大括号语法)就数据插入到页面上

<div id="app">
    <p>第二条狗狗的名字是:{{dogs[2]}}</p>
    <p>所有狗狗名字是:{{dogs}}</p>
</div>

<script>   
    const vm = new Vue({
        el:"#app",
        data:{
            dogs: ["哈士奇","中华田园犬","藏獒"]
        }
    })
</script>

示例的感悟:

  1. 会发现,如果将这个数组或对象输出到页面上,并不会像JS一样输出[Object object]
  2. Vue会输出JSON编码后的值,这样在调试的时候超级有用
  3. 调试时将内容渲染到页面比将数据输出到控制台更加有效果,
  4. 在页面上显示会随着值的变化而变化(响应式),,这样更加直观的看到数据的变化


2.数据响应变化

当一个 Vue 实例被创建时,通过插值语法将数据显示在页面上,当我们修改数据时,页面也会发生变化

// 我们的数据对象
var data = { a: 1 }

// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
  el:"#app",
  data: data
})

// 获得这个实例上的属性
// 返回源数据中对应的字段
vm.a == data.a // => true

// 设置属性也会影响到原始数据
vm.a = 2
data.a // => 2

// ……反之亦然
data.a = 3
vm.a // => 3

为什么页面会自动发生变化呢?

  1. 这就是因为在Vue实例化时,就会向Vue的响应式系统中加入data对象中能够找到的所有属性。
  2. 其实是Vue通过监听的方式一直在侦查值是否发生变化,我们可以在控制台上打印实例:

图如下:


监听.png

通过这张图,我们就会发现:

  1. 示例中data数据属性中的数据aVue实例通过get,和set一直在检测着,
  2. 也就是说vue的响应系统一直在观察数据a的变化,一旦发生变化,响应系统做出反应,改变视图.

所以当数据值发生改变,视图也会产生响应。


2.1 认识数据响应式的变化

通过刚才例子了解当数据发生变化时,页面视图也会进行重新渲染,值得注意的时,只有data中存在的属性才是响应的。

也就是说如果你添加一个新的属性,比如:

vm.b = 'hi'

那么对b的改动不会出发任何视图的更改。因为vue并没有将b加入到响应系统中, 说白了就是没有检测b的变化



那如何解决此类问题呢?

如果你知道你会晚些时候需要一个属性,那么你可以在开始时设置一些初始值。如:

data: {
  a: "hello",
  b: ''
}



这里要注意一个特例:

这个例外是使用 Object.freeze(),这个方法是冻结方法,意思是不允许修改对象的属性值,
这就会阻止修改现有的属性, 数据没发改变,也就不会触发Vue的响应系统

var obj = {
  foo: 'bar'
}

Object.freeze(obj)

new Vue({
  el: '#app',
  data: obj
})

这个示例中我们利用了Object.freeze()冻结方法,让Vue实例的所有data属性中的数据都失去了响应式, 这样做没什么意义, 做了时候,Vue也就失去了数据驱动的功能

但是Object.freeze()方法在某些时候还是非常有用的, 当然不是冻结整个data,我们可以冻结data属性中的一些对象数据,
想了解,接着往下看

2.2 对象数据的响应变化
2.2.1 了解对象数据的响应变化

刚看到的是data属性只是是基本类型的问题, 接下来看看数据属性值是对象的状况

vue在初始化的时候会循环data中的数据,(数据劫持),以此增加getter和setter来监听数据

示例:

<div id="app">
    {{student.name}}
</div>
<script src="../vue.js/vue.js"></script>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            student: {
                name:"xiaoming"
            }
        }
    })
</script>

页面显示结果


示例结果.png

此时页面正常显示结果, 如果我们在控制台改变对象数据,会有如下结果:


测试示例结果.png

通过示例了解到,Vue数据如果是对象student,student中的属性name在发生改变的时候,页面也会发生改变,说明对象中的name属性是被vue关注着的

那么问题来了, Vue会检测到student数据的变化, 那么如果student对象中的新镇一些属性,会不会被检测到呢.

我们可以通过如下的示例进行测试:

<div id="app"><!---view--->
    {{student.name}}
    {{student.age}}
</div>
<script src="../vue.js/vue.js"></script>
<script>
    const vm = new Vue({
        el: "#app",
        data: {
            student: {
                name:"xiaoming"
            }
        }
    })

    // 会发现初始化存在的name属性会触发页面的更新
    //setTimeout(() => {
    //    vm.student.name = "小明";
    //}, 2000)
    
    // 对象age的改变不会触发页面的更新
    //setTimeout(() => {
    //    vm.student.age = 18;
    //}, 2000)
    
    // 但是我们会发现,如果页面一旦更新,age的值也会渲染在页面上,
   //原因在于Vue get和set方法监听的是对象,不是对象的每一个属性
    setTimeout(() => {
        vm.student.name = "小明";
        vm.student.age = 18;
    }, 2000)

</script>

通过上面三个定时器的不同测试结果, 我们了解到了:

  1. Vue初始化时定义的数据student的改变,
  2. 以及student数据的name属性发生变化都会触发响应系统, 进而改变视图,
  3. 但是如果student数据新增一个属性, 却没有触发响应式系统, 页面也没有重新渲染,

为什么会这样呢?

我们可以在控制台打印Vue实例对象

示例结果.png

Vue会检测student就不多讲了

在看另外一张图

示例结果2.png

通过这张图我们就会发现,Vue不但会监听student数据,还会监听在Vue实例化时,student就已经存在的属性name,所以name的改变也会触发响应系统,

但是我们后添加的对象的数据不是响应式, Vue不会根据这些数据的变化来改变对应的视图,所以当你单独新增一个属性是不会触发响应系统,

通过示例,我们有发现不会触发响应系统的数据属性跟具有响应系统的属性一起改变,也会到处页面的重新渲染, 因为有人触发了响应系统,如果你稍后还是单独改变age数据值,页面依然不会变化

总结:

  1. 在实例化Vue之前,就已经定义好的对象,已经对象中的数据会被Vue响应式检测
  2. 数据对象新增的属性因为没有被检测,所以单独改变不会触发Vue响应式
  3. 但是只要Vue响应式被触发了,新增的属性值也会触发页面的改变


2.2.2 将不是响应系统检测的数据添加到响应系统

那么如何才能在给对象添加新的属性触发vue的响应系统,让页面发生更新呢?

可以通过 vm.$set()方法可以给对象添加响应式的数据变化

<div id="app"><!---view--->
    {{student.name}}
    {{student.age}}
</div>
<script src="../vue.js/vue.js"></script>
<script>

    const vm = new Vue({
        el: "#app",
        data: {
            student: {
                name:"xiaoming"
            }
        }
    })

    // 通过$set 方法改变对象的属性会触发响应式
    // 让页面视图发生更新
    setTimeout(() => {
        vm.$set(vm.student,"age",18)
    }, 2000)

</script>

为什么$set()会触发页面的 改变呢? 我们可以在控制台上打印Vue实例对象


示例结果.png

我们会发现通过$set方法处理后,Vue会将age属性添加到响应系统上,所以就触发了响应系统, 进而改变视图


2.2.3 修改多个不是响应系统检测的数据

但是$set方法并不好,如果我要设置100个属性那不得累死,怎么办呢?

所以我们可以采用直接替换属性对象内容的方法,因为响应系统会测试student数据整体的变化

<div id="app"><!---view--->
    {{student.name}}
    {{student.age}}
</div>
<script src="../vue.js/vue.js"></script>
<script>

    const vm = new Vue({
        el: "#app",
        data: {
            student: {
                name:"xiaoming"
            }
        }
    })

    // 因为vue实时监听对象student的变化
    // 所以我们可以采用替换对象值的方法来触发响应式
    setTimeout(() => {
        vm.student = {
            name: "xiaoming",
            age: 18
        }
    }, 2000)

</script>

通过上面的例子

我们会发现$set每次只能新增一个属性,如果我要新增多个属性就不是那么友好了,


2.2.4 解决直接替换对象数据的缺点

同样的如果采用替换原对象,通过字面量的方式替换,会发现如果我原对象已有多个属性,在通过替换原对象的方式触发响应式的时候,需要不断重写原对象的属性, 就很繁琐.

所以关于替换原对象,我们可以采用Object.assign 来给原对象扩展属性,然后在赋值给原对象,比如

//不要这样,这样并没有新的对象,还是在原有对象上新增属性
// 这种写法跟vm.student.age = 18 完全等价 不会触发响应式
Object.assign(vm.student,{
    age:18
})

//你应该这样做, 在合并后形成新的对象, 在把新对象赋值给vue的数据对象
// 这样多就会触发响应式
vm.student = Object.assign({},vm.student,{
    age:18
})


2.3 数组数据的响应变化

在操作数组是不能使用下标的方式去改变数据

因为这种方式改变数据的值并不会触发页面的重新渲染

因为通过下标,去改变数组中的某一项是监控不到的

<div id="app">
    {{arr}}
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            arr: [1, 2, 3, 4, 5, 6]
        }
    })
    // 通过下标改变内容,不会触发响应式,页面不会重新渲染
    // 但是数组的值会发生改变
    setTimeout(() => {
        vm.arr[0] = 5;
        vm.arr.length = 2;
    }, 5000)
</script>

以上的写法虽然改变了数组,但是不会同步渲染到页面中去,因为vm没有get,set 检测数组的每一项;
我们可以在控制台上打印实例对象查看Vue是否在监听数据的每一项


示例结果.png

通过Vue实例对象,我们就了解到Vue并没有检测数据的每一项.

那么我们如何让数组变成响应式的呢?

我们会发现Vue的响应系统是监听着数组本身的,所以我们只能采用直接替换原数组的方案

<div id="app"><!---view--->
    {{arr}}
</div>
<script src="../vue.js/vue.js"></script>
<script>

    const vm = new Vue({
        el: "#app",
        data: {
            arr:[1,2,3,4,5]
        }
    })

    // 采用替换原数组的方案,触发响应式,重新渲染页面
    setTimeout(() => {
        vm.arr = [5,2]
    }, 2000)

</script>

也可以通过数组的改变原数组的方法来触发响应系统

变异方法;

pop push shift unshift sort reserve splice()

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

变异方法 (mutation method),顾名思义,会改变被这些方法调用的原始数组。

非变异 (non-mutating method) 方法,例如:filter(), concat()slice() 。这些不会改变原始数组,但总是返回一个新数组。

变异方法触发响应系统的示例

<div id="app"><!---view--->
    {{arr}}
</div>
<script src="../vue.js/vue.js"></script>
<script>

    const vm = new Vue({
        el: "#app",
        data: {
            arr:[1,2,3,4,5]
        }
    })

    // 采用会改变原数组的一些方法来触发响应系统
    setTimeout(() => {
        // vm.arr.push(6)
        // vm.arr.pop()
        // vm.arr.unshift(6)
        // vm.arr.shift()
        // vm.arr.splice(1,2,9,8,7)
        // vm.arr.sort(() => {
        //     return -1
        // })
        // vm.arr.reverse();
    }, 2000)

</script>

其实并不是这些方法本身会触发响应系统,而是Vue 包含观察数组的变异方法的功能,所以它会触发响应系统, 然后更新视图

当使用非变异方法时,可以采用新数组替换旧数组:

<div id="app"><!---view--->
    {{arr}}
</div>
<script src="../vue.js/vue.js"></script>
<script>

    const vm = new Vue({
        el: "#app",
        data: {
            arr:[1,2,3,4,5]
        }
    })

    // 用修改后返回的数组直接覆盖原数组来触发响应系统
    setTimeout(() => {
        vm.arr = vm.arr.map(item => item*2)
    }, 2000)

</script>

虽然我们对响应式系统的底层原理不是那么的明白,
但至少现在我们就能知道怎样改变数据会触发响应系统,从而更新页面渲染,知道什么样的方法不会触发页面渲染

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345