1./compiler ⽬目錄是編譯模版;html
2./core ⽬目錄是 Vue.js 的核⼼心(也是後⾯面的重點);vue
3./platforms ⽬目錄是針對核⼼心模塊的 ‘平臺’ 模塊;node
4./server ⽬目錄是處理理服務端渲染;web
5./sfc ⽬目錄處理理單⽂文件 .vue;設計模式
6./shared ⽬目錄提供全局⽤用到的⼯工具函數。架構
Vue.js 的組成是由 core + 對應的 ‘平臺’ 補充代碼構成(獨立構建和運行時構建 只是 platforms 下 web 平臺的兩種選擇)。
app
雙向綁定(響應式原理)所涉及到的技術函數
1. Object.defineProperty 2. Observer 3. Watcher 4. Dep 5. Directive
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 幫忙修改數據。工具
觀察者模式是軟件設計模式的一種。 在此種模式中,一個目標對象管理全部相依於它的觀 察者對象,而且在它自己的狀態改變時主動發出通知。 這一般透過呼叫各觀察者所提供的 方法來實現。此種模式一般被用來實時事件處理系統。 訂閱者模式涉及三個對象: 發佈者、主題對象、訂閱者,三個對象間的是一對多的關係, 每當主題對象狀態發生改變時,其相關依賴對象都會獲得通知,並被自動更新。 看一個簡單的示例:
vue裏邊怎麼操做的呢? vue observerthis
弄明白原理和架構以後,咱們來實現一個簡單的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的文章吧
看完後能幫我點個贊嗎?