實現一個簡易的vue
阿新 • • 發佈:2018-12-26
1./compiler ⽬目錄是編譯模版;
2./core ⽬目錄是 Vue.js 的核⼼心(也是後⾯面的重點);
3./platforms ⽬目錄是針對核⼼心模組的 ‘平臺’ 模組;
4./server ⽬目錄是處理理服務端渲染;
5./sfc ⽬目錄處理理單⽂檔案 .vue;
6./shared ⽬目錄提供全域性⽤用到的⼯工具函式。
Vue.js 的組成是由 core + 對應的 ‘平臺’ 補充程式碼構成(獨立構建和執行時構建 只是 platforms 下 web 平臺的兩種選擇)。
vue的雙向資料繫結
雙向繫結(響應式原理)所涉及到的技術
1. Object.defineProperty
2. Observer
3. Watcher
4. Dep
5. Directive
複製程式碼
1. Object.defineProperty
var obj = {};
var a;
Object.defineProperty(obj,'a',{
get: function(){
console.log('get val');
return a;
},
set: function(newVal){
console.log('set val:' + newVal);
a = newVal;
}
});
obj.a // get val; 相當於<span>{{a}}</span>
obj.a = '111'; // set val:111 相當於<input v-model="a">
複製程式碼
setter 觸發訊息到 Watcher watcher幫忙告訴 Directive 更新DOM,DOM中修改了資料 也會通知給 Watcher,watcher 幫忙修改資料。
2. Observer
觀察者模式是軟體設計模式的一種。
在此種模式中,一個目標物件管理所有相依於它的觀 察者物件,並且在它本身的狀態改變時主動發出通知。
這通常透過呼叫各觀察者所提供的 方法來實現。此種模式通常被用來實時事件處理系統。
訂閱者模式涉及三個物件:
釋出者、主題物件、訂閱者,三個物件間的是一對多的關係,
每當主題物件狀態發生改變時,其相關依賴物件都會得到通知,並被自動更新。
看一個簡單的示例:
複製程式碼
vue裡邊怎麼操作的呢? vue observer
3. watcher
- Dep
- Directive
弄明白原理和架構之後,我們來實現一個簡單的vue雙向資料繫結
1.這個Vue是從哪裡來的呢?
是通過上述方法例項化的一個物件;但是裡邊有兩個未知生物 observe ? Compile? observe中寫的是雙向繫結的核心原理就是Object.defineProperty通過set,get來設定值與獲取值
把text屬性繫結到vue例項上面去使用
那其中的Dep又是什麼呢?
新增訂閱者跟通知訂閱更新再來看一下Compile中寫的什麼吧
function Compile(node, vm) {
if (node) {
this.$frag = this.nodeToFragment(node, vm);
return this.$frag;
}
}
Compile.prototype = {
nodeToFragment: function (node, vm) {
var self = this;
var frag = document.createDocumentFragment(); // 建立一段html文件片段
var child;
while (child = node.firstChild) {
self.compileElement(child, vm);
frag.append(child); // 將所有子節點新增到fragment中
}
return frag;
},
compileElement: function (node, vm) {
var reg = /\{\{(.*)\}\}/;
//節點型別為元素
if (node.nodeType === 1) {
var attr = node.attributes;
// 解析屬性
for (var i = 0; i < attr.length; i++) {
if (attr[i].nodeName == 'v-model') {
var name = attr[i].nodeValue; // 獲取v-model繫結的屬性名
node.addEventListener('input', function (e) {
// 給相應的data屬性賦值,進而觸發該屬性的set方法
// 觸發set vm[name]
vm[name] = e.target.value;
});
// node.value = vm[name]; // 將data的值賦給該node
new Watcher(vm, node, name, 'value');
}
};
}
//節點型別為text
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
var name = RegExp.$1; // 獲取匹配到的字串
name = name.trim();
// node.nodeValue = vm[name]; // 將data的值賦給該node
new Watcher(vm, node, name, 'nodeValue');
}
}
},
}
複製程式碼
哦,原來Compile中是渲染html的啊。其中的Watcher是不是監控節點變化,然後給Dep通知的呢?
function Watcher(vm, node, name, type) {
Dep.target = this;
this.name = name; //text
this.node = node; // 節點
this.vm = vm; // vue例項
this.type = type; //nodeValue 當前節點的值
this.update();
Dep.target = null;
}
Watcher.prototype = {
update: function() {
this.get();
var batcher = new Batcher();
batcher.push(this);
// this.node[this.type] = this.value; // 訂閱者執行相應操作
},
cb:function(){
this.node[this.type] = this.value; // 訂閱者執行相應操作
},
// 獲取data的屬性值
get: function() {
this.value = this.vm[this.name]; //觸發相應屬性的get
}
}
複製程式碼
哎呦,咱們猜對了呢,這樣雙向資料繫結馬上就要完成了,只剩一個Vue.nextTick()的地方了
/**
* 批處理建構函式
* @constructor
*/
function Batcher() {
this.reset();
}
/**
* 批處理重置
*/
Batcher.prototype.reset = function () {
this.has = {};
this.queue = [];
this.waiting = false;
};
/**
* 將事件新增到佇列中
* @param job {Watcher} watcher事件
*/
Batcher.prototype.push = function (job) {
if (!this.has[job.name]) {
this.queue.push(job);
this.has[job.name] = job;
if (!this.waiting) {
this.waiting = true;
setTimeout(() => {
this.flush();
});
}
}
};
/**
* 執行並清空事件佇列
*/
Batcher.prototype.flush = function () {
this.queue.forEach((job) => {
job.cb();
});
this.reset();
};
複製程式碼
看完後是不是覺得超簡單呢?
vue3版本將做出巨大的變化,把Dep跟Watcher都幹掉了,html直接跟資料進行繫結,等vue3出來後,在寫一篇關於vue的文章吧
看完後能幫我點個贊嗎?