Vue initialization beforeCreate before doing what?
Let’s continue with the initialization of this._init(), which then performs three initialization methods like this:
initInjections(vm)
initState(vm)
initProvide(vm)
Copy the code
5. InitInjections (VM): the main function is to initialize inject and access the corresponding dependency.
Inject and provide. This is a pair of apis added in [email protected] that need to be used together. It allows the parent component to provide dependencies to all descendant components after it, so that descendant components can be accessed no matter how deep the nesting
provide
: Provides an object or a function that returns an object.inject
: is an array or object of strings.
This pair of APIS on the official website of vue has two edible tips:
Provide and Inject mainly provide use cases for high-level plug-in/component libraries. Direct use in application code is not recommended.
- This is probably because it confuses the hierarchy of component data, but it works well when developing component libraries.
Provide and Inject binding are not responsive. This is intentional. However, if you pass in a listening object, the object’s properties are still responsive.
- There is a trick here, which is to take the root component
data
Properties defined in the class are provided to descendant components, which are no longer usedvuex
Can achieve simple global state management, or verycool
The ~
App. Vue root componentexport default {
provide() {
return {
app: this
}
},
data() {
return {
info: 'hello world! '}}} Child.vue descendant componentexport default {
inject: ['app'],
methods: {
handleClick() {
this.app.info = 'hello vue! '}}}Copy the code
Once the handleClick event is triggered, any descendant component that uses the Inject this.app.info variable will be responded no matter how deep it is nested, and this completes simple VUex. More examples we can go to vUE’s official website to browse, here is not the code word, now we will analyze so cool function it is exactly how to achieve ~
Inject and provide are internally initialized separately, although they are used in pairs. As can be seen from the above three initialization methods, initialize inject, initialize props/data state correlation, and initialize provide. The purpose of this is to be able to use the injected content in props/data.
Let’s first look at the method definition when initializing inject:
export function initInjections(vm) {
const result = resolveInject(vm.$options.inject, vm) // Find results... }Copy the code
Inject = vm.$options.inject = vm.$options.inject = vm.$options.inject = vm.
export function resolveInject (inject, vm) {
if(inject) {const result = object.create (null) const keys = object.keys (inject) // omit Symbol casefor (let i = 0; i < keys.length; i++) {
const key = keys[i]
const provideKey = inject[key].from
let source = vm
while (source) {
if(source._provided && hasOwn(source._provided, provideKey)) {//hasOwn specifies whether result[key] = source._provided[provideKey].break
}
source = source.$parent}... Add logic to set the default parameter of inject after [email protected]return result
}
}
Copy the code
We first define a result to return the result we found. The outer for loop will iterate over every item of Inject, and then the inner layer uses the while loop to find whether the parent of inject item provides corresponding dependencies from bottom up.
Ps: there may be someone will doubt, inject before the definition of the array, i.e., how can here through Object. The keys and values? This is because when we do the options merge in the previous chapter, we will also format the parameters. For example, the format of props, as defined as an array, will also be converted to object format. Inject is defined like this:
{inject: ['app'} inject: {app: {from:'app'}}}Copy the code
Following the text, source is the current instance, and source._provided holds the value currently provided. It looks from the current instance, then assigns its parent component instance to the source, and looks at its parent component. Break out of the loop, assign the result of the search to result, and then look for the next one.
Ps: Some people may have some questions, this time, it initializes the inject and then initialize the provide, how to access the parent provide? The parent component is initialized first, and the child component is initialized second, so it has the source._provided property.
After finding the result we thought of, we complete the previous definition of initInjections:
export function initInjections(vm) {
const result = resolveInject(vm.$options.inject, vm)
if(result) {// If there are results toggleObserving(falseKeys (result). ForEach (key => {... defineReactive(vm, key, result[key]) }) toggleObserving(true)}}Copy the code
If there are search results, we will first call toggleObserving(false). We don’t care about the implementation, except that what this method does is set up a flag bit that will determine whether the defineReactive() method sets its third parameter to responsive data. If the value of result[key] is set to the value of responsiveData, the parameter is false, and the value of key is set to the normal value under the VM, but this can be used in the current instance to access the corresponding dependency in inject. Then call toggleObserving(true) and change the bit so that defineReactive() can set the third parameter to reactive data, which is what it should be. The above is the relevant principle of inject realization. In a word, it first iterates each item and then iterates one by one whether the parent of each item has dependencies.
6. InitState (VM): specifies the state to be used for initialization, including props, Methods, data, computed, and watch.
First look at the definition of the initState(VM) method:
export function initState(vm) {
...
const opts = vm.$options
if(opts.props) initProps(vm, opts.props)
if(opts.methods) initMethods(vm, opts.methods)
if(opts.data) initData(vm)
...
if(opts.computed) initComputed(vm, opts.computed)
if(opts.watch && opts.watch ! == nativeWatch) { initWatch(vm, opts.watch) } }Copy the code
For now, I’m just going to talk about what we do with the initialization of the first three types of states, namely props, methods, and data, because computed and watch involve the reactive dependent watcher, so I’m going to skip this. Here are the three initializers:
6.1 initProps (VM, propsOptions) :
- The main function is to check whether the values accepted by the child components conform to the rules and make the corresponding values available
this
Direct access.
functionInitProps (vm, propsOptions) {// The second argument to the validation rule is const propsData = vm.$options. PropsData | | {} / / props specific value const props. = the vm _props = {} / / store props const isRoot =! vm.$parent// Is the root nodeif(! isRoot) { toggleObserving(false)}for (const key in propsOptions) {
const value = validateProp(key, propsOptions, propsData, vm)
defineReactive(props, key, value)
if(! (keyin vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)}Copy the code
We know that props is an important way for the parent component to communicate with its children, and that the second parameter in initProps, propsOptions, is the current instance, the child component in the communication role, which defines the rule to accept the parameters. The props rule for subcomponents can be defined as an array, but after merging options, it is formatted as an object:
When defining: {props: ['name'.'age'} name: {name: {type: null
},
age: {
type: null
}
}
Copy the code
So when defining the props rules, just use the object format, which is a better way to write the specification.
Now that you know the rules, the next thing you need to know is the exact value that the parent component passes to the child component, which is placed in vm.$options.propsData as an object, which is also what you get when you merge options. Next, we define an empty object under the instance, vm._props, to mount the values that meet the specification. IsRoot checks whether the current component isRoot or not. If not, it does not convert the props to responsive data.
Then, the validation rule of the formatted props is iterated through, and the validation rule is verified using the validateProp method to obtain the corresponding value, and the value is mounted to the VM._props. This._props can now access the props defined value:
props: ['name'],
methods: {
handleClick() {
console.log(this._props.name)
}
}
Copy the code
It wasn’t friendly to access internal private variables directly, so vue did a layer of proxy inside to switch access to this.name to this._props. Proxy needs to be introduced here, because it will be used in data later. Let’s look at its definition:
Format it:export function proxy(target, sourceKey, key) {
Object.defineProperty(target, key, {
enumerable: true,
configurable: true,
get: function () {
return this[sourceKey][key]
},
set: function () {
this[sourceKey][key] = val
}
})
}
Copy the code
It is very simple, just define a get method for an object value, and when it reads, let it return another value. This completes the initialization of props.
6.2 initMethods (VM, methods) :
- The main function is to change
methods
Inside the method mount tothis
Under.
function initMethods(vm, methods) {
const props = vm.$options.props
for(const key in methods) {
if(the methods [key] = = null) {/ / the methods [key] = = = null | | the methods [key] = = = undefined shorthand warn (` defines the key only and no corresponding value `)}if(props && hasOwn(props, key)) {warn(' method name is the same as the key of the props)}if((key inVm) &&isreserved (key)) {warn(' method name already exists and starts with _ or $')} vm[key] = methods[key] == null? Noop // empty function:bindBind (vm) {bind(vm)}}Copy the code
Initialization of methods is relatively simple. There are many boundary cases, such as defining only the key but not the method implementation, the name of the key and the props being the same, and the name of the key already existing and not properly named, starting with _ or $. We explained why this is not the case in chapter 1. Finally, mount methods within methods under this to complete the initialization of methods.
6.3 initData (vm) :
- The primary function is initialization
data
Again, mount tothis
Under. There’s an important point. The reasondata
The data inside is reactive, it is initialized here, you have to remember that ~.
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'? GetData (data, vm) / / through the data. The call (vm and vm) get the object returned: data | | {}if(! IsPlainObject (data)) {// If not an Object format data = {} warn(' data must be an Object ')} const keys = object. keys(data) const props = VM.$options.props // get props const methods = vm.$options.methods // Get methodslet i = keys.length
while (i--) {
const key = keys[i]
if(methods && hasOwn(methods, key)) {warn(' same name as methods in methods')}if(props && hasOwn(props, key)) {warn(' same as key ')}else if(! IsReserved (key)) {// Key cannot start with _ or $proxy(VM, '_data', key)}} observe(data,true)}Copy the code
$options.data = function; otherwise, return data or {}, and assign the result to vm._data. This is the same routine as props, which is used to make a layer of proxies. If the result is not an object format or an error is reported.
Then we iterate through each item in data, which cannot have the same name as the key in methods and props. Then we use proxy to create a layer of proxies. Observe (data, true), which recursively makes every item in the data responsive.
In fact, it is not difficult to find that the three of them mainly do the same thing, first do not have the same name between each other, and then can be directly accessed by this.
7. InitProvide (VM): The primary role is to initialize provide dependencies for child components.
The provide option should be an object or a function, so it can be evaluated, just like the value in data.
export function initProvide (vm) {
const provide = vm.$options.provide
if (provide) {
vm._provided = typeof provide === 'function'
? provide.call(vm)
: provide
}
}
Copy the code
$options.provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide So the child component can access the dependency provided by the parent component when initializing inject; If it is not a function type, return the defined provide.
8. CallHook (VM, ‘created’): Executes user-defined created hook functions, including mixins.
The created hook function () function is created by creating a hook function ().
- InitInjections (VM) : Make child components
inject
Can access the correct value - InitState (VM) : Mounts the state of the component definition to
this
Under. - InitProvide (VM) : initializes those provided by the parent component
provide
Rely on. - Created: implements the component
created
Hook function
Now that the initialization phase is over, we’ll move on to the component mount phase. We’ll end this chapter with a typical vue interview question:
The interviewer smiled politely and asked,
- Excuse me,
methods
Can the arrow function be used in the method inside, and what will be the result?
Dui back:
- You can’t use the arrow function because the arrow function
this
It’s bound when it’s defined. invue
The internal,methods
The context of each method within is currentvm
Component instance,methods[key].bind(vm)
If the arrow function is used, the context of the function becomes the parent context, i.eundefined
Yes, the result is a yesundefined
Accessing any variable will result in an error.
Do you know how the much-talked-about virtual Dom is generated? (on)
Easy to click a like or follow bai, also easy to find ~
Reference:
Vue. Js source code comprehensive in-depth analysis
Vue.js is easy to understand
Vue. Js components
Share a component library for everyone, may use up ~ ↓
A library of vUE functional components that you might want to use.