Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to implement two-way binding in vuejs

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article will explain in detail how to achieve two-way binding of vuejs. The editor thinks it is very practical, so I share it with you as a reference. I hope you can get something after reading this article.

The principle of two-way binding of vuejs: using data hijacking and publishing subscription mode, hijacking the setter and getter of each attribute through "Object.defineProperty ()", publishing messages to subscribers when the data changes, triggering the corresponding monitoring callback, and then updating the view.

The operating environment of this tutorial: windows7 system, vue2.9.6 version, DELL G3 computer.

Principle of two-way binding of Vue data

Vue mainly uses data hijacking and publish subscription mode, data hijacking using Object.defineProperty () method, and then notifies the publisher (topic object) to notify all observers. After receiving the notification, the observer will update the view.

Https://jsrun.net/RMIKp/embedded/all/light

MVVM framework mainly includes two aspects: data change update view, view change update data.

View changes update the data. If it is a tag like input, you can use the oninput event..

The data change update view can use the set method of Object.definProperty () to detect data changes, trigger this function when the data changes, and then update the view.

Realization process

Now that we know how to implement two-way binding, we need to hijack and listen for data first, so we need to set up an Observer function to listen for changes in all properties.

If the properties change, tell the subscriber watcher to see if the data needs to be updated, and if there are multiple subscribers, you need a Dep to collect those subscribers, and then manage them uniformly between the listeners observer and watcher.

You also need an instruction parser compile to scan and parse the nodes and attributes you need to listen on.

So, the process goes something like this:

Implement a listener Observer that hijacks and listens to all properties and notifies the subscriber if there is a change.

Implement a subscriber Watcher, execute the corresponding function when notified of a property change, then update the view, and use Dep to collect the Watcher.

Implement a parser Compile, which is used to scan and parse the relevant instructions of the node, and initialize the corresponding subscriber according to the initialization template.

Show an Observer

Observer is a data listener. The core method is to use Object.defineProperty () to add setter and getter methods to all attributes recursively.

Var library = {book1: {name: ",}, book2:",}; observe (library); library.book1.name = "vue authoritative Guide"; / / attribute name has been monitored. Now the value is: "vue authoritative Guide" library.book2 = "there is no such book" / / attribute book2 has been listened, and now the value is: "No this book" / / add detection function defineReactive (data, key, val) {observe (val) for data; / / Recursively traverse all sub-attributes let dep = new Dep () / / create a new dep Object.defineProperty (data, key, {enumerable: true, configurable: true, get: function () {if (Dep.target) {/ / determine whether you need to add a subscriber, only the first time you need to add a subscriber, and then you don't need it. See the Watcher function dep.addSub (Dep.target); / / add a subscriber} return val }, set: function (newVal) {if (val = = newVal) return; / / if the value has not changed, return val = newVal; console.log ("attribute" + key + "has been monitored, now the value is:" + newVal.toString () + ""); dep.notify (); / / notify all subscribers if the data changes. },});} / all properties of the listening object function observe (data) {if (! data | | typeof data! = = "object") {return; / / if it is not an object, return} Object.keys (data) .forEach (function (key) {defineReactive (data, key, data [key]);});} / Dep is responsible for collecting subscribers and triggering the update function when the attribute changes. Function Dep () {this.subs = {};} Dep.prototype = {addSub: function (sub) {this.subs.push (sub);}, notify: function () {this.subs.forEach ((sub) = > sub.update ());},}

In the idea analysis, we need a message subscriber Dep that can hold the subscriber, which is used to collect the subscriber and execute the corresponding update function when the property changes.

From a code point of view, the subscriber Dep is added to the getter so that it can be triggered when the Watcher is initialized, so you need to determine whether a subscriber is needed.

In setter, if any data changes, all subscribers are notified, and then the subscribers update the corresponding functions.

At this point, a relatively complete Observer is completed, and then we begin to design Watcher.

Implement Watcher

The subscriber Watcher needs to add itself to the subscriber Dep during initialization. We already know that the listener Observer performs the Watcher operation during get, so we only need to trigger the corresponding get function to add the corresponding subscriber action when the Watcher is initialized.

So how to trigger get? Because we have set Object.defineProperty (), we only need to get the corresponding property value to trigger it.

We just need to cache the subscriber on the Dep.target when the subscriber Watcher is initialized, and delete it after it has been successfully added.

Function Watcher (vm, exp, cb) {this.cb = cb; this.vm = vm; this.exp = exp; this.value = this.get (); / / add yourself to the subscriber} Watcher.prototype = {update: function () {this.run ();}, run: function () {var value = this.vm.data [this.exp]; var oldVal = this.value If (value! = = oldVal) {this.value = value; this.cb.call (this.vm, value, oldVal);}}, get: function () {Dep.target = this; / / cache yourself to determine whether to add watcher. Var value = this.vm.data [this.exp]; / / enforce the get function in the listener Dep.target = null; / / release your return value;},}

At this point, the simple Watcher is designed, and then the Observer and Watcher are associated to achieve a simple two-way binding.

Because the parser Compile has not yet been designed, you can write the template data to death first.

Convert the code to the ES6 constructor and try previewing it.

Https://jsrun.net/8SIKp/embedded/all/light

Because this code does not implement the compiler but directly passes in the bound variables, we only set a data (name) on a node to bind, and then new MyVue on the page, we can achieve two-way binding.

And two seconds later it is worth changing, and you can see that the page has also changed.

/ / MyVueproxyKeys (key) {var self = this; Object.defineProperty (this, key, {enumerable: false, configurable: true, get: function proxyGetter () {return self.data [key];}, set: function proxySetter (newVal) {self.data [key] = newVal;}});}

The purpose of the above code is to proxy the key of this.data to this, so that I can easily use this.xx to get this.data.xx.

Implement Compile

Although the above implements two-way data binding, the whole process does not parse the DOM store, but a fixed replacement, so the next step is to implement a parser to do the data parsing and binding work.

The implementation steps of the parser compile:

Parse the template instructions, replace the template data, and initialize the view.

Bind the corresponding update function to the corresponding node of the template and initialize the corresponding subscriber.

In order to parse the template, we first need to parse the DOM data, and then process the corresponding instructions on the DOM elements, so the whole DOM operation is more frequent, so we can create a new fragment fragment and store the parsed DOM into the fragment fragment for processing.

Function nodeToFragment (el) {var fragment = document.createDocumentFragment (); var child = el.firstChild; while (child) {/ / move Dom elements into fragment fragment.appendChild (child); child = el.firstChild;} return fragment;}

Next, you need to traverse each node and do special processing for the nodes with relevant instructions and template syntax, first with the simplest template syntax processing, using regular parsing syntax in the form of "{{variable}}".

Function compileElement (el) {var childNodes = el.childNodes; var self = this; [] .slice.call (childNodes) .forEach (function (node) {var reg = /\ {(. *)\} /; / match {{xx}} var text = node.textContent If (self.isTextNode (node) & & reg.test (text)) {/ / determine whether it conforms to the instruction self.compileText (node, reg.exec (text) [1]) of this form {{}};} if (node.childNodes & & node.childNodes.length) {self.compileElement (node); / / continue recursively traversing child nodes}) }, function compileText (node, exp) {var self = this; var initText = this.vm [exp]; updateText (node, initText); / / initialize the initialized data into the view new Watcher (this.vm, exp, function (value) {/ / generate the subscriber and bind the update function self.updateText (node, value);}) }, function updateText (node, value) {node.textContent = typeof value = = 'undefined'?': value;}

After getting the outermost node, call the compileElement function to judge all the child nodes. If the node is a text node tangent matching the instruction in the form of {{}}, then compile and initialize the corresponding parameters.

Then you need to generate a corresponding update function subscriber for the current parameter and update the corresponding DOM when the data changes.

In this way, the three processes of parsing, initialization and compilation are completed.

Next, a myVue can be modified to use template variables for two-way data binding.

Https://jsrun.net/K4IKp/embedded/all/light

Add a resolution event

After adding compile, a bidirectional data binding is almost complete, and the next step is to add more instructions to Compile to parse and compile, such as v-model, v-on, v-bind, and so on.

Add a v-model and v-on resolution:

Function compile (node) {var nodeAttrs = node.attributes; var self = this; Array.prototype.forEach.call (nodeAttrs, function (attr) {var attrName = attr.name; if (isDirective (attrName)) {var exp = attr.value; var dir = attrName.substring (2); if (isEventDirective (dir)) {/ / event instructions self.compileEvent (node, self.vm, exp, dir) } else {/ / v-model instruction self.compileModel (node, self.vm, exp, dir);} node.removeAttribute (attrName); / / finish parsing, remove attributes}});} / / v-instruction parsing function isDirective (attr) {return attr.indexOf ("v -") = = 0;} / / on: instruction parsing function isEventDirective (dir) {return dir.indexOf ("on:") = 0;}

The above compile function is used to traverse all node attributes of the current dom, and then determine whether the attribute is an instruction attribute, and if you are doing the corresponding processing (event to listen for events, data to listen for data..)

Full version of myVue

Add the mounted method to the MyVue, which is executed when all operations are done.

Class MyVue {constructor (options) {var self = this; this.data = options.data; this.methods = options.methods; Object.keys (this.data) .forEach (function (key) {self.proxyKeys (key);}); observe (this.data); new Compile (options.el, this); options.mounted.call (this) / / execute the mounted function} proxyKeys (key) {/ / proxy the this.data attribute to the this var self = this; Object.defineProperty (this, key, {enumerable: false, configurable: true, get: function getter () {return self.data [key];}, set: function setter (newVal) {self.data [key] = newVal) },});}}

And then it can be tested and used.

Https://jsrun.net/Y4IKp/embedded/all/light

Summarize the process and look back at this picture. Is it much clearer?

This is the end of the article on "how to achieve two-way binding in vuejs". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.

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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report