This is the fifth day of my participation in the First Challenge 2022.
The background,
In the previous article, we explained the sequence of initGlobalAPI and new Vue by analyzing vue.js entry issues through the packaging of rollup in the Vue project. This sequence determines the source of properties such as vue.options in new Vue. Understanding the source of these objects helps to understand the execution of the Vue.
This question is also a common interview question.
Second, code directory structure
Take a look at the vue project structure. This is a simplified file structure, because we are only talking about the source code, and many of the subdirectories are omitted, benchmarks, etc. Don’t worry, only the SRC directory contains the source code.
.Benchmarks ├── Examples ├─ Flow ├── SRC // source directory │ ├── Compiler │ │ ├─ CodeGen │ │ ├ ─ ─ directives │ │ └ ─ ─ parser │ ├ ─ ─ this is a most important core │ │ ├ ─ ─ components │ │ ├ ─ ─ global - API │ │ ├ ─ ─ the instance │ │ │ └ ─ ─ Render - helpers │ │ ├ ─ ─ the observer │ │ ├ ─ ─ util │ │ └ ─ ─ vdom │ │ ├ ─ ─ helpers │ │ └ ─ ─ modules │ ├ ─ ─ platforms │ │ ├ ─ ─ web │ │ │ ├ ─ ─ the compiler │ │ │ │ ├ ─ ─ directives │ │ │ │ └ ─ ─ modules │ │ │ ├ ─ ─ the runtime │ │ │ │ ├ ─ ─ components │ │ │ │ ├ ─ ─ Directives │ │ │ │ └ ─ ─ modules │ │ │ ├ ─ ─ server │ │ │ │ ├ ─ ─ directives │ │ │ │ └ ─ ─ modules │ │ │ └ ─ ─ util │ │ └ ─ ─ weex Weex, omitted │ ├─ server omitted │ ├─ SFC │ ├─ shared ├─ test ├─ typesCopy the code
Two, breakpoint into Vue source code
2.1 Breakpoints in test.html
We wrote a debugger before new Vue in test.html, opened the console, refreshed, and saw the code stopped here:
2.2 Access to the source code entry file
This comes in handy since we prepared the sourcemap file by configuring rollup’s –sourcemap parameter in the first article. Through further click breakpoint according to the steps, into the Vue constructor of files: the SRC/core/instance/index. Js.
The Vue constructor
3.1 Vue declaration file SRC/core/instance/index. Js
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '.. /util/index'
function Vue (options) {
if(process.env.NODE_ENV ! = ='production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')}this._init(options) The Vue constructor is a single line of code
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
Copy the code
From the code above, it is clear that the Vue constructor has only one critical line of code. But the amazing thing is that you don’t see the _init method declaration, so where is it declared? The answer is initMixin
3.2 the SRC/core/instance/init. InitMixin in js
As mentioned earlier, initMixn adds the _init method to the Vue constructor by exporting a function that takes the Vue constructor and extends the _init method:
// some import
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options? :Object) {
/ /... detail of _init}}Copy the code
Four,_init
The logic of the
So let’s take a step-by-step look at what _init does.
4.1 Declaring VM Variables
Vm is a very important variable in Vue, keep in mind that VM is an instance of Vue, i.e. Vm = this:
// some import
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options? :Object) {
const vm: Component = this // VM is an instance of Vue
// a uid
vm._uid = uid++ // Each vue instance has a _uid, which is an incremented number
vm._isVue = true // An identifier used to prevent being processed by data observation}}Copy the code
4.2 Handle option merging of components or root instances
4.2.1 Component Options
Component options are the objects we pass when creating a component, such as a child component declared in test.html. This is a component option and can be understood as a configuration item required to create a Vue component.
const sub = {
template: `
{{ someKey + foo }}
`.props: {
someKey: {
type: String.default: 'hhhhhhh'}},inject: ['foo']};Copy the code
4.2.2 Root instance options
That is, the option passed to the constructor when new Vue() is the root instance option, such as the object passed in test.html to create the Vue instance:
new Vue({ // This object is the root instance option
el: '#app'.data: {
msg: 'hello vue'
},
hahaha: 'hahahahahha'.provide: {
foo: 'bar'
},
components: {
someCom: sub
}
})
Copy the code
4.2.3 Merge options
$options = vm.$options = vm.$options = vm.$options = vm.$options = vm.$options = vm
Vue.prototype._init = function (options? :Object) {
// ...
if (options && options._isComponent) {
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/ /...
}
Copy the code
Here with the help of a few methods, mergerOptions and resolveConstructorOptions, including resolveConstructorOptions is to obtain the information such as the options from the constructor itself to instance reuse, temporarily not in the list.
4.3 Agent Instance Properties_renderProxy
到 vm
Their own
When you access _renderProxy, you are accessing attributes of the VM itself, which will be used later in the rendering process.
Vue.prototype._init = function (options? :Object) {
/ /...
if(process.env.NODE_ENV ! = ='production') {
initProxy(vm); // The development environment with ES6 Proxy implementation
} else {
vm._renderProxy = vm
}
/ /...
}
Copy the code
4.4 Performing a Series of initialization operations
Vue.prototype._init = function (options? :Object) {
/ /...
// Initialize key properties such as _inactive/_isMounted/_isDestroyed,
$parent/$children/$refs
initLifecycle(vm)
// Initializes the component's custom event, such as @some-event on the custom component
initEvents(vm)
// Initialize the most important render methods: vm._c and vm.$createElement,
// Parse the slot in the component and mount it to the vm.$slots attribute
initRender(vm)
// Call the beforeCreate lifecycle hook
callHook(vm, 'beforeCreate')
// Initialize the inject option on the component, render this as result[key] = val,
// Then do a data response to this result, proxy each key to the VM
initInjections(vm) // resolve injections before data/props
/ / here is the key of the treatment response data type, the later said alone, is roughly processing props/methods/data/computed/watch
initState(vm)
// Parse the provide on the component. The provide is similar to the React Provider, which passes data deep across components
// Again, proxy the parsing results to vm._provide;
// To add a bit more, why is initProvide initialized after initinject?
// He does this because inject is on child components, provide is on parent components, and parent components are provided before child components
// Initialize it after inject without any problem because it takes the provide from the parent component,
// The parent component's provide has already been initialized
initProvide(vm) // resolve provide after data/props
// Calls the Created lifecycle hook
callHook(vm, 'created')
/ /...
}
Copy the code
El and $4.5 the mount
If the $options.el attribute exists, then $mount is executed.
Vue.prototype._init = function (options? : $mount () {$mount () {$mount () {$mount () {$mount (); $mount(vm.$options.el) {vm.$mount(vm.$options.el)}}Copy the code