Recommended PC viewing, mobile code highlighting disorder
What happened to New Vue
Let’s walk through the core definition of Vue, using Web-full-ESM as an example
// scripts/config.js
const builds = {
// ...
// Runtime+compiler ES modules build (for bundlers)
'web-full-esm': {
entry: resolve('web/entry-runtime-with-compiler.js').
dest: resolve('dist/vue.esm.js').
format: 'es'.
alias: { he: './entity-decoder' }.
banner
}.
}Copy the code
src/platforms/web/entry-runtime-with-compiler.js
(entry)- — – >
src/platforms/web/runtime/index.js
- — – >
src/core/index.js
- — – >
src/core/instance/index.js
(core)
Vue constructor defined in SRC/core/instance/index, js
// src/core/instance/index.js
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 VueCopy the code
initMixin(Vue)
: Does one thing, definition_init
Methods.stateMixin(Vue)
: Defines data related methods$set
.$delete
.$watch
Methods.eventsMixin(Vue)
: Defines event-related methods$on
.$once
.$off
.$emit
.lifecycleMixin(Vue)
Definition:_update
, and life cycle related$forceUpdate
and$destroy
.renderMixin(Vue)
Definition:$nextTick
._render
willrender
Function tovnode
.
You can see that new Vue basically calls the _init method
_init method
Enclosing _init method when performing initMixin binding, in SRC/core/instance/init. In js:
export function initMixin (Vue) {
Vue.prototype._init = function (options) {
const vm = this
// uid
vm._uid = uid++
// a flag to avoid this being observed
vm._isVue = true
if (options && options._isComponent) {
// Optimize internal component instantiation because dynamic option merging is very slow and no internal component options require special handling.
initInternalComponent(vm. options)
} else {
/ / merge options
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor).
options || {}.
vm
)
}
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')
/ / a mount
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}Copy the code
Vue initialization mainly does a few things:
mergeOptions
Merge configurationinitLifecycle
Initialize the life cycleinitEvents
Initialize the event centerinitRender
Initialize render, such as definitionvm._c
和vm.$createElement
initState
Initialize thedata
,props
,computed
,watcher
And so on.
All of this will be covered in subsequent chapters, but here you just need to know what you did.
Data brokers
Have you ever wondered why our data is defined in data but can be accessed through this.xxx
export default {
data() {
return {
name: 'l1shu'}},mounted() {
console.log(this.name) // l1shu
}
}
Copy the code
One of the mysteries in _init initState, SRC/core/instance/state. The js:
// src/core/instance/state.js
export function initState (vm) {
// ...
if (opts.data) {
initData(vm) // Execute the initData method
} else {
observe(vm._data = {}. true /* asRootData */)
}
// ...
}Copy the code
Read data from $options and execute initData:
// src/core/instance/state.js
function initData (vm) {
let data = vm.$options.data
data = vm._data = typeof data = = = 'function'
? getData(data. vm)
: data || {}
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV ! = = 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function'.
vm
)
}
// 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 (process.env.NODE_ENV ! = = 'production') {
// Check whether it conflicts with methods
if (methods && hasOwn(methods. key)) {
warn(
`Method "The ${key}" has already been defined as a data property.`.
vm
)
}
}
// check if the same name conflicts with props
if (props && hasOwn(props. key)) {
process.env.NODE_ENV ! = = 'production' && warn(
`The data property "The ${key}" is already declared as a prop.` +
`Use prop default value instead.`.
vm
)
} else if (!isReserved(key)) {
// Data broker
proxy(vm. `_data`. key)
}
}
// Reactive processing
observe(data. true /* asRootData */)
}Copy the code
First check whether data is a function, then execute getData, which is defined as follows:
export function getData (data: Function. vm: Component): any {
// #7573 disable dep collection when invoking data getters
// https://github.com/vuejs/vue/issues/7573
pushTarget()
try {
return data.call(vm. vm)
} catch (e) {
handleError(e. vm. `data()`)
return {}
} finally {
popTarget()
}
}Copy the code
As you can see, the getData function logic is very simple, and pushTarget and popTarget will be covered in a later section on responsiveness.
Back to the initData function:
- traverse
keys
To determine whether the defined field andprops
.methods
Conflict of the same name, and a warning is thrown in development mode isReserved
Check whether the field is reserved_
or$
At the beginningproxy
Implementing the data broker (Core)observe
Implementing reactive processing, which we’ll talk about later
Finally, see how the proxy function implements data proxy:
// src/core/instance/state.js
const sharedPropertyDefinition = {
enumerable: true.
configurable: true.
get: noop.
set: noop
}
export function proxy (target: Object. sourceKey: string. key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target. key. sharedPropertyDefinition)
}Copy the code
Add the specified key to the VM using Object.defineProperty, and modify or read this._data using the set and GET methods.
When we call this.message, we actually call this._data.message
conclusion
Vue initialization logic written very clear, the different function logic into a number of separate functions to execute, so that the main logic at a glance
Since our goal in this chapter is to figure out how the template and data are rendered into the final DOM, we’ll leave the initialization logic behind.
At the end of initialization, we detect that if there is an EL attribute, we call the vm.$mount method to mount the VM. The goal of mount is to render the template into the final DOM, so let’s analyze the mount process of Vue.