What to do: 1. Merge options and process the configuration content of the component; 2. Initialize the properties related to the vue instance life cycle; 3. Initialize the monitoring of custom component events; 4. Initialize the render rendering required slots, rendering functions, etc.; 5. Call the beforeCreate function; 6. Initialize the injected data; 7. Initialize props, data, watch, etc.; 8. Inject the data passed down from ancestors and initialize provide; 9. Call the created function; 10 , mount DOM elements.
The operating environment of this tutorial: windows7 system, vue3 version, DELL G3 computer.
There will be some mixing before creation Method, used to initialize the methods and properties of the instance
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
Just look at initMixin here first
The function of the initMixin method is to mix into Vue_init
Method, _init
The core of the method is the following method
vm.$options = mergeOptions( // 合并options resolveConstructorOptions(vm.constructor), options || {}, vm )
Merge options and mount a $options attribute on the instance. What was merged? There are two situations here:
Initializing new Vue
When executing the new Vue
constructor, the parameter is an object , that is, the user's custom configuration; it will be combined with the prototype methods and global API properties defined before vue
; as well as the parameters in the global Vue.mixin
, these will be combined Merged into a new options
, and finally assigned to a new attribute $options
.
Subcomponent initialization
If it is a subcomponent initialization, in addition to merging the above, the parameters of the parent component will also be merged. If there is a parent component defined in the child event
, props
, etc. on the component.
After merging, you can access the user-defined data function through
this.$options.data
,this.$options.name
Access to the user-defined component name. This merged attribute is very important and will be used frequently.
The main function is to confirm the parent-child relationship of the component and initialize some instance properties.
export function initLifecycle(vm: Component) { const options = vm.$options // 之前合并的属性 let parent = options.parent; if (parent && !options.abstract) { // 找到第一个非抽象父组件 while (parent.$options.abstract && parent.$parent) { parent = parent.$parent } parent.$children.push(vm) } vm.$parent = parent // 找到后赋值 vm.$root = parent ? parent.$root : vm // 让每一个子组件的$root属性都是根组件 vm.$children = [] vm.$refs = {} vm._watcher = null ... vm._isDestroyed = false vm._isBeingDestroyed = false }
Initialize the listening of custom component events. If there is a parent listening event, add it to the instance. superior. The main function is to add custom events registered by the parent component using v-on or @ to the event center of the child component.
export function initEvents (vm: Component) { vm._events = Object.create(null) // 事件中心 vm._hasHookEvent = false // init parent attached events const listeners = vm.$options._parentListeners // 经过合并options得到的 if (listeners) { updateComponentListeners(vm, listeners) } }
Initialize slots, rendering functions, etc. required for render rendering. Actually, it’s just two things. 1. Slot processing, 2. $createElm is the declaration of h in the render function
export function initRender(vm) { vm._vnode = null ... vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false) //转化编译器的 vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true) // 转化手写的 ... }
CallbeforeCreate
Hook function. Now you just need to know that it will execute the user-defined life cycle method, and if there is a mixin mixed in, it will also be executed.
Related interview questions: Can the variables defined in data be accessed through this in the beforeCreate hook? Why and what can this hook do?
> Answer: It is not accessible because during the Vue initialization phase, the variables in data have not yet been mounted on this, and the access value at this time will be undefined. . The beforeCreate hook is rarely used in daily business development. When the instanll method inside the plug-in is installed through the Vue.use method, it is usually executed in the beforeCreate hook. This is how vue-router and vuex do it.
The subsequent initialization sequence is inject => state => provide Initialize inject before props/data. The purpose of this is to use the content injected in inject in props/data.
export function initInjections (vm: Component) { const result = resolveInject(vm.$options.inject, vm) if (result) { toggleObserving(false) // 刻意为之不被响应式 Object.keys(result).forEach(key => { ... defineReactive(vm, key, result[key]) }) toggleObserving(true) } }
The main function of resolveInject is to find the current situation layer by layer from bottom to top. Injected data
export function resolveInject (inject: any, vm: Component): ?Object { if (inject) { // inject is :any because flow is not smart enough to figure out cached // 首先定义一个result返回找到的结果。 const result = Object.create(null) const keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject) // 接下来使用双循环查找,外层的for循环会遍历inject的每一项 for (let i = 0; i < keys.length; i++) { const key = keys[i] // #6574 in case the inject object is observed... if (key === '__ob__') continue const provideKey = inject[key].from let source = vm //然后再内层使用while循环自底向上的查找inject该项的父级是否有提供对应的依赖。 while (source) { if (source._provided && hasOwn(source._provided, provideKey)) { result[key] = source._provided[provideKey] break } source = source.$parent } if (!source) { if ('default' in inject[key]) { const provideDefault = inject[key].default result[key] = typeof provideDefault === 'function' ? provideDefault.call(vm) : provideDefault } else if (process.env.NODE_ENV !== 'production') { warn(`Injection "${key}" not found`, vm) } } } return result } }
初始化会被使用到的状态,状态包括props,methods,data,computed,watch五个选项。(这里先看props,methods,data)
export function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
function initProps(vm: Component, propsOptions: Object) { // 第二个参数为验证规则 const propsData = vm.$options.propsData || {} // props具体的值 父组件传过来的 const props = vm._props = {} // 存放props 组件内可以通过这个访问到传过来的props const isRoot = !vm.$parent // 是否是根节点 if (!isRoot) { // 不是根节点则关闭响应式 toggleObserving(false) } for (const key in propsOptions) { const value = validateProp(key, propsOptions, propsData, vm) defineReactive(props, key, value) if (!(key in vm)) { proxy(vm, `_props`, key) // 代理 this.xx实际上访问的是 this._props.xx } } toggleObserving(true) }
function initMethods (vm: Component, methods: Object) { const props = vm.$options.props for (const key in methods) { if(methods[key] == null) { // methods[key] === null || methods[key] === undefined 的简写 warn(`只定义了key而没有相应的value`) } if(props && hasOwn(props, key)) { warn(`方法名和props的key重名了`) } if((key in vm) && isReserved(key)) { warn(`方法名已经存在而且以_或$开头`) } vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm) // 相当于methods[key].bind(vm) } }
function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} if (!isPlainObject(data)) { data = {} } // proxy data on instance const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (methods && hasOwn(methods, key)) { warn(`和methods内的方法重名了`) } if (props && hasOwn(props, key)) { warn(`和props内的key重名了`) } else if (!isReserved(key)) { // key不能以_或$开头 proxy(vm, `_data`, key) } } // observe data observe(data, true /* asRootData */) }
很容易看出是把用户传进来的provide
选项先获取到,如果是方法则执行一下再挂载到实例的_provided
属性,不是则直接挂载到_provided
属性
export function initProvide (vm: Component) { const provide = vm.$options.provide if (provide) { vm._provided = typeof provide === 'function' ? provide.call(vm) : provide } }
调用 created
钩子函数
1、选项合并,处理组件的配置内容,将传入的options与构造函数本身的options进行合并(用户选项和系统默认的选项进行合并)
2、初始化vue实例生命周期相关的属性,定义了比如:root、root、root、parent、children、children、children、refs
3、初始化自定义组件事件的监听,若存在父监听事件,则添加到该实例上
4、初始化render渲染所需的slots、渲染函数等。其实就两件事:插槽的处理 和 $createElm
的声明,也就是 render 函数中的 h 的声明
5、调用 beforeCreate 钩子函数,在这里就能看出一个组件在创建前和后分别做了哪些初始化
6、初始化注入数据,隔代传参时 先inject。作为一个组件,在要给后辈组件提供数据之前,需要先把祖辈传下来的数据注入进来
7、对props,methods,data,computed,watch进行初始化,包括响应式的处理
8、在把祖辈传下来的数据注入进来以后 再初始化provide
9、调用 created 钩子函数,初始化完成,可以执行挂载了
10、挂载到对应DOM元素上。如果组件构造函数设置了el选项,会自动挂载,所以就不用再手动调用$mount去挂载
The above is the detailed content of What does vue initialization do?. For more information, please follow other related articles on the PHP Chinese website!