Vue data 數(shù)據(jù)屬性
1.數(shù)據(jù)屬性 Data的了解
通過前面的學習,我們知道vue的數(shù)據(jù)是寫在vue選項對象(也可以理解為配置對象)的data屬性中的,我們可以通過Mustache(雙大括號語法)就數(shù)據(jù)插入到頁面上
<div id="app">
<p>第二條狗狗的名字是:{{dogs[2]}}</p>
<p>所有狗狗名字是:{{dogs}}</p>
</div>
<script>
const vm = new Vue({
el:"#app",
data:{
dogs: ["哈士奇","中華田園犬","藏獒"]
}
})
</script>
示例的感悟:
- 會發(fā)現(xiàn),如果將這個數(shù)組或?qū)ο筝敵龅巾撁嫔?并不會像JS一樣輸出
[Object object]
- Vue會輸出JSON編碼后的值,這樣在調(diào)試的時候超級有用
- 調(diào)試時將內(nèi)容渲染到頁面比將數(shù)據(jù)輸出到控制臺更加有效果,
- 在頁面上顯示會隨著值的變化而變化(響應式),,這樣更加直觀的看到數(shù)據(jù)的變化
2.數(shù)據(jù)響應變化
當一個 Vue 實例被創(chuàng)建時,通過插值語法將數(shù)據(jù)顯示在頁面上,當我們修改數(shù)據(jù)時,頁面也會發(fā)生變化
// 我們的數(shù)據(jù)對象
var data = { a: 1 }
// 該對象被加入到一個 Vue 實例中
var vm = new Vue({
el:"#app",
data: data
})
// 獲得這個實例上的屬性
// 返回源數(shù)據(jù)中對應的字段
vm.a == data.a // => true
// 設(shè)置屬性也會影響到原始數(shù)據(jù)
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3
為什么頁面會自動發(fā)生變化呢?
- 這就是因為在Vue實例化時,就會向Vue的響應式系統(tǒng)中加入
data
對象中能夠找到的所有屬性然爆。 - 其實是Vue通過監(jiān)聽的方式一直在偵查值是否發(fā)生變化,我們可以在控制臺上打印實例:
圖如下:
通過這張圖,我們就會發(fā)現(xiàn):
- 示例中data數(shù)據(jù)屬性中的數(shù)據(jù)
a
被Vue
實例通過get
,和set
一直在檢測著, - 也就是說vue的響應系統(tǒng)一直在觀察數(shù)據(jù)
a
的變化,一旦發(fā)生變化,響應系統(tǒng)做出反應,改變視圖.
所以當數(shù)據(jù)值發(fā)生改變扁达,視圖也會產(chǎn)生響應。
2.1 認識數(shù)據(jù)響應式的變化
通過剛才例子了解當數(shù)據(jù)發(fā)生變化時筹我,頁面視圖也會進行重新渲染,值得注意的時,只有data
中存在的屬性才是響應的玩徊。
也就是說如果你添加一個新的屬性,比如:
vm.b = 'hi'
那么對b的改動不會出發(fā)任何視圖的更改谨究。因為vue并沒有將b
加入到響應系統(tǒng)中, 說白了就是沒有檢測b
的變化
那如何解決此類問題呢?
如果你知道你會晚些時候需要一個屬性恩袱,那么你可以在開始時設(shè)置一些初始值。如:
data: {
a: "hello",
b: ''
}
這里要注意一個特例:
這個例外是使用 Object.freeze()
胶哲,這個方法是凍結(jié)方法,意思是不允許修改對象的屬性值,
這就會阻止修改現(xiàn)有的屬性畔塔, 數(shù)據(jù)沒發(fā)改變,也就不會觸發(fā)Vue的響應系統(tǒng)
var obj = {
foo: 'bar'
}
Object.freeze(obj)
new Vue({
el: '#app',
data: obj
})
這個示例中我們利用了Object.freeze()
凍結(jié)方法,讓Vue實例的所有data屬性中的數(shù)據(jù)都失去了響應式, 這樣做沒什么意義, 做了時候,Vue也就失去了數(shù)據(jù)驅(qū)動的功能
但是Object.freeze()
方法在某些時候還是非常有用的, 當然不是凍結(jié)整個data
,我們可以凍結(jié)data屬性中的一些對象數(shù)據(jù),
想了解,接著往下看
2.2 對象數(shù)據(jù)的響應變化
2.2.1 了解對象數(shù)據(jù)的響應變化
剛看到的是data屬性只是是基本類型的問題, 接下來看看數(shù)據(jù)屬性值是對象的狀況
vue在初始化的時候會循環(huán)data中的數(shù)據(jù),(數(shù)據(jù)劫持),以此增加getter和setter來監(jiān)聽數(shù)據(jù)
示例:
<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>
頁面顯示結(jié)果
此時頁面正常顯示結(jié)果, 如果我們在控制臺改變對象數(shù)據(jù),會有如下結(jié)果:
通過示例了解到,Vue數(shù)據(jù)如果是對象student
,student
中的屬性name
在發(fā)生改變的時候,頁面也會發(fā)生改變,說明對象中的name
屬性是被vue關(guān)注著的
那么問題來了, Vue會檢測到student
數(shù)據(jù)的變化, 那么如果student
對象中的新鎮(zhèn)一些屬性,會不會被檢測到呢.
我們可以通過如下的示例進行測試:
<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"
}
}
})
// 會發(fā)現(xiàn)初始化存在的name屬性會觸發(fā)頁面的更新
//setTimeout(() => {
// vm.student.name = "小明";
//}, 2000)
// 對象age的改變不會觸發(fā)頁面的更新
//setTimeout(() => {
// vm.student.age = 18;
//}, 2000)
// 但是我們會發(fā)現(xiàn),如果頁面一旦更新,age的值也會渲染在頁面上,
//原因在于Vue get和set方法監(jiān)聽的是對象,不是對象的每一個屬性
setTimeout(() => {
vm.student.name = "小明";
vm.student.age = 18;
}, 2000)
</script>
通過上面三個定時器的不同測試結(jié)果, 我們了解到了:
- Vue初始化時定義的數(shù)據(jù)
student
的改變, - 以及
student
數(shù)據(jù)的name
屬性發(fā)生變化都會觸發(fā)響應系統(tǒng), 進而改變視圖, - 但是如果
student
數(shù)據(jù)新增一個屬性, 卻沒有觸發(fā)響應式系統(tǒng), 頁面也沒有重新渲染,
為什么會這樣呢?
我們可以在控制臺打印Vue實例對象
Vue會檢測
student
就不多講了
在看另外一張圖
通過這張圖我們就會發(fā)現(xiàn),Vue不但會監(jiān)聽
student
數(shù)據(jù),還會監(jiān)聽在Vue實例化時,student
就已經(jīng)存在的屬性name
,所以name
的改變也會觸發(fā)響應系統(tǒng),
但是我們后添加的對象的數(shù)據(jù)不是響應式, Vue不會根據(jù)這些數(shù)據(jù)的變化來改變對應的視圖,所以當你單獨新增一個屬性是不會觸發(fā)響應系統(tǒng),
通過示例,我們有發(fā)現(xiàn)不會觸發(fā)響應系統(tǒng)的數(shù)據(jù)屬性跟具有響應系統(tǒng)的屬性一起改變,也會到處頁面的重新渲染, 因為有人觸發(fā)了響應系統(tǒng),如果你稍后還是單獨改變age
數(shù)據(jù)值,頁面依然不會變化
總結(jié):
- 在實例化Vue之前,就已經(jīng)定義好的對象,已經(jīng)對象中的數(shù)據(jù)會被Vue響應式檢測
- 數(shù)據(jù)對象新增的屬性因為沒有被檢測,所以單獨改變不會觸發(fā)Vue響應式
- 但是只要Vue響應式被觸發(fā)了,新增的屬性值也會觸發(fā)頁面的改變
2.2.2 將不是響應系統(tǒng)檢測的數(shù)據(jù)添加到響應系統(tǒng)
那么如何才能在給對象添加新的屬性觸發(fā)vue的響應系統(tǒng),讓頁面發(fā)生更新呢?
可以通過 vm.$set()方法可以給對象添加響應式的數(shù)據(jù)變化
<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 方法改變對象的屬性會觸發(fā)響應式
// 讓頁面視圖發(fā)生更新
setTimeout(() => {
vm.$set(vm.student,"age",18)
}, 2000)
</script>
為什么$set()會觸發(fā)頁面的 改變呢? 我們可以在控制臺上打印Vue實例對象
我們會發(fā)現(xiàn)通過$set
方法處理后,Vue會將age
屬性添加到響應系統(tǒng)上,所以就觸發(fā)了響應系統(tǒng), 進而改變視圖
2.2.3 修改多個不是響應系統(tǒng)檢測的數(shù)據(jù)
但是$set方法并不好,如果我要設(shè)置100個屬性那不得累死,怎么辦呢?
所以我們可以采用直接替換屬性對象內(nèi)容的方法,因為響應系統(tǒng)會測試student
數(shù)據(jù)整體的變化
<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實時監(jiān)聽對象student的變化
// 所以我們可以采用替換對象值的方法來觸發(fā)響應式
setTimeout(() => {
vm.student = {
name: "xiaoming",
age: 18
}
}, 2000)
</script>
通過上面的例子
我們會發(fā)現(xiàn)$set每次只能新增一個屬性,如果我要新增多個屬性就不是那么友好了,
2.2.4 解決直接替換對象數(shù)據(jù)的缺點
同樣的如果采用替換原對象,通過字面量的方式替換,會發(fā)現(xiàn)如果我原對象已有多個屬性,在通過替換原對象的方式觸發(fā)響應式的時候,需要不斷重寫原對象的屬性, 就很繁瑣.
所以關(guān)于替換原對象,我們可以采用Object.assign
來給原對象擴展屬性,然后在賦值給原對象,比如
//不要這樣,這樣并沒有新的對象,還是在原有對象上新增屬性
// 這種寫法跟vm.student.age = 18 完全等價 不會觸發(fā)響應式
Object.assign(vm.student,{
age:18
})
//你應該這樣做, 在合并后形成新的對象, 在把新對象賦值給vue的數(shù)據(jù)對象
// 這樣多就會觸發(fā)響應式
vm.student = Object.assign({},vm.student,{
age:18
})
2.3 數(shù)組數(shù)據(jù)的響應變化
在操作數(shù)組是不能使用下標的方式去改變數(shù)據(jù)
因為這種方式改變數(shù)據(jù)的值并不會觸發(fā)頁面的重新渲染
因為通過下標,去改變數(shù)組中的某一項是監(jiān)控不到的
<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]
}
})
// 通過下標改變內(nèi)容,不會觸發(fā)響應式,頁面不會重新渲染
// 但是數(shù)組的值會發(fā)生改變
setTimeout(() => {
vm.arr[0] = 5;
vm.arr.length = 2;
}, 5000)
</script>
以上的寫法雖然改變了數(shù)組,但是不會同步渲染到頁面中去,因為vm沒有g(shù)et,set 檢測數(shù)組的每一項;
我們可以在控制臺上打印實例對象查看Vue是否在監(jiān)聽數(shù)據(jù)的每一項
通過Vue實例對象,我們就了解到Vue并沒有檢測數(shù)據(jù)的每一項.
那么我們?nèi)绾巫寯?shù)組變成響應式的呢?
我們會發(fā)現(xiàn)Vue的響應系統(tǒng)是監(jiān)聽著數(shù)組本身的,所以我們只能采用直接替換原數(shù)組的方案
<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]
}
})
// 采用替換原數(shù)組的方案,觸發(fā)響應式,重新渲染頁面
setTimeout(() => {
vm.arr = [5,2]
}, 2000)
</script>
也可以通過數(shù)組的改變原數(shù)組的方法來觸發(fā)響應系統(tǒng)
變異方法;
pop push shift unshift sort reserve splice()
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
變異方法 (mutation method),顧名思義,會改變被這些方法調(diào)用的原始數(shù)組澈吨。
非變異 (non-mutating method) 方法把敢,例如:filter()
, concat()
和 slice()
。這些不會改變原始數(shù)組棚辽,但總是返回一個新數(shù)組。
變異方法觸發(fā)響應系統(tǒng)的示例
<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]
}
})
// 采用會改變原數(shù)組的一些方法來觸發(fā)響應系統(tǒng)
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>
其實并不是這些方法本身會觸發(fā)響應系統(tǒng),而是Vue 包含觀察數(shù)組的變異方法的功能冰肴,所以它會觸發(fā)響應系統(tǒng), 然后更新視圖
當使用非變異方法時屈藐,可以采用新數(shù)組替換舊數(shù)組:
<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]
}
})
// 用修改后返回的數(shù)組直接覆蓋原數(shù)組來觸發(fā)響應系統(tǒng)
setTimeout(() => {
vm.arr = vm.arr.map(item => item*2)
}, 2000)
</script>
雖然我們對響應式系統(tǒng)的底層原理不是那么的明白,
但至少現(xiàn)在我們就能知道怎樣改變數(shù)據(jù)會觸發(fā)響應系統(tǒng),從而更新頁面渲染,知道什么樣的方法不會觸發(fā)頁面渲染