Analysis source code series-Vue.set / vm.$set
Detailed
Article Directory
Function and concept description
Add a property to the responsive object and ensure that this new property is also responsive and triggers the view update. It must be used to add new properties to the reactive object, because Vue cannot detect ordinary new properties (such as this.myObject.newProperty ='hi')
Limitations It is
not allowed to dynamically add root-level responsive attributes. such as:
// 错误写法
this.$set(this, 'newkey', 1111)
// 正确写法
this.$set(this.obj, 'newkey', 111)
// 取值: this.obj.newkey => 111
summary
Vue.set
Andvm.$set
methods are actually the same, but the wording is not the same- The effect is to dynamically add attributes to the page, and the dynamically added attributes are also responsive attributes
Why use set to add responsive attributes
Look at vue Responsive principle: vue two-way data binding principle
can be seen in fact rely on a responsive Object.defineProperty
and Object.defineProperty
just listen to one of the attributes of an object, if there are multiple properties needs to listen
Look at a demo to understand:
var data = {
}
Object.defineProperty(data, 'data1', {
get: function() {
console.log('get data1')
return this.value
},
set: function(newVal) {
console.log('set data1')
this.value = newVal
}
})
data.data1 = 111 // 将会打印 set data1
console.log(data.data1) // 先打印 get data1 然后才是 111
data.data2 = 222 // 无打印,无报错
console.log(data.data2) // 直接打印222。表示没有进过 `Object.defineProperty`
Start looking at the source code
Here is a demo for debugging
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
addData: {
}
}
})
debugger
app.$set(app.addData, 'newkey', 1111)
</script>
The first to enter the $set
methods of the time, first came to the default value section, this time to return a null value, after the return, re-enter $set
is the part we need to debug
In debug mode, enter the set method
Line 2 1081 a method for detecting the data type of the target node, i.e. $set
the detection of the first parameter
- 1085-1089 determine the type of the array. After all, because the special type
Object.defindPropety
cannot detect the change of the array, the change of the array is updated manually. - 1090-1093 is to determine whether the attribute already exists before, and there is no need to repeat the monitoring if it exists
- 1095--1100 is to determine the root node can not
this.data
be directly added to the data - Note that 1094 row variable
ob
is observed (observer) for short. It is also used to determine whether the variable has been monitored - The values that have been monitored and do not need to be repeatedly monitored will be directly returned to the corresponding ones
val
. It also$set
returns a return value of the current - If the object is new, then it went to the 1106 line
defineReactive$$1
method
Add a listener defineReactive$$1
method
- 1021 initialize
new Dep
view the analysis of dep look dep analysis is necessary. This involves many of the following processes. The csdn does not support hash navigation, so you need to manually slide it down and take a look~ - 1023-1026 Determine whether the object is readable and writable
- 1019--1033 get on the target object
get
andset
method. - 1035 There is a shallow variables in our previous code in this variable does not exist
!shallow
that is true. Therefore, the implementation of aobserve
method to view observe analysis csdn does not support the hash navigation, you need to manually slide down under their own to see ~ - After reading the above
observe
analysis, we knowobserve
that property for his son, cycling add listening. - Line 1039 is bound to the event that gets the attribute value. When getting the corresponding value, I
Dep.target
just saw it as a global observerwatch
. So, if there isdep.target
call a target for the currentdepend
method, which is fromDep
the inherited. It is equivalent to register a callback to watch the event (which is estimatedwatch
andcomputed
a foreshadowing buried) - 1052 start line
set
method. Also start their owngetter
to get the current value inside - Line 1069, if we are copying an object newly, he has to re-add a data hold for these objects in a loop, if it has already been held, it can be skipped, which is the code above
- The last 1070 lines are called after set
dep.notify()
. It stores the time when the corresponding object update needs to be triggered (subscription observer mode). Now that the value is updated, it is triggered to the relevant subscription function (watch is also triggered at this time, and the view is also updated at this time) . And we have to get the set value before the front, so thewatch
methods insidenewVal
andoldVal
is being recorded at this time - Finally, the current value is returned,
$set
and the execution of the method is over
dep analysis
The dep code is not very long. dep is equivalent to a subscription center
- In line 717, you can see that each dep has a corresponding ID, and it is self-increasing
- Line 718 can be regarded as an event center, and all monitoring is stored here
- You can see that there are several methods on the dep prototype
addSub
removeSub
depend
notify
. They are all used to operate the corresponding monitor, add/delete, find the corresponding dependency, and notify these methods - 725 line
Dep.target
has very detailed notes, globally unique viewer
The core is remember a few addSub
removeSub
depend
notify
methods. Then continue to look at the code just now
observe analysis
Especially thoughtful notes
Attempt to create an observer instance for a value,
try to create value for the observer instance
returns the new observer if successfully observed,
if successfully observed, the new viewer is returned
or the existing observer if the value already has one.
Or existing Observer of (if the value already contains one)
-
It seems
value.__ob__
if there is, then this property would have been an observer, and this is our$set
first step in a judgment as to__ob__
the -
Then distinguish the array, the array does not have observers
-
Mainly to see the 1003 line, create a new observer
new Observe
note the capital, not the current object -
new Observe
In Figure 2 below, I won’t talk about it separately. -
926 lines. To
__ob__
add properties, make no mistake, and__ob__
set it to a non-enumerable type. For details, you can see the code that goes in 926 -
927-933 are all for array operations
-
Line 935, the walk method of the current object. Pasting the code directly, we can see that it is a loop, traversing our object once, and calling it for each object
defineReactive$$1
. As you can see, this method will only be called if the value is an object type. -
Since it is called
defineReactive$$1
. Then a recursion is formed here, the head of the recursion is to stop calling when the attribute is no longer an objectdefineReactive$$1
. -
After seeing this, the next step should go back to add a method to listen defineReactive $$ 's 1035 line
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
Observer.prototype.walk = function walk(obj) {
var keys = Object.keys(obj)
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i])
}
}
to sum up
-
vue responsive principle relies on
Object.defindPropety
theget
andset
methods, respectively, in these two methods to trigger the corresponding event -
Because of JS and
Object.defindPropety
restrictions that can not be dynamically added need to monitor the property, so we must useVue.set()
the method -
Vue.set()
Inside the method is a cyclic processing process. If the new monitor is an object, it will continue to call itself to form a recursion, until the last sub-attribute is a数组/非对象类型
parameter, the recursion ends, and then add a monitor for yourself, in the monitor It will trigger other related methods (events subscribed in Dep will be triggered). Form our common two-way data binding -
Since the
Object.defindPropety
change can only monitor the object, so the value of a particular index within the array change is not listening to, so should be usedVue.set
to manually trigger an update, this timeVue.set
only do the updated value, and will not repeat New Increase monitoring
Original starter: Analysis Source Series - Vue.set / vm.$set
Detailed This is a new blog address, interested can look at