一.Vue響應式原理
首先要了解幾個概念:
資料響應式:資料模型僅僅是普通的Javascript物件,而我們修改資料時,檢視會進行更新,避免了繁瑣的DOM操作,提高開發效率。
雙向繫結:資料改變,檢視改變,資料也隨之改變,我們可以使用v-model在表單上建立雙向資料繫結。
資料驅動是Vue最獨特的特性之一:開發過程中僅需要關注資料本身,不需要關心資料是如何渲染到檢視。
vue2.X中的響應式原理是基於defineProperty,相容IE8以上版本,核心原理程式碼如下:
let data={
msg:'hello',
count:10
}
let vm={}
proxyData(data)
function proxyData(data){
Object.keys(data).forEach(key=>{
Object.defineProperty(vm,key,{
enumerable:true,
configurable:true,
writeable:true,
//獲取值的時候執行
get(){
console.log('get:',key,data[key])
return data[key]
},
//設定值的時候執行
set(newValue){
data[key]=newValue
console.log('set:',key,newValue)
document.querySelector('#app').textContent=data[key]
}
})
})
}
vm.msg //獲取(get方法) hello
vm.msg='hello World' //設定新屬性值並渲染到頁面(set方法)
vm.msg //hello World
vue3.X中的響應式原理是基於Proxy,直接監聽物件,而非屬性,ES6中新增,IE不支援,效能由瀏覽器優化,效能比defineProperty要好,程式碼的話相比較defineProperty要簡潔一些,對於多個屬性的值不需要進行迴圈遍歷處理。
let data={
msg:'hello',
count:0
} //模擬 Vue 例項
let vm=new Proxy(data,{
//執行代理行為的函式
//當訪問 vm 的成員會執行
get(target,key){
console.log('get,key:',key,target[key])
return target[key]
},
set(target,key,newValue){
console.log('set,key:',key,newValue)
if(target[key] === newValue){
return
}
target[key]=newValue
document.querySelector("#app").textContent=target[key]
}
})
//測試
vm.msg='Hello World'
console.log(vm.msg)
二.釋出訂閱模式和觀察者模式
1.釋出/訂閱模式
這個概念有些抽象,下面舉個例子說明下,家長比較關心孩子成績,天天問孩子成績出來沒,假設可以到孩子所在班級去訂閱孩子成績,一旦考試成績出來,相當於觸發了一個事件,最後有班級的老師以簡訊的形式通知給家長,
不需要天天問孩子成績出來沒,家長就是事件的訂閱者,老師是事件的釋出者,孩子所在的班級可以假想成一個事件的中心。vue中的自定義事件都是基於釋出/訂閱模式的。下面模擬下發布訂閱模式的執行機制:
//事件觸發器
class EventEmitter(){
constructor(){
// 初始化物件{ 'click':[fn1,fn2],'change':[fn] }
this.subs=Object.create(null)
}
//註冊事件
$on(eventType,handler){
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
//觸發事件
$emit(eventType){
if(this.subs[eventType]){
this.subs[eventType].forEach(handler => {
handler()
})
}
}
}
//測試 let em =new EventEmitter() em.$on('click',()=>{
console.log('click1')
})
em.$on('click',()=>{
console.log('click2')
})
em.$emit('click') //列印結果 click1,click2
二.觀察者模式
觀察者模式和訂閱模式的區別是沒有事件中心,只有釋出者和訂閱者,並且釋出者需要知道訂閱者的存在.
概念:
觀察者 --Watcher
update():當事件發生時,具體要做的事情。
釋出者 --Dep
subs陣列:儲存所有的觀察者
addSub():新增觀察者
notify():當事件發生,呼叫所有觀察者的update()方法
//釋出者-目標
class Dep{
constructor() {
//記錄所有的訂閱者
this.subs=[]
}
//新增訂閱者
addsub(sub){
if(sub && sub.update){
this.subs.push(sub)
}
}
//釋出通知
notify(){
this.subs.forEach(sub=>{
sub.update()
})
}
}
//訂閱者-觀察者
class Watcher{
update(){
console.log('update')
}
}
//測試
let dep=new Dep()
let watcher=new Watcher()
dep.addsub(watcher)
dep.notify() //列印結果 update
總結:
觀察者模式是由具體目標排程,比如當事件觸發,Dep就會去呼叫觀察者的方法,所以觀察者的訂閱者和釋出者之間是存在依賴的。
釋出訂閱模式由統一排程中心呼叫,因此釋出者和訂閱者不需要知道對方的存在。