該文章簡單模擬Vue響應式原理(MVVM),筆者還只是個大三的學生,文章有不少不到之處歡迎各位大佬指出錯誤,多多交流。html
官方文檔的東西對於初學者來講都有點晦澀難懂,本文就將以一種簡單的方式來聊聊Vue的響應式原理。前端
Vuejs是數據驅動型,數據發生改變界面也會刷新改變。但這並非理所固然,在其內部作了不少複雜的操做。vue
思路react
首先要搞懂Vue內部是如何監聽數據的改變?bash
經過Object.definePropety這個方法來監聽數據的改變。app
這個方法的第三個參數的 Setters 和 Getters是關鍵。詳情ide
已經監聽了數據的改變,Vue是如何知道要通知哪些元素界面發生刷新呢?函數
經過發佈訂閱者模式。ui
Observer會監聽全部的data屬性而且給他們建立一一對應的Dep對象.this
Compile 會解析模板中指令(同時也會將界面初始化)。 一個指令就建立一個Watcher對象, 而後添加到相對應的Dep對象的訂閱中綁定更新函數。若是一個屬性的value發生改變Observer就會通知所對應的Dep對象調用notify()通知全部的訂閱者 更新界面。
詳情看下圖
PS:畫圖技術渣渣,見諒。
筆者只是簡單模擬,尚未達到能夠剖析源碼的程度。
//render.js
var render = function(template, data) {
const reg = /\{\{(\w+)\}\}/; //不作貪婪匹配
if (reg.test(template)) { //退出條件 false
//是否須要編譯
// vue源碼模板編譯用的正則方法
const key = reg.exec(template)[1]
// console.log(key)
template = template.replace(reg, data[key]);
return render(template, data) //遞歸渲染
}
// template.replace(/{{(.)+/)
return template
}
複製代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script src="./render.js"></script>
<script>
const obj = { //模擬數據
name: '張三',
age: '18',
sex: '男'
}
// 發佈訂閱者模式
class Dep {
constructor() {
this.subs = []
}
addSub(watch) { //添加訂閱方法
this.subs.push(watch)
}
notify() { //通知訂閱者更新
this.subs.forEach(item => {
item.update();
})
}
}
class Watch {
constructor(name) {
this.name = name;
}
update() {
console.log(this.name + 'update')
//更新視圖
document.getElementById('app').innerHTML = render(template,obj)
}
}
Object.keys(obj).forEach(key => { //遍歷obj對象
let dep = new Dep; //建立發佈者對象
let value = obj[key];
Object.defineProperty(obj, key, {//監聽數據
set: function(newValue) { //數據改變
console.log( '數據' + key + '改變了')
// 更新數據
value = newValue
dep.notify() //通知訂閱者更新
},
get: function() { //獲取數據
console.log('數據' + key + '加入了響應式系統')
let w = new Watch(value)
dep.addSub(w);
return value
}
})
})
obj.message = "沒用的"
//待編譯的模板
var template = '我是{{name}}, {{name}} 年齡 {{age}},性別 {{sex}} 。 {{message}} '
document.getElementById('app').innerHTML = render(template,obj);
</script>
</body>
</html>
複製代碼
obj.message = "沒用的"
這個數據沒在初始化中,全部沒有打印。即不是響應式屬性。
Vue 不容許動態添加根級響應式屬性,因此你必須在初始化實例前聲明全部根級響應式屬性。詳情看文檔聲明響應式屬性。
筆者是一個入門前端不久的小白,這篇文章挺淺顯的,可能也有一些錯誤,但願大佬們多多給點建議。若是這篇文章對你有那麼一點點小幫助的話,不妨點個贊吧。