knockout.js的學習筆記

來源:互聯網
上載者:User

knockout.js試圖將微軟曆經驗證的成功方案MVVM解決方案引進JS,因此很有必要學習下。MVVM是專門為解決富互動頻變動的介面開發而生,這與web開發非常相似。產經經理與測試與什麼主管,他們看不懂後端的東西,也只能對前端的看得到的東西指手劃腳了,因此變動是非常頻繁的,每次變動,但伴隨著痛若的事件重新綁定與代理,以及與它們相關的業務代碼的調整,在JS這種調試特喝別痛苦的語言中,情況就更嚴重了。每次改版都加劇前端離職的決心,前端換了幾波人才把項目做出來。jQuery號稱是改變人們寫JS的方式,但只是提供了更好的磚瓦而已(原生API是沙石)。想獲得後端那樣開發效率,必須有Struts2, Spring, rails這般一站式的架構,就開發流程進行控制,開發人員只是在架構裡做填空題。這樣,看另一個人的代碼,就知道應該是哪裡開始看起,看完這裡就知道下一步應該是哪裡。易讀性應該由架構來塑造,維護性由架構來提供!

之所以放棄ember.js的研究是因為她對流程的控制太弱了,代碼量一多,還是像狗屎一般的亂!knockout.js雖然有許多不如意的地方,但它對流程的控制是非常好的,只有三個入口。在元素上進行資料繫結,編寫viewModel,將viewModel綁到目標的節點上。簡單明了,其缺點可以在我通讀knockout後再造一個輪子解決!

一般的資料繫結有三種:

One-Time,One-Way,Two-way。

One-Time繫結模式的意思即為從viewModel綁定至UI這一層只進行一次綁定,程式不會繼續追蹤資料的在兩者中任何一方的變化,這種綁定方式很使用於報表資料,資料僅僅會載入一次。

One-Way繫結模式即為單向綁定,即object-UI的綁定,只有當viewModel中資料發生了變化,UI中的資料也將會隨之發生變化,反之不然。

Two-Way繫結模式為雙向繫結,無論資料在Object或者是UI中發生變化,應用程式將會更新另一方,這是最為靈活的綁定方式,同時代價也是最大的。

資料繫結只是作為元素的自訂屬性寫上標籤內,並不能決定它是何種綁定。

viewModel是一個結構非常簡單的hash,鍵名為命令,值的定義方式決定其綁定方式。 如果值是通過ko.observable定義的說明是雙向繫結,否則為One-Time綁定,在knockout不存在單向綁定。knockout2.0還從ember.js借鑒了計算屬性,即用ko.computed定義的,它的值會依賴其他值進行推斷,因此就形成了依賴,需要構築一枚依賴鏈。

最後一步是將viewModel綁到節點上,事實上它還會遍曆其後代,進行綁定。預設是綁定body上,因此使用者的行為只能影響到body裡面的元素與URL。如果頁面非常複雜,建議還是指定具體節點吧。viewModel還可以綁定注釋節點,但有的公司會對頁面進行壓縮,去空白去注釋,因此不太建議使用。

我們先從ko.applyBindings看起吧。

ko.applyBindings(viewModel, rootNode)//這裡會對rootNode進行修正       ↓applyBindingsToNodeAndDescendantsInternal(viewModel, rootNode, true)//第三個參數為強制綁定,會影響shouldApplyBindings變數//如果是UL與OL會對裡面的結構進行修正normaliseVirtualElementDomStructure//通過shouldApplyBindings變數決定是否對此節點進行資料繫結//通過applyBindingsToNodeInternal判定是否繼續綁定到後代中//通過applyBindingsToDescendantsInternal綁定到後代中

applyBindingsToNodeInternal在這裡調用時其參數為rootNode, null, viewModel, true。它是一個極惡的方法,大量使用閉包。精簡如下:

function applyBindingsToNodeInternal (node, bindings, viewModel, force) {    var initPhase = 0; // 0 = before all inits, 1 = during inits, 2 = after all inits    var parsedBindings;    function makeValueAccessor(bindingKey) {        return function () {             return parsedBindings[bindingKey]         }    }    function parsedBindingsAccessor() {        return parsedBindings;//這是一個對象    }    var bindingHandlerThatControlsDescendantBindings;    ko.dependentObservable(function () {/*略*/ },  null,{         'disposeWhenNodeIsRemoved' : node     });    return {        //除了html, template命令,都允許在後代節點繼續綁定        shouldBindDescendants: bindingHandlerThatControlsDescendantBindings === undefined    };};

難度在於dependentObservable的第一個回調

function anonymity() {    var bindingContextInstance = viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)    ? viewModelOrBindingContext    : new ko.bindingContext(ko.utils.unwrapObservable(viewModelOrBindingContext));    //將viewModelOrBindingContext整成bindingContext執行個體    //bindingContext執行個體用$data儲存viewModel    var viewModel = bindingContextInstance['$data'];    //將bindingContextInstance綁定rootNode上    if (bindingContextMayDifferFromDomParentElement)        ko.storedBindingContextForNode(node, bindingContextInstance);    // Use evaluatedBindings if given, otherwise fall back on asking the bindings provider to give us some bindings    var evaluatedBindings = (typeof bindings == "function") ? bindings() : bindings;    //如果bindings不存在,則通過getBindings擷取,getBindings會調用parseBindingsString,變成對象    parsedBindings = evaluatedBindings || ko.bindingProvider['instance']['getBindings'](node, bindingContextInstance);    if (parsedBindings) {        // First run all the inits, so bindings can register for notification on changes        if (initPhase === 0) {            initPhase = 1;            for (var bindingKey in parsedBindings) {                var binding = ko.bindingHandlers[bindingKey];                if (binding && node.nodeType === 8)                    validateThatBindingIsAllowedForVirtualElements(bindingKey);                //注釋節點只能繫結資料流程式控制制命令                if (binding && typeof binding["init"] == "function") {                    var handlerInitFn = binding["init"];                    //在頁中,使用者的操作只能影響到元素的value, checked, selectedIndex, hasFocus, placeholder的變化                    //而更多的變化需要通過綁定事件,通過JS代碼調用實現                    //因此init主要用於綁定事件                    var initResult = handlerInitFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);                    // If this binding handler claims to control descendant bindings, make a note of this                    if (initResult && initResult['controlsDescendantBindings']) {//這裡主要是html,與template命令,只有它們阻止繼續在後代中綁定事件                        if (bindingHandlerThatControlsDescendantBindings !== undefined)                            throw new Error("Multiple bindings (" + bindingHandlerThatControlsDescendantBindings + " and " + bindingKey + ") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");                        bindingHandlerThatControlsDescendantBindings = bindingKey;                    }                }            }            initPhase = 2;        }        if (initPhase === 2) {            for (var bindingKey in parsedBindings) {                var binding = ko.bindingHandlers[bindingKey];                if (binding && typeof binding["update"] == "function") {                    var handlerUpdateFn = binding["update"];//更新UI                    handlerUpdateFn(node, makeValueAccessor(bindingKey), parsedBindingsAccessor, viewModel, bindingContextInstance);                }            }        }    }}

這裡存在一個疑惑,像value, checked, event等命令是要綁定事件的,但很難想象事件的回調是怎麼調用到這匿名函數的。下一節將深入到其發行者訂閱者機制看看。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.