preface
As a front end rookie for 4 years, I thought it was time to settle down. After thinking about it, I decided to start with the framework I was using, Vue, so I decided to spend 1-2 months reading the source code in detail, despite having read it a few times before.
The target
Figure out the functions of each module and sort out the main process of Vue initialization.
Preparation before Reading
This reading is based on V2.6.14
1. Github downloads Vue source code
2. The installation package
3. Create the test HTML file
Directory Structure description
. ├ ─ ─ BACKERS. Md ├ ─ ─ LICENSE ├ ─ ─ the README. Md ├ ─ ─ benchmarks / / performance test │ ├ ─ ─ big - table │ ├ ─ ─ dbmon │ ├ ─ ─ the reorder - list │ ├ ─ ─ SSR │ ├ ─ ─ SVG │ └ ─ ─ uptime ├ ─ ─ dist/directory/compiled ├ ─ ─ examples / / 🌰 │ ├ ─ ─ commits │ ├ ─ ─ elastic - the header │ ├ ─ ─ firebase │ ├ ─ ─ Grid │ ├── MarkDown │ ├── Modal │ ├── moving-exercises │ ├─ SVG │ ├─ test.html │ ├─ Todomvc │ ├─ exercises _ _ _ _ _ _ _ _ _ _ _ _ _ _ ├ ─ ─ the flow / / flow statement │ ├ ─ ─ compiler. Js │ ├ ─ ─ component. The js │ ├ ─ ─ global - API. Js │ ├ ─ ─ modules. Js │ ├ ─ ─ options. Js │ ├ ─ ─ SSR. Js │ ├─ ├─ ├─ download.exe │ ├─ download.exe │ ├─ download.exe ├─ UE-Server-renderer │ ├─ Ue-template-Compiler │ ├─ weex-template-Compiler │ ├─ Weex-Vue-Framework ├─ │ ├─ alias.js │ ├─ config.js │ ├─ featuresflags.js │ ├── Build Gen - release - note. Js │ ├ ─ ─ the get - weex - version. Js │ ├ ─ ─ git - hooks │ ├ ─ ─ release - weex. Sh │ ├ ─ ─ the sh │ └ ─ ─ ├─ SRC // Verify-commit-mg.js ├─ SRC // Key to explain the module │ ├ ─ ─ the compiler │ ├ ─ ─ the core │ ├ ─ ─ platforms │ ├ ─ ─ server │ ├ ─ ─ SFC │ └ ─ ─ Shared ├ ─ ─ the test / / test module │ ├ ─ ─ e2e │ ├ ─ ─ Helpers │ ├ ─ ─ SSR │ ├ ─ ─ unit │ └ ─ ─ weex ├ ─ ─ types / / ts statement │ ├ ─ ─ the index, which s │ ├ ─ ─ the options, which s │ ├ ─ ─ the plugin, which s │ ├ ─ ─ The test │ ├ ─ ─ tsconfig. Json │ ├ ─ ─ typings. Json │ ├ ─ ─ umd. Which s │ ├ ─ ─ vnode. Which s │ └ ─ ─ vue. Which s └ ─ ─ yarn. The lockCopy the code
For better debugging, we create a new test.html under test and introduce dist/vue.js
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>Document</title>
<script src=". /.. /dist/vue.js"></script>
Copy the code
The preparation work is basically finished, now start to look at the main process.
Initialize the
SRC/core/index. Js,
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
Copy the code
Vue imports from./instance/index
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
Initialization, mainly this _init method.
It defined in the instance/init, that includes four methods initMixin, initInternalComponent, resolveConstructorOptions, resolveModifiedOptions
initMixin
The main function of this function is to initialize something: what is specified in the comment;
let uid = 0
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options? :Object) {
const vm: Component = this
// a uid
// Each vUE instance generates a uid, which is incremented
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
// Merge options subcomponents and root components merge configuration items differently
// Optimize merging of options for subcomponents
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 */
// Set the proxy to delegate attributes on the VM instance to vm._renderProxy
if(process.env.NODE_ENV ! = ='production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
$parent, $root, $children, $refs
initLifecycle(vm)
// Initialize the event
initEvents(vm)
// initialize $slots, $scopedSlots, mount $createElement
$listeners set $attrs, $listeners to responsiveness
initRender(vm)
/ / call beforeCreate
callHook(vm, 'beforeCreate')
// Initialize inject before data/props is initialized, and at initialization, switch to non-responsive state
// Inject is non-responsive
initInjections(vm) // resolve injections before data/props
// Initialize data props, data, method, computed, and so on
initState(vm)
// initialize provide
initProvide(vm) // resolve provide after data/props
// Calls the created hook
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)
}
// Attach to el
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
Copy the code
resolveConstructorOptions
This function merges options
export function resolveConstructorOptions (Ctor: Class<Component>) {
let options = Ctor.options
// If there is a parent class, recurse
if (Ctor.super) {
// Recursively merge options, if base class
const superOptions = resolveConstructorOptions(Ctor.super)
// If options change, you need to merge them again
const cachedSuperOptions = Ctor.superOptions
if(superOptions ! == cachedSuperOptions) {// super option changed,
// need to resolve new options.
Ctor.superOptions = superOptions
// check if there are any late-modified/attached options (#4976)
// If options change, you need to merge them again
const modifiedOptions = resolveModifiedOptions(Ctor)
// update base extend options
// Then remerge
if (modifiedOptions) {
extend(Ctor.extendOptions, modifiedOptions)
}
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
}
}
return options
}
Copy the code
From the debugger, we can see the merged $options as shown below:
Merges the options passed in at initialization with some properties of the vue constructor
resolveModifiedOptions
The function of this function is to reassign if the option of super changes by traversing it
function resolveModifiedOptions (Ctor: Class<Component>): ?Object {
let modified
const latest = Ctor.options
const sealed = Ctor.sealedOptions
for (const key in latest) {
if(latest[key] ! == sealed[key]) {if(! modified) modified = {} modified[key] = latest[key] } }return modified
}
Copy the code
$mount
Debug through breakpoints. We know that $mount method definitions in platforms/web/runtime/index. Js
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
) :Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)} $mount only does one thing, executes`mountComponent`
Copy the code
mountComponent
UpdateComponent does a few things:
1. Run the beforeMount lifecycle function.
2. Mount vDOM and convert it to DOM.
3. Call _update;
4. Create the component’s Watcher
5. Run the Mounted life cycle function.
export function mountComponent (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 = () = > {
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 = () = > {
vm._update(vm._render(), hydrating)
}
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
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
conclusion
The main trend of new Vue() actually looks relatively simple.
1. Initialize by calling _init
$parent, $root, $children, $refs
3. Initialize some custom events, including the comparison and update of old and new events
$slots, $scopedSlots, $createElement
5. Call beforeCreate
6. Initialize the Inject before data/props is initialized and switch to the non-responsive state during initialization
Initialize data props, data, Method, computed, and so on
8. Initialize provide
9. Call the created hook function
If there is an EL, mount $mount automatically. If there is no el, call $mount manually
11.$mount Execute mountComponent;
11.1 Run the 'beforeMount' life cycle function; 11.2 Mount vDOM and convert it to DOM. 11.3 Calling the '_update' method; 11.5 Running the 'Mounted' lifecycle FunctionCopy the code
At this point, the initialization to rendering process is almost complete, but we’ll talk about the details later
Next video
Response type principle, as well as realization and simple Vue
The last
Look at it seems not so difficult, humble for attention, for praise, I hope you pay attention to my public number front engineer, we progress together, write bad, spray light ~