vue bidirectional data binding principle
1, Principle
- vue bidirectional data binding is realized through data hijacking combined with the mode of publishing subscribers, that is, when the data and view are synchronized, the data changes. If you try to change, the data also changes
- Core: the core of vue data binding is object Defineproperty() method
- Introduce object Defineproperty() method
(1)Object.defineProperty(obj, prop, descriptor). There are three parameters in this syntax: Obj (object on which the property is to be defined), prop (property to be defined or modified) descriptor (specific change method)
(2) In short, this method is used to define a value. When calling, we use the get method in it. When we assign a value to this property, we use the set method in it
var obj = {} Object.defineProperty(obj,'hello',{ get:function(){ console.log('Called get method') }, set:function(newVal){ console.log('Called set method,The value of the method is'+ newVal) } }); obj.hello;//'get method called' obj.hello='hi';//'the set method is called, and the value of the method is hi '
2, First, simply implement a js two-way data binding to familiarize yourself with this method:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="app"> <input type="text" id="a"> <span id="b"></span> </div> </body> <script> var obj = {}; //Define an empty object var val = 'zhao'; //Give initial value Object.defineProperty(obj, 'val', {//Define the properties of the object to modify get: function () { return val; }, set: function (newVal) { val = newVal;//Definition val is equal to the modified content document.getElementById('a').value = val;//Make the content of the text box val document.getElementById('b').innerHTML = val;//Let the inner val of span } }); document.addEventListener('keyup', function (e) {//When entering content in the text box, make the val defined in the object equal to the value of the text box obj.val = e.target.value; }) </script> </html>
In this way, we can realize the two-way data binding of js and have a preliminary understanding of this method;
The effect of this example is: with the change of input text in the text box, the same text content will be displayed synchronously in span; The two-way binding of view = > and view = > model is realized.
The set method is triggered by adding an event listener keyup. While set modifies the accessor properties, it also modifies the dom style and changes the text in the span tag.
3, The principle of realizing a real two-way binding
1. Achieve results
Let's think about the direction of vue data binding first. OK
//Implement vue <div id="app"> <input type="text" v-model="text"> {{text}} </div> <script> var vm = new Vue({ el:'app', data:{ text:'hello world' } }) </script>
2. Task splitting
Splitting tasks can make our thinking clearer:
(1) Bind the contents of data in vue to the input text box and text node
(2) When the content of the text box changes, the data in the vue instance also changes
(3) When the content in data changes, the content of input box and text node also changes
3. Start task 1 - binding content
Let's first understand the concept of DocuemntFragment (fragmented document). You can think of it as a dom node container. When you create 10 nodes, when each node is inserted into the document, it will cause a backflow of the browser, that is, the browser will backflow 10 times, which is very resource consuming.
The use of fragmented documents, that is, I put 10 nodes into a container first, and finally I insert the container directly into the document! The browser only reflowed once.
Note: another important feature is that if you use the appendChild method to add a node from the original dom tree to the DocumentFragment, the original node will be deleted.
for instance:
As you can see, my app has two child nodes, an element node and a text node
But when I hijacked the data through DocumentFragment
<div id="app"> <input type="text"> "hello world" </div> <script> var dom = nodeToFragment(document.getElementById('app')) console.log(dom)//Take a look at the fragmented document console.log(document.getElementById('app'))//Take a look at the hijacked app function nodeToFragment(node) { var fragment = document.createDocumentFragment() var child while (child = node.firstChild) { fragment.appendChild(child) } return fragment } </script>
Note: my fragmented document hijacks all the child nodes, and there is no content in my div with id app.
At the same time, I should mainly judge the conditions of while. Judge whether there are child nodes, because every time I hijack the first child node in the node, there will be one less node in the node. Until there is no child, the child will become undefined and the cycle will be terminated.
To implement content binding
We need to consider two problems, one is how to bind to the input, the other is how to bind to the text node.
This is the idea. We have obtained all the sub nodes of div in the DocumentFragment, and then process each node to see if there is any content associated with the vm instance. If so, modify the content of this node. Then add it back into the DocumentFragment.
First, we write a function to handle each node. If there is an input bound v-model attribute or a text node with {{xxx}} appears, we will replace the content with the content in the data in the vm instance
Then, when adding nodes to the fragmented document, each node handles it.
Create the instantiation function of Vue:
The renderings are as follows: