不要再搞混Vue的響應式原理和雙向數據綁定了
前言
之前公司招人,面試了一些的前端同學,因為公司使用的前端技術是Vue,所以免不了問到其響應式原理和Vue的雙向數據綁定。但是這邊面試到的80%的同學會把兩者搞混,通常我要是先問響應式原理再問雙向數據綁定原理,來面試的同學大都會認為是一回事,那么這里我們就說一下二者的區別。
響應式原理
是Vue的核心特性之一,數據驅動視圖,我們修改數據視圖隨之響應更新,就很優雅~
Vue2.x是借助Object.defineProperty()實現的,而Vue3.x是借助Proxy實現的,下面我們先來看一下2.x的實現。
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
//攔截get,當我們訪問data.key時會被這個方法攔截到
get: function getter () {
//我們在這里收集依賴
return obj[key];
},
//攔截set,當我們為data.key賦值時會被這個方法攔截到
set: function setter (newVal) {
//當數據變更時,通知依賴項變更UI
}
})
復制代碼
我們通過Object.defineProperty為對象obj添加屬性,可以設置對象屬性的getter和setter函數。之后我們每次通過點語法獲取屬性都會執行這里的getter函數,在這個函數中我們會把調用此屬性的依賴收集到一個集合中 ;而在我們給屬性賦值(修改屬性)時,會觸發這里定義的setter函數,在次函數中會去通知集合中的依賴更新,做到數據變更驅動視圖變更。
3.x的與2.x的核心思想一致,只不過數據的劫持使用Proxy而不是Object.defineProperty,只不過Proxy相比Object.defineProperty在處理數組和新增屬性的響應式處理上更加方便。
let nObj=new Proxy(obj,{
//攔截get,當我們訪問nObj.key時會被這個方法攔截到
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}!`);
return Reflect.get(target, propKey, receiver);
},
//攔截set,當我們為nObj.key賦值時會被這個方法攔截到
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}!`);
return Reflect.set(target, propKey, value, receiver);
}
})
復制代碼
Proxy的詳細使用方法參考ES6教程。
Vue的響應式原理的實現細節相信大多數同學已經很熟悉了,這里就不在展開細談了,如果還想更詳細的了解,或者想要做一個簡易的Vue實現,可以參考這篇Vue原理,相信你會有不小收獲。
雙向數據綁定
雙向數據綁定通常是指我們使用的v-model指令的實現,是Vue的一個特性,也可以說是一個input事件和value的語法糖。Vue通過v-model指令為組件添加上input事件處理和value屬性的賦值。
<template>
<input v-model='localValue'/>
</template>
復制代碼
上述的組件就相當于如下代碼
<template>
<!-- 這里添加了input時間的監聽和value的屬性綁定 -->
<input @input='onInput' :value='localValue' />
<span>{{localValue}}</span>
</template>
<script>
export default{
data(){
return {
localValue:'',
}
},
methods:{
onInput(v){
//在input事件的處理函數中更新value的綁定值
this.localValue=v.target.value;
console.log(this.localValue)
}
}
}
</script>
復制代碼
因此當我們修改input輸入框中的值時,我們通過v-model綁定的值也會同步修改,基于上述原理,我們可以很容易的實現一個數據雙向綁定的組件。
v-model實踐
首先我們定義一個Vue組件,相信大家已經很熟悉了。
<tempalte>
<div class="count" @click="addCount">click me {{value}}</div>
</template>
<script>
export default{
props:{
//關鍵的第一步:設置一個value屬性
value:{
type:Number,
default:0
}
},
watch:{
//監聽value變化,更新組件localvalue狀態
value(v){
this.localvalue=v;
}
},
methods:{
//關鍵的第二步:事件觸發localvalue變更,通過事件同步父組件狀態變更
addCount(){
this.localvalue++;
this.$emit('input',this.localvalue);
}
},
data(){
return{
//組件狀態,遵守單項數據流原則,不直接修改props中的屬性
localvalue:0
}
},
created(){
//初始化獲取value值
this.localvalue=this.value;
}
}
</script>
復制代碼
上面的組件定了我們通過在props中添加value屬性,并且在值更新時觸發input事件。created鉤子和watch中為localvalue賦值是為了同步父組件狀態到子組件中。通過上面??的組件定義,我們就可以在組件上使用v-model指令做雙向數據綁定了。
<template>
<add-one v-model="count"></add-one>
<span>父組件{{count}}</span>
</tempalte>
<script>
export default{
data() {
return {
count: 0,
};
},
methods: {
},
created(){
}
}
</script>
復制代碼
下面是實際效果 當然我們也可以不使用value和input事件這樣的組合,為了更使得組件的定義更加符合語義,我們也可以自定義要實現雙向綁定的屬性和事件。我們在組件的model選項中設置value和event即可。如下:
export default{
//這里做了一個value和event的映射
model:{
value:'count',
event:'change'
},
props:{
//關鍵的第一步:設置一個value屬性
count:{
type:Number,
default:0
}
},
methods:{
//關鍵的第二步:事件觸發localvalue變更,通過事件同步父組件狀態變更
addCount(){
this.localvalue++;
this.$emit('change',this.localvalue);
}
},
}
復制代碼
通過上面的組件定義
<add-one v-model="count"></add-one>
復制代碼
就相當于
<template>
<add-one @change='onChange' :count='count'></add-one>
<span>{{count}}</span>
</template>
<script>
export default{
data(){
return {
count:0,
}
},
methods:{
onChange(v){
this.count=v;
console.log(this.count)
}
}
}
</script>
復制代碼
只不過v-model指令幫我們做上面的事件添加,屬性綁定和狀態同步操作罷了。這里埋下一個小伏筆,不知道同學們熟不熟悉*Vue的自定義指令*,不熟悉的同學可以在評論區留言,下篇提前把使用自定義指令的各種技巧為各位同學奉上,附帶v-model的具體實現。
加更:自定義指令的各種技巧和v-model的實現已完成,感興趣的同學可以看一下理論+實踐,徹底掌握Vue自定義指令
結語
以上就是本人關于Vue響應式原理和雙向數據綁定原理的理解