Introduction to VUE source code analysis Vue is a new luxury in the MVVM framework. If I remember correctly, the author should have graduated soon and now at google. As mentioned by the author, vue has been influenced by many big-name frameworks such as knockout and angularjs in api design. However, the author believes that vue has advantages in performance and ease of use. At the same time, I also made a comparison with the performance of other frameworks, here. Today, the Vue portal for version 0.10.4 is straightforward: 1var demo = new Vue ({el: '# demo', data: {message: 'Hello Vue. js! '}) Unlike ko and aveon, vue must specify el at the beginning. I personally think the design here is not very reasonable, because if a piece of data is to be bound to two different dom nodes, You have to specify an ancestor dom node that simultaneously contains the two dom nodes. Next, find the Vue definition. Open the source code and use grunt for vue. The build command uses the gulp-component written by the author to combine code snippets. Please take a look at the details here. From/src/main. js, we can see that the definition of Vue is the definition of ViewModal. Open ViewModel and find that its definition only instantiates a Compiler and passes itself as a parameter to the constructor. At the same time, we can see that some methods are defined on the ViewModel prototype, basically related to internal events and dom operations. Next we will mainly look at this compiler. Don't forget that our first goal is to find out the main principle of its duplex binding. The Duplex binding is switched to the compiler definition, and the code is too long. I hesitated and decided to delete some comments and paste them out, because most of the comments worth reading are here. I hope readers can read the source files in depth. Function Compiler (vm, options) {var compiler = this, key, I compiler. init = true compiler. destroyed = false options = compiler. options = options | {} utils. processOptions (options) extend (compiler, options. compilerOptions) compiler. repeat = compiler. repeat | false compiler. expCache = compiler. expCache | {} var el = compiler. el = compiler. setupElement (options) utils. log ('\ nnew VM instance:' + El. tagName + '\ n') compiler. vm = el. vue_vm = vm compiler. bindings = utils. hash () compiler. dirs = [] compiler. deferred = [] compiler. computed = [] compiler. children = [] compiler. emitter = new Emitter (vm) if (options. methods) {for (key in options. methods) {compiler. createBinding (key)} if (options. computed) {for (key in options. computed) {compiler. createBinding (key) }}// VM ---------------- ----------------------------------------------------- Vm. $ ={} vm. $ el = el vm. $ options = options vm. $ compiler = compiler vm. $ event = null var parentVM = options. parent if (parentVM) {compiler. parent = parentVM. $ compiler parentVM. $ compiler. children. push (compiler) vm. $ parent = parentVM} vm. $ root = getRoot (compiler ). vm // DATA --------------------------------------------------------------------- com Piler. setupObserver () var data = compiler. data = options. data | |{}, defaultData = options. defaultData if (defaultData) {for (key in defaultData) {if (! HasOwn. call (data, key) {data [key] = defaultData [key] }}var params = options. paramAttributes if (params) {I = params. length while (I --) {data [params [I] = utils. checkNumber (compiler. eval (el. getAttribute (params [I])} extend (vm, data) vm. $ data = data compiler.exe cHook ('created ') data = compiler. data = vm. $ data var vmProp for (key in vm) {vmProp = vm [key] if (key. charAt (0 )! = '$' & Data [key]! = VmProp & typeof vmProp! = 'Function') {data [key] = vmProp} compiler. observeData (data) // COMPILE ------------------------------------------------------------------ if (options. template) {this. resolveContent ()} while (I --) {compiler. bindDirective (compiler. deferred [I])} compiler. deferred = null if (this. computed. length) {DepsParser. parse (this. computed)} compiler. init = false compiler.exe cHook ('ready')} comments have been written in co Mpiler instantiation is divided into four stages. The first stage is some basic settings. Two important points: Define a vm attribute in compiler to save the reference to the passed ViewModel; and Call createBinding for each member of method and computed. Jump to createBinding: CompilerProto. createBinding = function (key, directive) {/* omitted */var compiler = this, methods = compiler. options. methods, isExp = directive & directive. isExp, isFn = (directive & directive. isFn) | (methods & methods [key]), bindings = compiler. bindings, computed = compiler. options. computed, binding = new Binding (compiler, key, isExp, isFn) if (isExp) {/* omitted */} else if (isFn ){ Bindings [key] = binding. value = compiler. vm [key] = methods [key]} else {bindings [key] = binding if (binding. root) {/* omitted */if (computed & computed [key]) {// computed property compiler. defineComputed (key, binding, computed [key])} else if (key. charAt (0 )! = '$') {/* Omitting */} else if (computed & computed [utils. baseKey (key)]) {/* omitted */} else {/* omitted */} return binding} It does two things: one is to instantiate something called Bingding, and the other is to reprocess the bingding of method and computed members. With intuition and the code we have seen before, we can boldly guess that the instantiated bingding is probably used to save data and the corresponding "Update callback function" set. Click in/src/binding. Indeed, the update, pub, and other functions, as well as sub, dir and other object members, prove that they are right. By now, there are already a lot of instantiated objects. There will be more in the future. To keep everyone away, please take a look at this key object diagram in advance: After Reading bingding, we will continue to return to createBinding, I also mentioned that I have done some further processing on The bingding of method and computed members. For method, a reference with the same name is directly added to the vm. we can regard the vm as a public carrier. The reference above is equivalent to making itself public. For computed members, defineComputed defines attributes of the same name on the vm and maps getter/setter to $ get and $ set of the corresponding computed member. So far, the first part of compiler has been completed, basically setting up the data shelf. We can see the pub and sub of bingding and know that vue is also in the observe mode. Next we will look at how it compiles the view into a data update function and registers it in bingding. Back to compiler, the second part handles the vm and adds some references. The third part is the key. At first glance, we know that the most important thing is the first sentence compiler. setupObserver () and the last sentence compiler. observeData (data ). The readers who directly read the source code can see clearly in the comments. The first sentence is used to register some internal events. The last sentence is used to convert data members into getter/setter. And bind it with The bingding we just mentioned. It is worth noting that if the data members are objects or arrays, vue recursively converts them into getter/setter, so it does not matter how deep you are nested, it doesn't matter if you replace these Members directly. It re-recursively converts the new object. The code here is easy to understand. You can click it on your own. I just want to say that vue uses a lot of event schedulers in its internal implementation, that is,/src/emitter. For example, set operations on data. When the set function only triggers a set event, all view update functions that follow this event are registered. This small design decouples key modules and allows independent tests. It also provides a lot of space for the expansion of the framework itself. The following figure shows the internal event distribution when the data member is modified: view rendering and expansion see the last part of view rendering. It is worth noting that, vue supports angular-style reusable ve. The specific implementation of directive is not much different from the previous ko, and it is a declaration of functions such as bind and update. In terms of expansion, vue has a clear concept of component and plugin, which is easy to understand. You can simply look at the document. In addition, note that vue only processes the dependency between computed and common data at the end. In summary, vue is very sophisticated in the kernel architecture. The essence is not to implement some powerful but complex data structures like ko, but to implement what is needed. It refers to the Code architecture that fully implements functions and decouples as much as possible, providing a lot of space for expansion. For example, it uses an intermediate such as binding, rather than registering the UPDATE function directly to the set function of data. These designs are worth learning. Of course, we also see some objections: for example, do you want to clearly divide data conversion and view compilation into two processes? This makes it easy to reuse data, that is, the first question. In this way, compiler code instantiation can be slightly more elegant: First, data and dependencies are processed, then bingding is established, various events are bound, and finally views are processed.