摘要:原理的雙向數(shù)據(jù)綁定的原理相信大家都十分了解主要是通過的對(duì)象的屬性重寫的和函數(shù)來實(shí)現(xiàn)的所以接下來不使用進(jìn)行實(shí)際的代碼開發(fā)過程中如果函數(shù)使用父級(jí)的情況還是使用顯示緩存中間變量和閉包來處理原因是箭頭函數(shù)沒有獨(dú)立的執(zhí)行上下文所以箭頭函數(shù)內(nèi)部出現(xiàn)對(duì)象
1.原理
vue的雙向數(shù)據(jù)綁定的原理相信大家都十分了解;主要是通過ES5的Object對(duì)象的defineProperty屬性;重寫data的set和get函數(shù)來實(shí)現(xiàn)的
所以接下來不使用ES6進(jìn)行實(shí)際的代碼開發(fā);過程中如果函數(shù)使用父級(jí)this的情況;還是使用顯示緩存中間變量和閉包來處理;原因是箭頭函數(shù)沒有獨(dú)立的執(zhí)行上下文this;所以箭頭函數(shù)內(nèi)部出現(xiàn)this對(duì)象會(huì)直接訪問父級(jí);所以也能看出箭頭函數(shù)是無法完全替代function的使用場(chǎng)景的;比如我們需要獨(dú)立的this或者argument的時(shí)候1.2 defineProperty是什么
語(yǔ)法:
Object.defineProperty(obj, prop, descriptor) 參數(shù): obj:必要的目標(biāo)對(duì)象 prop:必要的需要定義或者修改的屬性名 descriptor:必要的目標(biāo)屬性全部擁有的屬性 返回值: 返回傳入的第一個(gè)函數(shù);即第一個(gè)參數(shù)obj
該方法允許精確的添加或者修改對(duì)象的屬性;通過賦值來添加的普通屬性會(huì)創(chuàng)建在屬性枚舉期間顯示(fon...in;object.key);這些添加的值可以被改變也可以刪除;也可以給這個(gè)屬性設(shè)置一些特性;比如是否只讀不可寫;目前提供兩種形式:數(shù)據(jù)描述(set;get;value;writable;enumerable;confingurable)和存取器描述(set;get)
數(shù)據(jù)描述當(dāng)修改或者定義對(duì)象的某個(gè)屬性的時(shí)候;給這個(gè)屬性添加一些特性
var obj = { name:"xiangha" } // 對(duì)象已有的屬性添加特性描述 Object.defineProperty(obj,"name",{ configurable:true | false, // 如果是false則不可以刪除 enumerable:true | false, // 如果為false則在枚舉時(shí)候會(huì)忽略 value:"任意類型的值,默認(rèn)undefined" writable:true | false // 如果為false則不可采用數(shù)據(jù)運(yùn)算符進(jìn)行賦值 }); 但是存在一個(gè)交叉;如果wrirable為true;而configurable為false的時(shí)候;所以需要枚舉處理enumerable為false --- 我是一個(gè)writable栗子 --- var obj = {}; Object.defineProperty(obj,"val",{ value:"xiangha", writable:false, // false enumerable:true, configurable:true }); obj.val = "書記"; // 這個(gè)時(shí)候是更改不了a的 --- 我是一個(gè)configurable栗子 --- var obj = {}; Object.defineProperty(obj,"val",{ value:"xiangha", writable:true, // true enumerable:true, configurable:false // false }); obj.val = "書記"; // 這個(gè)時(shí)候是val發(fā)生了改變 delete obj.val 會(huì)返回false;并且val沒有刪除 --- 我是一個(gè)enumerable栗子 --- var obj = {}; Object.defineProperty(obj,"val",{ value:"xiangha", writable:true, enumerable:false, // false configurable:true }); for(var i in obj){ console.log(obj[i]) // 沒有具體值 } 綜上:對(duì)于我們有影響主要是configurable控制是否可以刪除;writable控制是否可以修改賦值;enumerable是否可以枚舉
所以說一旦使用Object.defineProperty()給對(duì)象添加屬性;那么如果不設(shè)置屬性的特性;則默認(rèn)值都為false
var obj = {}; Object.defineProperty(obj,"name",{}); // 定義了心屬性name后;這個(gè)屬性的特性的值都為false;這就導(dǎo)致name這個(gè)是不能重寫不能枚舉不能再次設(shè)置特性的 obj.name = "書記"; console.log(obj.name); // undefined for(var i in obj){ console.log(obj[i]) }
總結(jié)特性:
value:設(shè)置屬性的值
writable ["ra?t?bl] :值是否可以重寫
enumerable [?"nju:m?r?b?l]:目標(biāo)屬性是否可以被枚舉
configurable [k?n"f?g?r?bl]:目標(biāo)屬性是否可以被刪除是否可以再次修改特性
存取器描述var obj = {}; Object.defineProperty(obj,"name",{ get:function(){} | undefined, set:function(){} | undefined, configuracble:true | false, enumerable:true | false }) 注意:當(dāng)前使用了setter和getter方法;不允許使用writable和value兩個(gè)屬性
gettet&& setter
當(dāng)設(shè)置獲取對(duì)象的某個(gè)屬性的時(shí)候;可以提供getter和setter方法
var obj = {}; var value = "xiangha"; Object.defineProperty(obj,"name",{ get:function(){ // 獲取值觸發(fā) return value }, set:function(val){ // 設(shè)置值的時(shí)候觸發(fā);設(shè)置的新值通過參數(shù)val拿到 value = val; } }); console.log(obj.name); // xiangha obj.name = "書記"; console,.log(obj.name); // 書記
get和set不是必須成對(duì)出現(xiàn)對(duì);任寫一個(gè)就行;如果不設(shè)置set和get方法;則為undefined
哈哈;前戲終于鋪墊完成了
補(bǔ)充:如果使用vue開發(fā)項(xiàng)目;嘗試去打印data對(duì)象的時(shí)候;會(huì)發(fā)現(xiàn)data內(nèi)的每一個(gè)屬性都有g(shù)et和set屬性方法;這里說明一下vue和angular的雙向數(shù)據(jù)綁定不同
angular是用臟數(shù)據(jù)檢測(cè);Model發(fā)生改變的時(shí)候;會(huì)檢測(cè)所有視圖是否綁定了相關(guān)的數(shù)據(jù);再更新視圖
vue是使用的發(fā)布訂閱模式;點(diǎn)對(duì)點(diǎn)的綁定數(shù)據(jù)
網(wǎng)上圖
2.實(shí)現(xiàn)頁(yè)面很簡(jiǎn)單;包含:
1. 一個(gè)input,使用v-model指令 2. 一個(gè)button,使用v-click指令 3. 一個(gè)h3,使用v-bind指令。
我們最后也會(huì)類似vue對(duì)方式來實(shí)現(xiàn)雙向數(shù)據(jù)綁定
var app = new xhVue({ el:"#app", data: { number: 0 }, methods: { increment: function() { this.number ++; }, } })2.1 定義
首先我們需要定義一個(gè)xhVue的構(gòu)造函數(shù)
function xhVue(options){ }2.2 添加
為了初始化這個(gè)構(gòu)造函數(shù);給其添加一個(gè)_init屬性
function xhVue(options){ this._init(options); } xhVue.prototype._init = function(options){ this.$options = options; // options為使用時(shí)傳入的結(jié)構(gòu)體;包括el,data,methods等 this.$el = document.querySelector(options.el); // el就是#app,this.$el是id為app的Element元素 this.$data = options.data; // this.$data = {number:0} this.$methods = options.methods; // increment }2.3 改造升級(jí)
改造_init函數(shù);并且實(shí)現(xiàn)_xhob函數(shù);對(duì)data進(jìn)行處理;重寫set和get函數(shù)
xhVue.prototype._xhob = function(obj){ // obj = {number:0} var value; for(key in obj){ if(obj.hasOwnProperty(ket)){ value = obj[key]; if(typeof value === "object"){ this._xhob(value); } Object.defineProperty(this.$data,key,{ enumerable:true, configurable:true, get:function(){ return value; }, set:function(newVal){ if(value !== newVal){ value = newVal; } } }) } } } xhVue.prototype._init = function(options){ this.$options = options; this.$el = document.querySelector(options.el); this.$data = options.data; this.$method = options.methods; this._xhob(this.$data); }2.4 xhWatcher
指令類watcher;用來綁定更新函數(shù);實(shí)現(xiàn)對(duì)DOM更新
function xhWatcher(name,el,vm,exp,attr){ this.name = name; // 指令名稱;對(duì)于文本節(jié)點(diǎn);例如text this.el = el; // 指令對(duì)應(yīng)DOM元素 this.vm = vm; // 指令所屬vue實(shí)例 this.exp = exp; // 指令對(duì)應(yīng)的值;例如number this.attr = attr; // 綁定的屬性值;例如innerHTML this.update(); } xhWatcher.prototype.update = function(){ this.el[this.attr] = this.vm.$data[this.exp]; // 例如h3的innerHTML = this.data.number;當(dāng)numner改變則會(huì)觸發(fā)本update方法;保證對(duì)應(yīng)的DOM實(shí)時(shí)更新 }2.5 完善_init和_xhob
繼續(xù)完善_init和_xhob函數(shù)
// 給init的時(shí)候增加一個(gè)對(duì)象來存儲(chǔ)model和view的映射關(guān)系;也就是我們前面定義的xhWatcher的實(shí)例;當(dāng)model發(fā)生變化時(shí);我們會(huì)觸發(fā)其中的指令另其更新;保證了view也同時(shí)更新 xhVue.prototype._init = function(options){ this.$options = options; this.$el = document.querySelector(options.el); this.$data = options.data; this.$method = options.methods; this._binding = {}; // _binding this._xhob(this.$data); } // 通過init出來的_binding xhVue.prototype._xhob = function(obj){ // obj = {number:0} var value; for(key in obj){ if(obj.hasOwnProperty(ket)){ this._binding[key] = { // _binding = {number:_directives:[]} _directives = [] } value = obj[key]; if(typeof value === "object"){ this._xhob(value); } var binding = this._binding[key]; Object.defineProperty(this.$data,key,{ enumerable:true, configurable:true, get:function(){ return value; }, set:function(newVal){ if(value !== newVal){ value = newVal; // 當(dāng)number改變時(shí);觸發(fā)_binding[number]._directives中已綁定的xhWatcher更新 binding._directives.forEach(function(item){ item.update(); }); } } }) } } }2.6 解析指令
怎么才能將view與model綁定;我們定義一個(gè)_xhcomplie函數(shù)來解析我們的指令(v-bind;v-model;v-clickde)并這這個(gè)過程中對(duì)view和model進(jìn)行綁定
xhVue.prototype._xhcompile = function (root) { // root是id為app的element的元素;也就是根元素 var _this = this; var nodes = root.children; for (var i = 0,len = nodes.length; i < len; i++) { var node = nodes[i]; if (node.children.length) { // 所有元素進(jìn)行處理 this._xhcompile(node) }; // 如果有v-click屬性;我們監(jiān)聽他的click事件;觸發(fā)increment事件,即number++ if (node.hasAttribute("v-click")) { node.onclick = (function () { var attrVal = nodes[i].getAttribute("v-click"); // bind讓data的作用域與methods函數(shù)的作用域保持一致 return _this.$method[attrVal].bind(_this.$data); })(); }; // 如果有v-model屬性;并且元素是input或者textrea;我們監(jiān)聽他的input事件 if (node.hasAttribute("v-model") && (node.tagName = "INPUT" || node.tagName == "TEXTAREA")) { node.addEventListener("input", (function (key) { var attrVal = node.getAttribute("v-model"); _this._binding[attrVal]._directives.push(new xhWatcher( "input", node, _this, attrVal, "value" )); return function () { // 讓number的值和node的value保持一致;就實(shí)現(xiàn)了雙向數(shù)據(jù)綁定 _this.$data[attrVal] = nodes[key].value } })(i)); }; // 如果有v-bind屬性;我們要讓node的值實(shí)時(shí)更新為data中number的值 if (node.hasAttribute("v-bind")) { var attrVal = node.getAttribute("v-bind"); _this._binding[attrVal]._directives.push(new xhWatcher( "text", node, _this, attrVal, "innerHTML" )) } } }
并且將解析函數(shù)也加到_init函數(shù)中
xhVue.prototype._init = function(options){ this.$options = options; this.$el = document.querySelector(options.el); this.$data = options.data; this.$method = options.methods; this._binding = {}; // _binding this._xhob(this.$data); this._xhcompile(this.$el); }最后
Document
所有的代碼;復(fù)制到編輯器就可查看效果了~~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/94309.html
摘要:它由微軟架構(gòu)師和開發(fā),通過利用微軟圖形系統(tǒng)和的互聯(lián)網(wǎng)應(yīng)用派生品的特性來簡(jiǎn)化用戶界面的事件驅(qū)動(dòng)程序設(shè)計(jì)。微軟的和架構(gòu)師之一于年在他的博客上發(fā)表了。更改時(shí)會(huì)得到提醒這個(gè)情況是一個(gè)單向流。 前言 記得四個(gè)月前有一次面試,面試官問我 MVVM 是什么,MVVM 的本質(zhì)是什么。我大腦一片混亂,那時(shí)我對(duì) MVVM 的認(rèn)知就只是雙向綁定和Vue,以這個(gè)關(guān)鍵字簡(jiǎn)單回答了幾句,我反問 MVVM 的本質(zhì)是...
摘要:原理的雙向數(shù)據(jù)綁定的原理相信大家都十分了解主要是通過的對(duì)象的屬性重寫的和函數(shù)來實(shí)現(xiàn)的所以接下來不使用進(jìn)行實(shí)際的代碼開發(fā)過程中如果函數(shù)使用父級(jí)的情況還是使用顯示緩存中間變量和閉包來處理原因是箭頭函數(shù)沒有獨(dú)立的執(zhí)行上下文所以箭頭函數(shù)內(nèi)部出現(xiàn)對(duì)象 1.原理 vue的雙向數(shù)據(jù)綁定的原理相信大家都十分了解;主要是通過ES5的Object對(duì)象的defineProperty屬性;重寫data的set...
摘要:響應(yīng)式原理為了探究這一切的原因,我再次點(diǎn)開了的官網(wǎng)。在官網(wǎng)很下面的位置,找到了關(guān)于響應(yīng)式原理的說明。因此,新添加到數(shù)組中的對(duì)象中的屬性,就成了非響應(yīng)式的屬性了,改變它自然不會(huì)讓組件重新渲染。響應(yīng)式屬性的對(duì)象,有這個(gè)對(duì)象就代表是響應(yīng)式的。 ??最近在用Vue開發(fā)一個(gè)后臺(tái)管理的demo,有一個(gè)非常常規(guī)的需求。然而這個(gè)常規(guī)的需求中,包含了大量的知識(shí)點(diǎn)。有一個(gè)產(chǎn)品表格,用來顯示不同產(chǎn)品的信息。...
摘要:的模式之間不同主要是與的數(shù)據(jù)傳遞的流程不同。所以無論是復(fù)雜化簡(jiǎn)單化還是修改流程,基本都是因?yàn)榧夹g(shù)棧變化了對(duì)應(yīng)做的調(diào)整。實(shí)例實(shí)際項(xiàng)目往往采用更靈活的方式,以為例。用戶可以向發(fā)送指令事件,再由直接要求改變狀態(tài)。與不發(fā)生聯(lián)系,都通過傳遞。 概述 M -V- X 本質(zhì)都是一樣的 重點(diǎn)還是在于M-V 的橋梁要靠 X來牽線。 X的模式之間不同 主要是 M與V 的數(shù)據(jù)傳遞的流程不同。數(shù)據(jù)傳遞的流程不...
閱讀 1944·2023-04-26 00:59
閱讀 3213·2021-11-15 18:10
閱讀 3149·2021-09-22 16:02
閱讀 833·2021-09-02 15:15
閱讀 3797·2019-08-30 15:56
閱讀 1977·2019-08-30 15:54
閱讀 2931·2019-08-29 16:31
閱讀 2106·2019-08-29 16:10