In-depth responsive principle
We have already talked about most of the basic content, and now we will talk about the underlying content. One of the most significant features of Vue.js is the response system - the model is just a normal object, and modifying it will update the view. This makes state management very simple and intuitive, but understanding its principles is also important and can avoid some common problems. Next, we will start digging deep into the underlying details of the Vue.js response system.
How to track changes
Passing a normal object to the Vue instance as its data option, Vue.js will traverse its properties and convert them to getter/setter with Object.defineProperty. This is an ES5 feature and cannot be implemented with patches. This is why Vue.js does not support IE8 and lower.
Users cannot see getters/setters, but internally they let Vue.js track dependencies, notifying changes when properties are accessed and modified. One problem is that getter/setter formatting is different when printing data objects on the browser console, and using the vm.$log() instance method can get more friendly output.
Each instruction/data binding in the template has a corresponding watcher object, which records the attributes as dependencies during the calculation process. After that, when the dependent setter is called, the watcher recalculation will be triggered, which will also cause its associated instructions to update the DOM.
Change detection problem
Due to ES5 restrictions, Vue.js cannot detect the addition or removal of object properties. Because Vue.js converts the property to getter/setter when initializing the instance, the property must be on the data object to allow Vue.js to convert it to make it responsive. For example:
var data = { a: 1 }var vm = new Vue({ data: data})// `vm.a` and `data.a` are now responsive vm.b = 2// `vm.b` is not responsive data.b = 2// `data.b` is not responsiveHowever, there is a way to add attributes after instance creation and make it responsive.
For Vue instances, you can use the $set(key, value) instance method:
vm.$set('b', 2)// `vm.b` and `data.b` are now responsiveFor normal data objects, the global method Vue.set(object, key, value):
Vue.set(data, 'c', 3)// `vm.c` and `data.c` are now responsive
Sometimes you want to add some properties to an existing object, such as using Object.assign() or _.extend() to add properties. However, new properties added to the object do not trigger updates. At this time, you can create a new object, including the properties of the original object and the new properties:
// Don't use `Object.assign(this.someObject, { a: 1, b: 2 })`this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })There are also some array-related issues that have been discussed in list rendering before.
Initialize data
Although Vue.js provides an API to dynamically add response attributes, it is recommended to declare all response attributes on data objects.
Don't do this:
var vm = new Vue({ template: '<div>{{msg}}</div>'})// Then add `msg`vm.$set('msg', 'Hello!')Do this:
var vm = new Vue({ data: { // Declare `msg` msg: '' }, template: '<div>{{msg}}</div>'})// Then set `msg`vm.msg = 'Hello!'There are two reasons for doing this:
1. The data object is like a schema of component state. Declaring all properties on it makes the component code easier to understand.
2. Adding a top-level response property forces all watchers to recalculate because it did not exist before and no watcher tracks it. This performance is usually acceptable (especially compared to Angular's dirty checks), but can be avoided at initialization.
Asynchronous update queue
Vue.js defaults to asynchronously update the DOM. Whenever data changes are observed, Vue starts a queue to cache all data changes in the same event loop. If a watcher is fired multiple times, it will only be pushed into the queue once. Wait until the next event loop, Vue will clear the queue and only perform necessary DOM updates. MutationObserver is preferred in internal asynchronous queues, and setTimeout(fn, 0) is used if not supported.
For example, with vm.someData = 'new value' set, the DOM will not update immediately, but will update the next time the event loop clears the queue. We basically don't have to care about this process, but it will help if you want to do something after the DOM status is updated. Although Vue.js encourages developers to follow the data-driven approach and avoid directly modifying the DOM, it sometimes does. In order to wait for Vue.js to complete the update of the DOM after the data changes, you can use Vue.nextTick(callback) immediately after the data changes. The callback is called after the DOM update is completed. For example:
<div id="example">{{msg}}</div>
var vm = new Vue({ el: '#example', data: { msg: '123' }})vm.msg = 'new message' // Modify data vm.$el.textContent === 'new message' // falseVue.nextTick(function () { vm.$el.textContent === 'new message' // true})This instance method is more convenient because it does not require global Vue, and this callback is automatically bound to the current Vue instance:
Vue.component('example', { template: '<span>{{msg}}</span>', data: function () { return { msg: 'not updated' } }, methods: { updateMessage: function () { this.msg = 'updated' console.log(this.$el.textContent) // => 'not updated' this.$nextTick(function () { console.log(this.$el.textContent) // => 'updated' }) } }})The secret of computing attributes
You should note that the computed properties of Vue.js are not simple getters. Computational properties continuously track its response dependencies. When calculating a computed property, Vue.js updates its dependency list and caches the results. The cached results are invalid only when one of the dependencies changes. Therefore, as long as the dependency does not change, accessing the computed property will directly return the cached result instead of calling getter.
Why cache? Suppose we have a high-consuming computation property A, which is going to iterate through a giant array and do a lot of calculations. Then, there may be other computed properties that depend on A. If there is no cache, we will call A's getter many times, more than necessary.
Since the computed property is cached, getter is not always called when accessing it. Consider the following example:
var vm = new Vue({ data: { msg: 'hi' }, computed: { example: function () { return Date.now() + this.msg } }})Computed property example has only one dependency: vm.msg. Date.now() is not a response dependency because it has nothing to do with Vue's data observation system. Therefore, when accessing vm.example, the timestamp will be found to remain unchanged unless vm.msg changes.
Sometimes I hope that getter does not change the original behavior and call getter every time I access vm.example. At this time, the cache can be turned off for the specified computed attribute:
computed: { example: { cache: false, get: function () { return Date.now() + this.msg } }}Now every time you access vm.example, the timestamp is new. However, accessing in JavaScript is just like this; data binding is still driven by dependencies. If the computed attribute {{example}} is bound in the module, the DOM will only be updated if the response dependency changes.
This article has been compiled into the "Vue.js Front-end Component Learning Tutorial", and everyone is welcome to learn and read.
For tutorials on vue.js components, please click on the special topic vue.js component learning tutorial to learn.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.