Better reading experience of the original text
When you pass a normal JavaScript Object to the Vue instance’s data option, Vue iterates through all of the Object’s properties and converts them into getters/setters using Object.defineProperty. Object. DefineProperty is a non-shim feature in ES5, is that why Vue does not support IE8 and later browsers
The above excerpt is from the deep response principle
So, what does it look like to convert all of these properties into getters and setters? This paper is not in-depth specific, simple and general understanding of its process, to grasp the overall understanding of its main ideas
Assume the following code:
const vm = new Vue({
el: '#app'.data: {
msg: 'hello world'}})Copy the code
The data option can accept an object or method, in this case an object.
First, all the key-value pairs of the object will be mounted on vm._data (in addition to the __ob__ key on the vm._data object, we can ignore it for now) so that we can access the data using vm._data. MSG
But we usually use vm.msg to access data like this. How do we do this? Proxy = vm._data[key]; proxy = vm._data[key]
proxy(vm, `_data`, key)
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
Copy the code
$data = vm.$data = vm.$data = vm.$data = vm.$data = vm.$data
const dataDef = {}
dataDef.get = function () { return this._data }
Object.defineProperty(Vue.prototype, '$data', dataDef)
if(process.env.NODE_ENV ! = ='production') {
dataDef.set = function () {
warn(
'Avoid replacing instance root $data. ' +
'Use nested data properties instead.'.this)}}Copy the code
The simple idea is to access the VM.Data. MSG is actually a vm._data. MSG. If directly in the development environment on vm.data = xxxThis assignment is not
$data. MSG = XXX ‘;
MSG, vm._data. MSG, and vm.$data. MSG. The original data is vm._data. MSG, and the other two are represented by _data. $data = Vue; $data = Vue; $data = Vue; $data = Vue
Now, getter/setter
Add a few things to the demo:
const vm = new Vue({
el: '#app'.data: {
msg: 'hello world'
},
computed: {
msg2() {
return this.msg + '123'}}})Copy the code
Msg2 is dependent on MSG. When msG2 changes, the value of MSG2 needs to be updated automatically. The change of MSG2 can be heard in the setter of vm._data. MSG.
Intuitively, it seems possible to run through all the key-value pairs of computed objects and then analyze them, but I wonder if I need to parse the AST, or the re, to see if this. MSG is used, or this.$data. This._data. MSG, and iterating through all the keys in the data, seems like a lot of work. Besides, if msG2 is not used in the program, it is unnecessary.
In fact, Vue initializes by setting a getter/setter for each key/value pair of vm._data as follows:
// obj 即为 vm._data
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
Object.defineProperty(obj, key, {
enumerable: true.configurable: true.get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if(newVal === value || (newVal ! == newVal && value ! == value)) {return
}
/* eslint-enable no-self-compare */
if(process.env.NODE_ENV ! = ='production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if(getter && ! setter)return
if (setter) {
setter.call(obj, newVal)
} else{ val = newVal } childOb = ! shallow && observe(newVal) dep.notify() } })Copy the code
The core of Vue responsiveness is that getters collect dependencies and setters trigger dependency updates
Again, with computed MSg2 as shown above, when we value MSg2 for the first time (note that it has to be evaluated, either in template or in a program), we have to value this. MSG, which triggers the getter of MSG. At this point we can confirm that MSG2 depends on MSG
What can MSGS depend on? So far it looks like there are three
- The template in the template
- computed
- watch
We can print vm._watchers, which is an array of Watcher instances. We can see the expression value of Watcher instances, which is the getter of MSG when the expression is triggered
This expression corresponds to the above three cases, because the expression needs to be reevaluated when MSG changes, so these dependencies need to be saved, so the source code for the Watcher class
A watcher parses an expression, collects dependencies, and fires callback when the expression value changes. This is used for both the $watch() api and directives.
The watcher. Deps array represents the dependencies of the Watcher. The values are Dep instances, which can be understood as data data associated with the expression of the Watcher instance. Note that the DEPS array can be empty, either for template, which does not depend on data, or for computed, which has not yet been retrieved (for example, I have msg2 defined, but it is not used in the program, The deps is empty, which means that if I change MSG, I don’t need to notify MSG2, because MSG2 is useless. But if I type vm.msg2 on the console, I trigger the getter for MSG, and then I collect dependencies, and the DEps is not empty. This indicates that I have already used MSG2, and I need to inform MSG2 to change it next time MSG is updated.
As for Watch, I have tried that DEPS is not empty under any circumstances, which requires further check of source code confirmation
The dePS array element is a Dep instance that has a subs attribute, which is an array of Watcher instances, representing the items that depend on the Dep
Dep is linked to data. Each Dep instance corresponds to a key/value pair of data. Watcher instances depend on Dep. When data is updated, are these the only three places that need to be updated or respond at the same time?)
Summary: We’re going to set getters/setters for all key-value pairs in data, and when we get the getters we’re going to collect dependencies. When setters are set, we will trigger the collected dependencies to update the data. Understanding this gives you an initial understanding of the reactive principle of Vue
If you see the end, you might as well pay attention to my public account “code nong Jiji”