摘要:在等庫中,開頭的屬性方法都是框架自用的。使用或者更新的,就能得到所有訪問器屬性的定義對象,然后合成。至此,內(nèi)部各種概念的關(guān)系圖如下框架設(shè)計第三版,敬請期待
不是有了Object.defineProperty, Proxy或Reflect,放進一個對象就new出一個ViewModel出來。只能說,它們是必要條件。我們需要將要監(jiān)聽的屬性變成訪問器屬性,所有訪問器屬性都是共用同一套setter, getter方法。getter里面做依賴收集(不是必須的),setter里做視圖刷新或觸發(fā)該屬性的$watch回調(diào)。在此之前,我們需要完成一套觀察者模式,就是github中常見的EventEmitter庫:
https://github.com/Olical/Eve...
https://github.com/asyncly/Ev...
https://github.com/primus/eve...
但這些庫的訂閱數(shù)組都是放函數(shù)。我們需要放綁定對象,需要改造一下,并且改成$watch, $fire接口。
var EventBus = { $watch: function (type, callback) { var binding = callback if (typeof callback === "function") { binding = { expr: type, update: callback } } var bus = this.$events var list = bus[type] if (!list) { list = bus[type] = [] } function unwatch() { avalon.Array.remove(list, binding) if(!list.length){ delete bus[type] } } list.push(binding) return unwatch }, $fire: function (type, value) { var list = this.$events[type] if (list && lsit.length) { for (var i = 0, obj; obj = list[i++];) { obj.update() } } } }
然后我們再為它添加一個$id,用于標(biāo)記這個VM是作用于頁面某個元素上的。
var vm = avalon.define({ $id: "test", aaa: 1, bbb: 2 }){{@aaa}}
我們看avalon.define的一個簡單實現(xiàn):
avalon.define = function(obj) { var vm = {} var other = {} for (var name in obj) { if (typeof obj[name] !== "function" && name.charAt(0) !== "$") { (function (key, value) { function get(){ //在avalon1.4,1.5中這里會進行動態(tài)依賴收集,詳見這里 //https://github.com/RubyLouvre/avalon/blob/1.5/src/10%20dependency.js return get._value } get._value = value Object.defineProperty(obj, key, { set: function (newValue) { if(newValue !== get._value){ get._value = newValue if(vm.$hashcode) vm.$fire(key, newValue) } return newValue }, get: get }) })(name, obj[name]) }else{ other[name] = obj[name] } } for(var name in other){ vm[name] = other } vm.$events = {} vm.$hashcode = new Date - Math.random() vm.$fire = EventBus.$fire vm.$watch = EventBus.$watch return avalon.vmodels[vm.$id] = vm }
此外,你可以添加更多以$開頭的屬性方法,來增強它的功能。在avalon, angular等庫中,$開頭的屬性方法都是框架自用的。avalon2的一個簡單的vm是藏了許多不可遍歷的$xxx屬性方法:
那么如何將綁定屬性放進vm.$events.aaa數(shù)組中呢。這就要靠掃描機制。從上到下掃描。
avalon.scan = function (el, vm) { scanNodes([el], vm) } function scanNodes(array, vm) { for (var i = 0, el; el = array[i++]; ) { switch (el.nodeType) { case 1: scanTag(el, vm) break case 3: scanText(el, vm) break } } } function scanTag(el, vm){ var id = el.getAttribute("ms-controller") if(id && avalon.vmodels[id]){ var vm2 = avalon.vmodels[id] if(vm && vm2 && vm == vm2){ vm = mergeVM(vm,vm2) }else{ vm = vm2 } el.removeAttribute("ms-controller") } var bindings = scanAttrs(el,vm) for(var i = 0, b; b = bindings[i++];){ vm.$watch(b.expr, b) //重點 } if(el.children && el.children.length){ scanNodes(el.children, vm) } } function scanText(){ // 用正則檢測是否有花括號 // 有則轉(zhuǎn)換為綁定對象 // 并進行vm.$watch } function scanAttrs(){ //遍歷el.attributes中所有對象,看name是否以ms-開頭 }
那么如何將綁定屬性放進vm.$events.aaa數(shù)組中呢。這就要靠掃描機制。從上到下掃描。
avalon.scan = function (el, vm) { scanNodes([el], vm) } function scanNodes(array, vm) { for (var i = 0, el; el = array[i++]; ) { switch (el.nodeType) { case 1: scanTag(el, vm) break case 3: scanText(el, vm) break } } } function scanTag(el, vm){ var id = el.getAttribute("ms-controller") if(id && avalon.vmodels[id]){ var vm2 = avalon.vmodels[id] if(vm && vm2 && vm == vm2){ vm = mergeVM(vm,vm2) }else{ vm = vm2 } el.removeAttribute("ms-controller") } var bindings = scanAttrs(el,vm) for(var i = 0, b; b = bindings[i++];){ vm.$watch(b.expr, b) //重點 } if(el.children && el.children.length){ scanNodes(el.children, vm) } } function scanText(){ // 用正則檢測是否有花括號 // 有則轉(zhuǎn)換為綁定對象 // 并進行vm.$watch } function scanAttrs(){ //遍歷el.attributes中所有對象,看name是否以ms-開頭 }
里面用到一個mergeVM方法,其實也簡單,就是將兩個VM合并成一個新的VM。使用Object.getOwnPropertyDescriptor或者更新的Object.getOwnPropertyDescriptors,就能得到所有訪問器屬性的定義對象,然后合成。如果是古老瀏覽器,我們可以將訪問器屬性放到一個叫$accessors對象上。
現(xiàn)在我們這個VM是很簡單的,它只支持一重屬性,如果屬性的屬性也是對象呢。這個我們需要將這define方法遞 歸一下不就行了嗎?!對于數(shù)組的監(jiān)控,業(yè)界流行的方法是重寫數(shù)組的大部分方法,然后再加上一些移除數(shù)組的方法。
至此,avalon內(nèi)部各種概念的關(guān)系圖如下:
from 《javascript框架設(shè)計》第三版,敬請期待
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/87935.html
摘要:換言之,的對應(yīng)的,此外它還有。它們共同構(gòu)成的監(jiān)控系統(tǒng)。和是相輔相成的。兩者一起,構(gòu)成了作用域的核心功能數(shù)據(jù)變化的響應(yīng)。迭代的最大值稱為。框架設(shè)計第三版,敬請期待 angular的ViewModel有一個專門的官方術(shù)語叫$scope, 它只是一個普通構(gòu)造器(Scope)的實例。換言之,它是一個普通的JS對象。為了實現(xiàn)MVVM框架通常宣傳的那種改變數(shù)據(jù)即改變視圖的魔幻效果,它得裝備上更多更...
摘要:棧是操作系統(tǒng)在建立某個進程時或者線程為這個線程建立的存儲區(qū)域。在具有多線程機制的操作系統(tǒng)中,處理機調(diào)度的基本單位不是進程而是線程。一個進程可以有多個線程,而且至少有一個可執(zhí)行線程。 WEB前端面試題的記錄(二) 1、一次完整的HTTP事務(wù)是怎樣的一個過程:基本流程: 域名解析 發(fā)起TCP的3次握手 建立TCP連接后發(fā)起http請求 服務(wù)器端響應(yīng)http請求,瀏覽器得到html代碼 瀏...
摘要:相關(guān)組件版本最近,在公司的項目中,要開發(fā)一個使用瀑布流的前臺,衡量了各種解決方案后,還是覺得最成熟,所以就選用了它。測試的結(jié)果很令人沮喪,依然沒有控制節(jié)點的位置,所以應(yīng)該不是這個問題。 相關(guān)組件版本:avalon 1.3.6、masonry 3.1.5 最近,在公司的項目中,要開發(fā)一個使用瀑布流的前臺,衡量了各種解決方案后,還是覺得masonry最成熟,所以就選用了它。而在之前開發(fā)后臺...
摘要:給人印象中,樹組件是非常令人畏懼的一個組件,超級復(fù)雜,超級難寫。但使用來做,這卻是級其簡單的一件事。換言之,我們用元素作為樹的節(jié)點,那么樹組件內(nèi)部也需要存在樹組件,需要形成遞歸結(jié)構(gòu)。的機制又是出場的時候了。 給人印象中,樹組件是非常令人畏懼的一個組件,超級復(fù)雜,超級難寫。但使用avalon2來做,這卻是級其簡單的一件事。首先從樣式入做,無序列表是天然可用的樹結(jié)構(gòu),幾個UL元素套在一起,...
閱讀 3511·2023-04-25 14:57
閱讀 2560·2021-11-22 14:56
閱讀 2079·2021-09-29 09:45
閱讀 1761·2021-09-22 15:53
閱讀 3313·2021-08-25 09:41
閱讀 896·2019-08-29 15:22
閱讀 3289·2019-08-29 13:22
閱讀 3122·2019-08-29 13:08