preface
From the small white contact front development has been nearly a year, from the beginning even need to understand the principle of closure of one night (although the further I don’t understand this principle) to now able to achieve a simple one-page webapp, review of nearly a year’s time, is also in accordance with the previous leave footprints, walk one step at a time, From HTML to CSS to JS to jQuery library to Webpack to Vue. Along the way relatively smooth, single also stumble, also have doubts. When I first got to know Vue, it was amazing! There is such a way to write, simple, convenient. But of course the process is full of questions, why is this so? Why can I write that. After nearly a year of polishing, this xiaobai also decided to start research, read Vue source, hope in the advent of Vue3.0, will 2.6.X source research again, for the future 3.0 foundation, and will this year in the heart of many doubts all solved! Also hope this article is also helpful to friends on the road of Vue ~
First, preparation
Before we start reading, we need to know about the structure of the Vue file and the static type checker FLOW used for Vue source code.
File architecture
After downloading the latest vue source from Github, we can see that the vue source project file is still very large, similar to the general webpack, our Vue source file also exists in the SRC file.
The core file is the core of this article and the core of Vue instance initialization.
The platform folder is where our vUE gets into the runtime, whether it’s packaged through the CLI or referenced directly in HTML, etc.
The server file contains the code that generates the render template compilation, such as how our template is compiled into the Render function, and the logic is in this folder.
FLOW
FLOW is a static type checker. Due to the large number of classes available in Vue, the FLOW static checker is introduced to ensure that class properties don’t go wrong. Its usage is similar to TypeScript, but it won’t be explained here.
Second, the new Vue ()
1, Class Vue
The text begins! First, we find the location of the Vue class definition 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)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
Copy the code
You can see here that the Vue class is very “simple “= =; A judgment is made to prevent calls to Vue functions, followed by an initialization of this._init(options). _init is mixed into the class at initMixin(Vue). Options is the object passed in that we usually write main.js:
new Vue({
render: h= > h(App),
}).$mount('#app')
Copy the code
So, let’s take a look at what initMixin does to Vue, what functionality it adds to this class, and what the _init() function does.
2, initMixin ()
As you can see from the code above, initMixin is in the init.js file in the same directory as initMixin. Let’s see what initMixin does.
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options? : Object) {
//some codes}}export function initInternalComponent (vm: Component, options: InternalComponentOptions) {
//some codes
}
export function resolveConstructorOptions (Ctor: Class<Component>) {
//some codes
}
function resolveModifiedOptions (Ctor: Class<Component>): ?Object {
//some codes
}
Copy the code
InitMixin adds only one _init method to Vue, so we’ll focus on _init. What does _init do in Vue instantiation
_init()
First, paste the source code
Vue.prototype._init = function (options? : Object) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
/* istanbul ignore else */
if(process.env.NODE_ENV ! = ='production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el) // The first mount of the instance}}Copy the code
1, mergeOptions
Let’s start by looking at the options we pass in, and see if there is an options._isComponent. The initialization logic for the Vue goes, of course, by merging the parent (Vue) options with the current options via mergeOptions(). Where does Vue get options? !!!!! Here to sell to keep in suspense, later will talk about ~ or a more important piece, so leave a suspense, is also to deepen the impression ~
2, a series of initXXX()
As you can see, there are a number of init initializations that follow. This chapter will give you a general overview of what each init does, and we’ll talk about it in a later article. InitEvents: initialization of vUE listening events; InitRender: the render method that initializes a Vue, which is explained at length in this article; Callhook: beforeCreate: invokes the preset beforeCreate hook function. InitInjections (VM) : descendant component injection related initialization; InitState (VM) : initializes properties such as data, props, and methods. This is the core of bidirectional data binding. InitProvide (VM): Descendant component initProvide related initialization; CallHook (VM, ‘created’): Calls a predefined Created hook function.
3,vm.$mount(vm.$options.el)
The final step is to mount the instance by calling the vm.$mount() method. As you can see, if we pass in a property that has an EL property, it’s called, otherwise it’s not called. So we will have two cases in New Vue:
new Vue({
render: h= > h(App),
}).$mount('#app')// do not go _init() mount call itself
new Vue({
el:'#app'//_init calls mount directly
render: h= > h(App),
})
Copy the code
At this point, the _init(options) process is over, although we’ll mention it countless times later in this article… Let’s take a look at what vm.$mount() does and how it actually mounts our Vue instance.
3, vm. $mount (el)
Vm. $mount (el) defined in SRC/platforms/web/index, js, together we take a look at the code.
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
) :Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
Copy the code
Looks like just a wrapper, we continue to look for mountComponent (this, el, hydrating), the code in the SRC/core/instance/lifecycle. Js. Let’s look at the code:
export function mountComponent ( //Vue instance mount vm: Component, el:? Element, hydrating? : boolean) :Component {
vm.$el = el
if(! vm.$options.render) { vm.$options.render = createEmptyVNodeif(process.env.NODE_ENV ! = ='production') {
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0)! = =The '#') ||
vm.$options.el || el) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
)
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
)
}
}
}
callHook(vm, 'beforeMount')
let updateComponent
/* istanbul ignore if */
if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
updateComponent = (a)= > {
const name = vm._name
const id = vm._uid
const startTag = `vue-perf-start:${id}`
const endTag = `vue-perf-end:${id}`
mark(startTag)
const vnode = vm._render()
mark(endTag)
measure(`vue ${name} render`, startTag, endTag)
mark(startTag)
vm._update(vnode, hydrating)
mark(endTag)
measure(`vue ${name} patch`, startTag, endTag)
}
} else {
updateComponent = (a)= > {
vm._update(vm._render(), hydrating) // Mount the instance, call}}new Watcher(vm, updateComponent, noop, {
before () {
if(vm._isMounted && ! vm._isDestroyed) { callHook(vm,'beforeUpdate')}}},true /* isRenderWatcher */)
hydrating = false
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')}return vm
}
Copy the code
Let’s look at his logic step by step and look at his logic. 1,! Vm. $options.render determines to create an empty vNode if the instance does not have a render function. CallHook (vm, ‘beforeMount’) calls the two arguments in _update(), vm._render() to create a node, which we’ll focus on later.
let updateComponent () => {
vm._update(vm._render(), hydrating)
}
Copy the code
4, New Watcher() adds an observer, which is a render Watcher that listens for data changes and expands the node rendering. For now, all we need to know is that Watcher calls the updateComponent function passed in during initialization, triggering both rendering and instance mounting. CallHook (VM, ‘mounted’): The vm.$mount function is now complete.
Third, summary
Update vm._render(vm._render(), hydrating) Vm._update () and vm._render() are also two important ones. One involves patch (which will definitely involve diff algorithm) and the other involves Vnode creation. Now this little white is also in a relatively unthinking situation, I don’t know how to expand it, it will be easier to accept. Need to sort out the outline again in continue to write down ~ so stop here first! Here also posted the current white learning arrangement outline: Vue source code interpretation
The final final hit a small advertisement ~
Big guys need SMS service, or number authentication ability, you can see here! China Unicom Innovation capability platform operator official platform! No middleman to earn the difference ~