In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly explains "how to achieve a vue two-way binding". The content of the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "how to achieve a vue two-way binding".
Start
A picture of the beginning
It can be seen from the picture that new Vue () is divided into two steps.
The agent listens for all data, associates it with Dep, and notifies subscribers of view updates through Dep. [related recommendation: vuejs video tutorial]
Parse all the templates, subscribe the data used in the template, and bind an update function. When the data changes, Dep informs the subscriber to execute the update function.
The next step is to analyze how to implement it and what you need to write. Let's take a look at the basic code of vue. Let's analyze it from scratch.
{{message}}
Let app = new Vue ({el: "# app", data: {message: "Test this is a content"}})
From the above code, we can see the operation of new Vue, which carries el and data attributes, which is the most basic attribute. In html code, we know that it is the template root node of vue rendering, so vue needs to implement a template parsing method Compile class to render the page. The parsing method also needs to deal with {{}} and v-model instructions. In addition to parsing the template, we also need to implement the data proxy, that is, the Observer class.
Implement the Vue class
As shown in the following code, this completes the Vue class, which is simple enough. If you are not familiar with the class keyword, it is recommended to learn it first. We may see from the following that two classes are instantiated here, one is the class for proxy data, and the other is the class for parsing the template.
Class Vue {constructor (options) {/ / proxy data new Observer (options.data) / / bind data this.data = options.data / / parse template new Compile (options.el, this)}}
Then let's write a Compile class to parse the template, and then we'll take a look at what the parsing template does.
It is impossible for us to parse the template directly to dom, so we need to create a document fragment (virtual dom), and then copy a copy of the template DOM node into the virtual DOM node. After parsing the virtual DOM node, we replace the virtual DOM node with the original DOM node.
After the virtual node is copied, we need to traverse the whole node tree for parsing. During the parsing process, we will traverse the atrr attribute of DOM to find the instructions related to Vue. In addition, we also have to parse the contents of the textContent node to determine whether there are double curly braces.
Make a subscription to the properties used in the parsing
The implementation template parses the Compile class
Next, we will gradually realize
Build the Compile class, first get the static node and Vue instance, and then define a property of the virtual dom to store the virtual dom
Class Compile {constructor (el, vm) {/ / get static node this.el = document.querySelector (el); / / vue instance this.vm = vm / / Virtual dom this.fragment = null / / initialization method this.init ()}}
Implement the initialization method init (), which is mainly used to create a virtual dom and call the parsing template method, and then replace the DOM node into the page after the parsing is completed.
Class Compile {/ /... Omit the other code init () {/ / create a new blank document fragment (virtual dom) this.fragment = document.createDocumentFragment () / / traverse all child nodes to add Array.from (this.el.children) .forEach (child = > {this.fragment.appendChild (child)}) / / parse template this.parseTemplate (this.fragment) to the virtual dom. / / add the parsing to the page this.el.appendChild (this.fragment) }}
The implementation of parsing template method parseTemplate is mainly traversing all the child nodes in the virtual DOM and parsing, and different processing is carried out according to the type of child nodes.
Class Compile {/ /... Omit other code / / parse template parseTemplate (fragment) {/ / get child node let childNodes = fragment.childNodes of virtual DOM | | [] / / traversal node childNodes.forEach ((node) = > {/ / match curly braces regular expression var reg = /\ {(. *)\}\} /; / get node text var text = node.textContent If (this.isElementNode (node)) {/ / determines whether it is a html element / / parses html element this.parseHtml (node)} else if (this.isTextNode (node) & & reg.test (text)) {/ / determines whether it is a text node with double curly braces / parsing text this.parseText (node) Reg.exec (text) [1])} / / Recursive parsing If there are any child elements, continue parsing if (node.childNodes & & node.childNodes.length! = 0) {this.parseTemplate (node)}}) }}
According to the above code, we come to the conclusion that we need to implement two simple judgments, that is, to determine whether it is a html element and a text element. Here, we can distinguish it by obtaining the value of nodeType. If you do not understand, you can directly take a look at the portal: Node.nodeType. Here we also extend an isVueTag method to be used in the following code.
Class Compile {/ /... Omit other codes / / determine whether to carry v-isVueTag (attrName) {return attrName.indexOf ("v -") = = 0} / / determine whether it is a html element isElementNode (node) {return node.nodeType = = 1;} / / determine whether it is a text element isTextNode (node) {return node.nodeType = = 3;}}
Implement the parseHtml method, and parse the html code mainly by traversing the attr attribute on the html element
Class Compile {/ /... Omit other code / / parse html parseHtml (node) {/ / get element attribute collection let nodeAttrs = node.attributes | | [] / / element attribute collection is not an array, so here you need to turn it into an array and then traverse Array.from (nodeAttrs). ForEach ((attr) = > {/ / get attribute name let arrtName = attr.name / / determine whether the name has a v-if (this.isVueTag (arrtName)) {/ / get the attribute value let exp = attr.value; / / the string let tag = arrtName.substring (2) after cutting v- If (tag = = "model") {/ / v-model instruction processing method this.modelCommand (node, exp, tag)});}}
To implement the modelCommand method, in the template parsing phase, we just bind the value of data in the vue instance to the element and implement to listen for the input method to update the data.
Class Compile {/ /... Omit other code / / process model instruction modelCommand (node, exp) {/ / get data let val = this.vm.data [exp] / / bind data node.value = val when parsing | | "/ / listen for input event node.addEventListener (" input ", (event) = > {let newVlaue = event.target.value) If (val! = newVlaue) {/ / update data data this.vm.data [exp] = newVlaue / / update closure data to avoid two-way binding failure val = newVlaue}})}}
It is relatively simple to deal with Text elements, mainly by replacing the textContent content in the element with data.
Class Compile {/ /... Omit other code / / parse text parseText (node, exp) {let val = this.vm.data [exp] / / parse update text node.textContent = val | | "}}
So far, the preliminary writing of the Compile class has been completed. The test results are as follows, and the template can be parsed normally.
Here is the part of the flow chart that we have implemented so far.
Pothole one:
In point 6, the modelCommand method does not implement two-way binding, but only one-way binding. Subsequent two-way binding timing still needs to be processed.
Pit two:
Point 7 the code above in the parseText method does not subscribe to the changes to the data, so the data will only be bound once during template parsing
Implement the data proxy Observer class
This is mainly used to proxy all the data in data. An Object.defineProperty method will be used here. If you don't understand this method, take a look at the document portal:
Documentation:
Https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
The Observer class is mainly a method that recursively traverses all the properties in data and then proxies the data.
Three parameters data, key, and val are passed in defineReactive
Both data and key are parameters to Object.defineProperty, while val uses it as a closure variable for Object.defineProperty to use
/ / listener class Observer {constructor (data) {this.observe (data)} / / Recursive method observe (data) {/ / determines that if the data is empty and not of object type, it returns the empty string if (! data | | typeof data! = "object") {return ""} else {/ traverse data for data proxy Object.keys (data). ForEach (key = > {this.defineReactive (data) Key, data [key])}} / / proxy method defineReactive (data, key, val) {/ / Recursive subattribute this.observe (data [key]) Object.defineProperty (data, key, {configurable: true, / / Configurable attribute enumerable: true, / / traverable attribute get () {return val} Set (newValue) {val = newValue}})}
Let's test whether the data proxy has been successfully implemented, and output the data in the constructor of Vue
Class Vue {constructor (options) {/ / proxy data new Observer (options.data) console.log (options.data) / bound data this.data = options.data / / parsing template new Compile (options.el, this)}}
As a result, we can see that the data proxy has been implemented.
The corresponding flow chart is as follows
Pit point three:
Although the data agent is implemented here, according to the figure, the manager needs to be introduced to notify the manager that the data has changed when the data changes, and then the manager notifies the subscriber to update the view, which will be discussed in the subsequent filling process.
Implementation Manager Dep class
Above we have implemented template parsing to initialization view, as well as data proxies. The Dep class to be implemented below is mainly used to manage subscribers and notify subscribers. Here, an array is used to record each subscriber, and the class also gives a notify method to call the subscriber's update method to notify the subscriber to update. A target property is also defined to store temporary subscribers for use when joining the manager.
Class Dep {constructor () {/ / record subscriber this.subList = []} / / add subscriber addSub (sub) {/ / determine whether it exists first Prevent repeated addition of subscribers if (this.subList.indexOf (sub) = =-1) {this.subList.push (sub)}} / / notify subscribers of notify () {this.subList.forEach (item = > {item.update () / / subscribers perform updates, where item is a subscriber and update is the method provided by subscribers)}} / / Dep global attribute Using temporary storage of subscribers Dep.target = null
After the manager implementation is complete, we also implement the following parts of the flowchart. Pay attention to the following points
Observer notifies Dep mainly by calling the notify method
Dep notifies Watcher mainly by calling the update method in the Watcher class.
Implement the subscriber Watcher class
The subscriber code is relatively small, but it is a bit difficult to understand. Two methods are implemented in the Watcher class, one is the update update view method, and the other is the putIn method (I have read several articles that are defined as get methods, probably because I don't understand them well enough).
Update: mainly calls the incoming cb method body, which is used to update page data
PutIn: mainly used to manually add to the Dep manager.
/ / subscriber class Watcher {/ / vm:vue instance itself / / exp: attribute name of proxy data / / cb: what needs to be done when updating constructor (vm, exp, cb) {this.vm = vm this.exp = exp this.cb = cb this.putIn ()} update () {/ / call cb method body Change the this to point and pass in the latest data as the parameter this.cb.call (this.vm This.vm.data [this.exp])} putIn () {/ / bind the subscriber itself to the target global property of Dep Dep.target = this / / call the method of getting data to add the subscriber to the manager let val = this.vm.data [this.exp] / / clear the global property Dep.target = null}}
Pothole 4:
The putIn method in the Watcher class is not added to the manager after the reconstructor call, but binds the subscriber itself to the target global property
Buried pit
We have completed the construction of each class through the above code, as shown in the following figure, but there are still several processes that are problematic, that is, the potholes above. So we have to fill in the hole.
Buried pits 1 and 2
Complete pit one and pit two, add instantiated subscriber code to the modelCommand and parseText methods, and customize the method to be executed when you update. In fact, you can update the value in the page when you update.
ModelCommand (node, exp) {/ /. Omit other code / / instantiate subscribers and update the value of node new Watcher (this.vm, exp, (value) = > {node.value = value})} parseText (node, exp) {/ /. Omit other code / / instantiate subscribers and update the text content new Watcher (this.vm, exp, (value) = > {node.textContent = value})} directly when updating.
Buried pit 3
The completion of pit 3 is mainly to introduce the manager and notify the manager of the change, mainly by calling the dep.notify () method in the Object.defineProperty set method.
/ / listening method defineReactive (data, key, val) {/ / instantiate Manager-add this line let dep = new Dep () / /. Omit the other code set (newValue) {val = newValue / / notify the manager to change-add this line dep.notify ()}
Buried pit 4
Complete the pit four, the main four will add subscribers to the manager
DefineReactive (data, key, val) {/ /. Omit the other code get () {/ / add the subscriber to the manager-add this paragraph if (Dep.target) {dep.addSub (Dep.target)} return val}, / /. Omit other code}
After completing the pit four, there may be handsome boys wondering, how did you join here? what is Dep.target? we might as well look at the code from the beginning and combine it with the following picture.
Now that we have implemented a simple two-way binding, let's test it.
Thank you for reading, the above is the content of "how to achieve a vue two-way binding". After the study of this article, I believe you have a deeper understanding of how to achieve a two-way vue binding, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.