Knockout. js tries to introduce Microsoft's verified mvvm solution to JS, so it is necessary to learn. Mvvm is designed for interface development to solve rich interactive frequency changes, which is very similar to web development. Production Manager and test supervisor, who can't understand the back-end things and can only point out what the front-end sees, so the changes are very frequent, every change, but with the painful events, re-binding and proxy, and services related to themCodeIn the JS debugging language that is especially painful, the situation is even more serious. Each revision adds to the determination of the front-end to resign, and the front-end has changed several waves of talents to implement the project. Jquery is called a method that changes the way people write JavaScript, but it only provides better bricks and tiles (the native API is sha shi ). To achieve the development efficiency of the backend, you must have a one-stop framework such as struts2, spring, and rails to control the development process. Developers only fill in blank questions in the framework. In this way, when you look at another person's code, you will know where it should be and where it should be. Ease of use should be shaped by the framework, and maintainability should be provided by the Framework!
The reason why I gave up research on Ember. JS is that her control of the process is too weak, and the code is too large, it is still like a mess! Although knockout. js has many unsatisfactory aspects, it controls the process very well and has only three entries. Bind the data on the element, write the viewmodel, and bind the viewmodel to the target node. It is simple and clear, and its disadvantages can be solved by recreating a wheel after I read the knockout!
Generally, there are three types of data binding:
One-time, one-way, two-way.
The one-time binding mode means binding only once from viewmodel to UI,ProgramIt does not continue to track data changes on either side of the two. This binding method is used for report data and data is loaded only once.
The one-way binding mode is unidirectional binding, that is, object-UI binding. Only when the data in the viewmodel changes, the data in the UI will also change, otherwise.
The two-way binding mode is bidirectional binding. No matter the data changes in the object or UI, the application updates the other party. This is the most flexible binding method and the most costly one.
Data Binding is only written into the tag as the custom attribute of an element, and cannot decide what type of binding it is.
Viewmodel is a simple hash structure. The key name is a command, and the value definition method determines the binding method. If the value is defined by KO. observable, It is bidirectional binding. Otherwise, it is one-time binding and there is no one-way binding in knockout. Also from Ember. JS uses Ko. computed defines that the value of computed depends on other values for inference. Therefore, dependency is formed and a dependency chain needs to be constructed.
The last step is to bind the viewmodel to the node. In fact, it will traverse its descendants and bind it. By default, it is bound to the body. Therefore, user behavior can only affect the elements and URLs in the body. If the page is complex, we recommend that you specify a specific node. Viewmodel can also be bound to a comment node, but some companies will compress the page and comment it out in blank spaces. Therefore, it is not recommended.
Let's start with Ko. applybindings.
Ko. applybindings (viewmodel, rootnode) // The rootnode is modified here. applybindingstonodeanddescendantsinternal (viewmodel, rootnode, true) // The third parameter is forced binding, it will affect the shouldapplybindings variable // if it is UL and OL, it will modify the structure in normalisevirtualelementdomstructure // The shouldapplybindings variable determines whether to bind data to the node // use applybindingstonodeinternal in future generations // bind to future generations through applybindingstodescendantsinternal
When applybindingstonodeinternal is called here, its parameter is rootnode, null, viewmodel, true. It is a very bad method, and a lot of closures are used. Simplified as follows:
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; // This is an object} var variable; Ko. dependentobservable (function () {/* slightly */}, null, {'disposewhennodeisremoved ': node}); Return {// besides the HTML and template commands, you can bind shouldbinddescendants to future nodes: bindinghandlerthatcontrolsdescendantbindings ==== undefined };};
The difficulty lies in the first callback of dependentobservable.
Function anonymity () {var bindingcontextinstance = viewmodelorbindingcontext & (viewmodelorbindingcontext instanceof KO. bindingcontext )? Viewmodelorbindingcontext: New Ko. bindingcontext (Ko. utils. unwrapobservable (viewmodelorbindingcontext); // convert viewmodelorbindingcontext into a bindingcontext instance // use $ data to save viewmodel var viewmodel = bindingcontextinstance ['$ data']; // bindingcontextinstance bound to the rootnode if (bindingcontextmaydifferfromdomparentelement) Ko. storedbindingcontextfornode (node, bindingcontextinstance); // use evaluate Dbindings if given, otherwise fall back on asking the bindings provider to give us some bindings var evaluatedbindings = (typeof bindings = "function ")? Bindings (): bindings; // If bindings does not exist, get it through getbindings. getbindings calls parsebindingsstring to convert it to object 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 bind Ing = Ko. bindinghandlers [bindingkey]; If (binding & node. nodetype = 8) validatethatbindingisallowedforvirtualelements (bindingkey); // The comment node can only bind the process control command if (binding & typeof binding ["init"] = "function ") {var handlerinitfn = binding ["init"]; // on the page, user operations can only affect the value, checked, selectedindex, hasfocus, placeholder changes // more changes need to be implemented by binding events and calling through JS Code. // Therefore, init is mainly used to bind event 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 ']) {// here it is mainly HTML, and template commands, only the 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"]; // update UI handlerupdatefn (node, makevalueaccessor (bindingkey), parsedbindingsaccessor, viewmodel, bindingcontextinstance );}}}}}
there is a doubt that commands such as value, checked, and event are bound to events, but it is hard to imagine how the Event Callback calls the anonymous function. The next section will go deep into its publisher subscriber mechanism.