Textbook explanation (original quote)
When a component is defined, data must be declared as a function that returns an initial data object, because the component can be used to create multiple instances. If data is still a pure object, then all instances will share references to the same data object! By providing a data function, each time a new instance is created, we can call the data function to return a new copy of the original data object
ð Be careful not to gulp down dates, feel the following two sentences:
- is
component
Why does it have to be a function - Multiple instances of a component can be created
For a long time, I have understood that: why does vue need to return the data function? When we introduce JS directly, new vue must return the function. Until today it was discovered that the data of the component created by Vue was not required to be returned by the function
If you don’t want to look at the analysis, look at this
Tip problem analysis and summary
The reason why new Vue can return without using a function is that every time new is passed, a new object (a new memory address) is passed. So changing one instance of Vue does not affect the other instances
For components, there are default values once the component is defined (after we introduce a component and change some of the values). Again into the same component, the introduction of the second component of initial value or keep the original Settings) so the component registration (an internal process of vue), vue will save the component incoming configuration, repeatedly generated when the same component values from the save configuration, and then through the new to create a new component instance. If the data is an object (the memory address of the reference type is the same), then each time a new component instance is generated, the data points to the same memory area, and the value of one of the components of the same type is updated. The rest will be updated along with it
To solve the component problem, we need to use function form. Each time we create a component, we need to return a new object (an object with a different memory address) through function. This way, the data of the component is its own
To understand this, start with prototypes
Unfamiliar to see here ð prototypes and prototype chains – basic, but very important
3 chestnut understanding after looking at the source
1. Case 1:
function Animal() {}
Animal.prototype.data = { name: 'Pet shop'.address: 'guangzhou' }
var dog = new Animal()
var cat = new Animal()
console.log(dog.data.address) / / guangzhou
console.log(cat.data.address) / / guangzhou
dog.data.address = 'dongguan'
console.log(cat.data.address) / / dongguan
dog.data === cat.data // true
Copy the code
- Tip’s first takeaway
Both dog and cat are based on Animal. Naturally inherits the properties of the stereotype. After inheritance, since data is a normal object and a reference data type, the data for dog and cat actually point to the same memory address
Even the strict operator judgment is equal, which means they have the same value and the same memory location, and modifying one will affect the other
2. Case 2:
function Animal() {
this.data = this.data()
}
Animal.prototype.data = function() {
return { name: 'Pet shop'.address: 'guangzhou'}}var dog = new Animal()
var cat = new Animal()
console.log(dog.data.address) / / guangzhou
console.log(cat.data.address) / / guangzhou
dog.data.address = 'dongguan'
console.log(cat.data.address) / / guangzhou
console.log(dog.data.address) / / dongguan
dog.data === cat.data // false
Copy the code
This.data = this.data()
Animal actually acts as constructor during our implementation of new. See what happens to a new object. At this point this.data is still a function, and it hasn’t been executed yet, so call this.data(). Let the function return a value. Then re-assign to this.data
- Conclusion 2
When a function is used, data is locked in the scope of the current function and returned, creating another object, so that multiple instances do not interact
3. Case 3
function Animal({ data }) {
this.data = data
}
var dog = new Animal({ data: { name: 'Pet shop'.address: 'guangzhou'}})var cat = new Animal({ data: { name: 'Pet shop'.address: 'guangzhou'}})console.log(dog.data.address) / / guangzhou
console.log(cat.data.address) / / guangzhou
dog.data === cat.data // false
Copy the code
- Conclusion tip # 3
Note that the way the variable is declared is directly in the constructor, not through the prototype chain. This is why new Vue data can be non-function, and when the constructor executes, the data is already isolated from each other, right
Using the debugger, see what happens to new Vue
Multiple pictures warning!! What happened to New Vue!!
For new Vue, look at case 3. In the process of new, the parameter assignment is passed in
Start the debugger
<! -- Introduce vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
Copy the code
debugger
// Enter debugger mode before new Vue
var app = new Vue({
el: '#app'.data: { message: 'Hello Vue! '}})Copy the code
1. The vUE is initialized
2. Go inside the init method
- Line 4994 is our common VM object. This is the this object of vue. (The image is not long enough, a little bit up to see vm = this)
- 4998 We often say the first step of the life cycle
beforeCreate
- This is the function we’re going to dig into today:
initState
Initialize the data object - 5002 Life cycle step 2
create
BeforeCreate can not get this. Data. You need to get it when you create it
3. Go to the initState method
- You can see the initialization
props
Initialization,methods
. And then it’s initializationdata
. If there is no data, it will give a default value{}
- Initialize the
data
Then start processingcomputed
. Then mountwatch
- The topic is research
data
. Continue intoinitData
Inside the function
4. InitData method
data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
Copy the code
- As you can see, there is a judgment. If a function is passed, the function is called (
The getData method is what calls the function to return the object
). Data is the default value if it is not a function, otherwise it is the default value. - The next step is to start doing some proxy, data hijacking listening
proxy
,observer
Such asWe don’t talk about data anymore. I’ll analyze it next time
summary
::: Tip new vue Summary
Vue = Vue; Vue = Vue; Vue = Vue; Vue = Vue; Vue = Vue; Vue = Vue; Vue = Vue; Vue = Vue; You can also return a function
: : :
New Vue source simply look below. Let’s get back to the main character of the daycomponents
The implementation of the
As a component type, components is a simple factory pattern (the parameters of the component are set at the beginning, and a new component needs to be created, referred to as the factory pattern), and many instances of the component are created. Just like in case 1
Write a debugger to get into the source code
Daily multi-picture warning!!
<! -- Introduce vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
Copy the code
// Enter debugger mode before new Vue
debugger
// Define a new component called button-counter
Vue.component('button-counter', {
data: function() {
return {
count: 0}},template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Copy the code
1. Enter initAssetRegisters initialization registry (Component Registry)
- Line 5225. Determine which one to register
component
component - Line 5232. Determine which one to register
directive
The instructions. The registration events are all thereinitAssetRegisters
In the. - Line 5226
validateComponentName
Verify that the component name is occupied - 5229 is to determine whether the component has a defined name. If not, use the label of its own component. This is to verify that the component has been generated for the previous step.
- 5230
this.options._base.extend(definition)
I have this code, so I’m going to go in there
In the extend function
this.options._base
It’s actually Vue in the picture below. callVue.extend
- Pay attention to lines 5146 to 5148. I broke at 5147. The next steps will come back here
- All the way down. It generates a
Sub
Object. - Lines 5149-5155. Is to prepare a
new
In the process.
Sub.prototype = Object.create(Super.prototype) // Prototype constructor
Sub.prototype.constructor = Sub // The constructor equals the Sub method. So when we say new, we're going to execute what's in Sub
Sub.cid = cid++
Sub.options = mergeOptions(Super.options, extendOptions) // Merge parameters, etc
Copy the code
- Line 5192. the
Sub
Object return returns. That is back toinitAssetRegisters
The function is gone - When you get back, take
Sub
Assigned todefinition
Object (line 5230 of step 1) - And then definition gets returned. One of these returns is wrapped in a function. The function is assigned to
Vue[type]
(Line 5217 of step 1 received) At this time type iscomponent
. Equivalent to callingVue.component
In this case, the return value isSub
- Key points:Lines 5152 and 5154. Sub.options merges two objects, which are
Super.options(should be some parameters of the parent)
. The second is to merge its own parameters,Data is in line 5154. We’ll talk about this in the next stepsoptions
It’s a little long. Separate 2 pictures
3. Find a way to enter the init method
Because we left a breakpoint in Step 2, the way the component was created in the first place was created globally. Probably many steps did not see, change the code to a local component, in the debugger
Change the code to this, because we have breakpoints, so there is no need for debugger, refresh directly into the breakpoint we set:
var ComponentA = {
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'.data() {
return { count: 0}}}var app = new Vue({
el: '#app'.components: {
'component-a': ComponentA
}
})
Copy the code
Can go back into Sub. Indicating that the step we explored earlier was called. The Sub method is called to execute init. So we’re init. Go back to the previous step and see who called it.
It seems that this step is to start a new component. So the init method is triggered
This time I finally see the next function
4. Go to the init method of the component
- A very familiar feeling. That’s right! is
new Vue
The process! After all, components have their own lifecycle, parameters, sub-components, so here we are again - the
initState
–initData
I’m not going to repeat the process. If you’re not clear, you can look up againnew Vue
In the process.
5. The data of the component is ininitData
The role of
It starts to go around here. Think clearly
-
Recall line 5152 of the extend function in Step 2. And 5154 rows. The options of the component are stored.
-
That is in line 4700 of the figure below. The VM is the current component. Options are the Sub object generated when the component is registered
6. Abstract some code at this point
- Sub.options is a value that has been added since the component was registered. So let’s show you the default as well
- Sub.prototype.init estimates are late assignments to the function that creates the life cycle of the vue. So let’s give him a simplified version of the function, just simulating the assignment of this.data, and see what happens
var Sub = function() {
this.init()
}
Sub.prototype = {}
Sub.prototype.constructor = Sub
Sub.prototype.init = function() {
this.data = typeof Sub.options.data === 'function' ? Sub.options.data() : Sub.options.data
}
Sub.options = {} // The default value will be given later
Copy the code
7. Simulate new components according to the abstract code
The first attempt was to use the data object form:
The principle is the same as in case 1 above
Because data is a reference type. Sub.options has a value from the beginning, and when you create a new component, you take the value from the same place
// It is also said above. Let's start with a default value for sub.options. Simulate the parameters passed in
Sub.options = {
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'.data: {
count: 0}}// Combine the code from Step 6. Create three components
var component1 = new Sub()
var component2 = new Sub()
var component3 = new Sub()
console.log(component1) // {data:{count:0}}
console.log(component2) // {data:{count:0}}
console.log(component2) // {data:{count:0}}
// It looks all right. Let's change the value of a component
component1.data.count = 1
// The fabled idea of component medians influencing each other appears
console.log(component1) // {data:{count:1}}
console.log(component2) // {data:{count:1}}
console.log(component3) // {data:{count:1}}
Copy the code
What if I change it to a function?
The tip principle is the same as in Case 2.
Sub.options gets the same value. But sub.options. data is already a function type, not a reference type. After the function is executed, the return value is not the address of the heap memory, so changing a Sub instance (the value of the component) does not affect the rest of the components
// It is also said above. Let's start with a default value for sub.options. Simulate the parameters passed in
Sub.options = {
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'.data() {
return {
count: 0}}}// Combine the code from Step 6. Create three components
var component1 = new Sub()
var component2 = new Sub()
var component3 = new Sub()
console.log(component1) // {data:{count:0}}
console.log(component2) // {data:{count:0}}
console.log(component3) // {data:{count:0}}
component1.data.count = 2
// Now it will not affect each other
console.log(component1) // {data:{count:2}}
console.log(component2) // {data:{count:0}}
console.log(component2) // {data:{count:0}}
Copy the code
8. Finally, summarize the demo
You can try to change it yourself. Run a run
var Sub = function() {
this.init()
}
Sub.prototype = {}
Sub.prototype.constructor = Sub
Sub.prototype.init = function() {
this.data = typeof Sub.options.data === 'function' ? Sub.options.data() : Sub.options.data
}
Sub.options = {
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'.data() {
return {
count: 0}}}// Combine the code from Step 6. Create three components
var component1 = new Sub()
var component2 = new Sub()
var component3 = new Sub()
console.log(component1) // {data:{count:0}}
console.log(component2) // {data:{count:0}}
console.log(component3) // {data:{count:0}}
component1.data.count = 2
// Now it will not affect each other
console.log(component1) // {data:{count:2}}
console.log(component2) // {data:{count:0}}
console.log(component2) // {data:{count:0}}
Copy the code