一、前言
在vue的視圖層與modal層進行資料互動的時,視圖層的資料傳入到modal層,modal層通過defineProperty來劫持每個元素,并系結監聽事件進行監聽,一旦監聽到資料變化,就通過defineProperty的set函式重新更新視圖層,

二、使用Object.defineProperty( )
實作核心方法就是前文所說的Object.defineProperty( ),如果要對所有屬性都進行監聽的話,那么可以通過遞回方法遍歷所有屬性值,并對其進行Object.defineProperty( )處理,
// 1.實作一個監聽器Observer,用來劫持并監聽所有屬性,如果有變動的,就通知訂閱者 function Observer(data) { this.data =https://www.cnblogs.com/zxd66666/p/ data; this.walk(data); } Observer.prototype = { walk: function(data) { var self = this; Object.keys(data).forEach(function(key) { self.defineReactive(data, key, data[key]); }); }, defineReactive: function(data, key, val) { var dep = new Dep(); var childObj = observe(val); Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function getter () { if (Dep.target) { dep.addSub(Dep.target); } return val; }, set: function setter (newVal) { if (newVal === val) { return; } val = newVal; dep.notify(); } }); } }; function observe(value, vm) { if (!value || typeof value !== 'object') { return; } return new Observer(value); }; function Dep () { this.subs = []; } Dep.prototype = { addSub: function(sub) { this.subs.push(sub); }, notify: function() { this.subs.forEach(function(sub) { sub.update(); }); } }; Dep.target = null;
三、實作Watcher進行監聽
我們只要在訂閱者Watcher初始化的時候出發對應的get函式去執行添加訂閱者操作即可,只要獲取對應的屬性值就可以觸發get函式,核心原因就是因為我們使用了Object.defineProperty( )進行資料監聽,
// 2.實作一個訂閱者Watcher,可以收到屬性的變化通知并執行相應的函式,從而更新視圖, function Watcher(vm, exp, cb) { this.cb = cb; this.vm = vm; this.exp = exp; this.value = https://www.cnblogs.com/zxd66666/p/this.get(); // 將自己添加到訂閱器的操作 } Watcher.prototype = { update: function() { this.run(); }, run: function() { var value = https://www.cnblogs.com/zxd66666/p/this.vm.data[this.exp]; var oldVal = this.value; if (value !== oldVal) { this.value =https://www.cnblogs.com/zxd66666/p/ value; this.cb.call(this.vm, value, oldVal); } }, get: function() { Dep.target = this; // 快取自己 var value = https://www.cnblogs.com/zxd66666/p/this.vm.data[this.exp] // 強制執行監聽器里的get函式 Dep.target = null; // 釋放自己 return value; } };
四、最后利用Compile決議和系結dom節點
決議器Compile實作步驟:
1.決議模板指令,并替換模板資料,初始化視圖;
2.將模板指令對應的節點系結對應的更新函式,初始化相應的訂閱器;
為了決議模板,首先需要獲取到dom元素,然后對含有dom元素上含有指令的節點進行處理,因此這個環節需要對dom操作比較頻繁,所有可以先建一個fragment片段,將需要決議的dom節點存入fragment片段里再進行處理:
// 3.實作一個決議器Compile,可以掃描和決議每個節點的相關指令,并根據初始化模板資料以及初始化相應的訂閱器, function Compile(el, vm) { this.vm = vm; this.el = document.querySelector(el); this.fragment = null; this.init(); } Compile.prototype = { init: function () { if (this.el) { this.fragment = this.nodeToFragment(this.el); this.compileElement(this.fragment); this.el.appendChild(this.fragment); } else { console.log('Dom元素不存在'); } }, nodeToFragment: function (el) { var fragment = document.createDocumentFragment(); var child = el.firstChild; while (child) { // 將Dom元素移入fragment中 fragment.appendChild(child); child = el.firstChild } return fragment; }, compileElement: function (el) { var childNodes = el.childNodes; var self = this; [].slice.call(childNodes).forEach(function(node) { var reg = /\{\{(.*)\}\}/; var text = node.textContent; if (self.isElementNode(node)) { self.compile(node); } else if (self.isTextNode(node) && reg.test(text)) { self.compileText(node, reg.exec(text)[1]); } if (node.childNodes && node.childNodes.length) { self.compileElement(node); } }); }, compile: function(node) { var nodeAttrs = node.attributes; var self = this; Array.prototype.forEach.call(nodeAttrs, function(attr) { var attrName = attr.name; if (self.isDirective(attrName)) { var exp = attr.value; var dir = attrName.substring(2); if (self.isEventDirective(dir)) { // 事件指令 self.compileEvent(node, self.vm, exp, dir); } else { // v-model 指令 self.compileModel(node, self.vm, exp, dir); } node.removeAttribute(attrName); } }); }, compileText: function(node, exp) { var self = this; var initText = this.vm[exp]; this.updateText(node, initText); new Watcher(this.vm, exp, function (value) { self.updateText(node, value); }); }, compileEvent: function (node, vm, exp, dir) { var eventType = dir.split(':')[1]; var cb = vm.methods && vm.methods[exp]; if (eventType && cb) { node.addEventListener(eventType, cb.bind(vm), false); } }, compileModel: function (node, vm, exp, dir) { var self = this; var val = this.vm[exp]; this.modelUpdater(node, val); new Watcher(this.vm, exp, function (value) { self.modelUpdater(node, value); }); node.addEventListener('input', function(e) { var newValue =https://www.cnblogs.com/zxd66666/p/ e.target.value; if (val === newValue) { return; } self.vm[exp] = newValue; val = newValue; }); }, updateText: function (node, value) { node.textContent = typeof value =https://www.cnblogs.com/zxd66666/p/='undefined' ? '' : value; }, modelUpdater: function(node, value, oldValue) { node.value = typeof value =https://www.cnblogs.com/zxd66666/p/='undefined' ? '' : value; }, isDirective: function(attr) { return attr.indexOf('v-') == 0; }, isEventDirective: function(dir) { return dir.indexOf('on:') === 0; }, isElementNode: function (node) { return node.nodeType == 1; }, isTextNode: function(node) { return node.nodeType == 3; } }
這樣就大功告成了,此文章參考https://www.cnblogs.com/libin-1/p/6893712.html,完整源代碼下載,請點擊這里獲取;
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/79813.html
標籤:JavaScript
上一篇:HTTP和前后端分離
