I have a small transparent front end, if there is any wrong or insufficient understanding of VUE, please kindly understand and correct, thank you for your support. The corresponding vUE source code for this article is 2.6.0-release version, happy to read.
1. Vue engineering structure
This section has a lot of operations that just mount an API, with a partially parsed, partially skipped strategy, which will be bulk up in the next series
1.1 Pictures show the complete project catalog
1.2 Here are the files and directories we pay more attention to
| - vue2.6.0 - release | - flow / / about flow file directory It abandoned the poor | - scripts / / about the build script file directory | - SRC | - | compiler / / compile module - codegen / / Code generation | - directives / / instruction v - bind v - model v - on | - the parser / / ast syntax tree generation part of the core module | | - core / / components / / built-in component KeepAlive | - global - API / / the extend, assets, mixins, use), the observer, Util | - the instance / / render related and hook mount vue constructor defined life cycle Prototype chain instance methods on mount | - the realization of the observer / / response type | - util / / kit is mainly the debug, lang, Next-tick, options(merge strategy), props(props), Env running environment sniffer | - vdom / / virtual dom implementation | - platforms / / platform related web | | weex | - server / / server to render a single file to compile | - SFC / / | - Shared for js file / / hooks clear | - based toolkit and life cycle test / / test parts | - typesCopy the code
1.3 the config file
Various types of the output file configuration file, about the UMD, CommonJS, AMD can refer to this article You can also view the AMD, UMD, CMD, CommonJS, ES6module
const builds = {
// Runtime+compiler CommonJS build (CommonJS)
// This is the type of CJS nodeJS environment I will learn this time
'web-full-cjs-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'), // The corresponding entry file
dest: resolve('dist/vue.common.dev.js'),
format: 'cjs'.env: 'development'.alias: { he: './entity-decoder' },
banner
},
'web-full-cjs-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'), // The corresponding entry file
dest: resolve('dist/vue.common.prod.js'),
format: 'cjs'.env: 'production'.alias: { he: './entity-decoder' },
banner
}
}
Copy the code
1.3.1 Variable substitution during packaging
// Replace the variable value of the same name in the package, such as version is dynamic and specified by us at packaging time
// Get the version number in package.json, replacing vue.version = version in the code
// Switch class and tag class
// It is found after startup
// 1. __WEEX__
// 2. __WEEX_VERSION__
// 3. __VERSION__
// 4. Process.env. NEW_SLOT_SYNTAX // Whether to use new slot syntax
// 5.process.env.vbind_prop_dictate // Whether fast binding syntax is enabled
// 6. process.env.node_env // Dev =development prod=production removes unnecessary alarms and prompts
// built-in vars
const vars = {
__WEEX__:!!!!! opts.weex,__WEEX_VERSION__: weexVersion,
__VERSION__: version
}
Copy the code
1.3.2 Alias Settings
// Import... from 'core'
module.exports = {
vue: resolve('src/platforms/web/entry-runtime-with-compiler'), // Vue full version entry
compiler: resolve('src/compiler'), // Compiler entry
core: resolve('src/core'), // Vue core file
shared: resolve('src/shared'), / / toolkit
web: resolve('src/platforms/web'), // Web platform entry
weex: resolve('src/platforms/weex'), // WeeX platform entry
server: resolve('src/server'), / / SSR entrance
entries: resolve('src/entries'), //
sfc: resolve('src/sfc') //
}
Copy the code
2. Vue
The following uses occur when using VUE
Vue.mixins({})
new Vue({
created () {
// this}})Copy the code
So there must be a place to define Vue () {}, and we can start looking for Vue from the entry file in config
=> scripts/config web/entry-runtime-with-compiler // Package entry web/entry-runtime-with-compiler
=> src/platforms/web/entry-runtime-with-compiler.js // import Vue from './runtime/index'
=> src/platforms/web/runtime/index.js // import Vue from 'core/index'
=> src/core/index // import Vue from './instance/index'
=> src/core/instance/index // function Vue
Copy the code
2.1 the Vue statement
The Vue constructor declares the definition
function Vue (options) {
// In the test or development environment check if the instance is generated in the form of a new Vue, otherwise the alarm is generated because all subsequent operations are performed around the Vue instance
/ / new Vue ✔ ()
/ / the Vue (x)
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) // Instance execution methods we will learn later
}
Copy the code
Prototype API & Vue API
Vue’s global API wrapper is divided into instance prototype object mount and constructor API mount
2.2.1 initMixin
// The initialization method mounts vue.prototype. _init =function () {}
Copy the code
2.2.2 stateMixin
Data, props Data agent Settings
/** * Data hijacking this is also the core of Vue implementation principles. This is used for data proxy * All Vue instances such as this. XXX access data is called this._data.xxx * All Vue instances such as this. XXX are accessing this._props. XXX * hijack the data set to alert the operation to which this
const dataDef = {}
dataDef.get = function () { return this._data }
const propsDef = {}
propsDef.get = function () { return this._props }
if(process.env.NODE_ENV ! = ='production') {
dataDef.set = function () {
warn(
'Avoid replacing instance root $data. ' +
'Use nested data properties instead.'.this
)
}
propsDef.set = function () {
warn(`$props is readonly.`.this)}}Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
Copy the code
// Observer set, delete method
Vue.prototype.$set = set // Set the observation object
Vue.prototype.$delete = del // Delete the observed object
Vue.prototype.$watch // $watch on instance
Copy the code
2.2.3 eventsMixin
Vue.prototype.$on = function () {} // Add a listener
Vue.prototype.$once = function () {} // Add a one-time listener
Vue.prototype.$off = function () {} // Uninstall the listener
Vue.prototype.$emit = function () {} // Launch event
Copy the code
2.2.4 lifecycleMixin
Vue.prototype._update = function () {} // View updates focus on view component updates
Vue.prototype.$forceUpdate = function () {} The point of forcing updates is to force the observer to update accordingly
Vue.prototype.$destroy = function () {} // Destroy the current instance
Copy the code
2.2.5 renderMixin
Render related processing and API mount
/ / https://chunmu.github.io/mylife/vue-2.6.0/api.html#_1-nexttick
Vue.prototype.$nextTick = function () {} // View updates focus on view component updates please refer to
Vue.prototype._render = function () {} The point of forcing updates is to force the observer to update accordingly
Copy the code
2.2.6 installRenderHelpers
// The mount operation was not executed
export function installRenderHelpers (target: any) {
target._o = markOnce // Marks the once directive attribute
target._n = toNumber
target._s = toString
target._l = renderList // Render for loop
target._t = renderSlot
target._q = looseEqual
target._i = looseIndexOf
target._m = renderStatic // Render static content
target._f = resolveFilter
target._k = checkKeyCodes
target._b = bindObjectProps // Dynamic property binding
target._v = createTextVNode // Create a Text VNode node
target._e = createEmptyVNode // Create an empty VNode
target._u = resolveScopedSlots
target._g = bindObjectListeners
target._d = bindDynamicKeys //
target._p = prependModifier
}
Copy the code
2.3 initGlobalAPI
Note that the vue. prototype instance method is configured first, followed by the constructor API mount
2.3.1 agent config
/** * hijacks the set method of config configuration. Read-only objects should not modify vue. config directly but configure the fields ** / as needed in the passed parameters
const configDef = {}
configDef.get = (a)= > config
if(process.env.NODE_ENV ! = ='production') {
configDef.set = (a)= > {
warn(
'Do not replace the Vue.config object, set individual fields instead.')}}Object.defineProperty(Vue, 'config', configDef)
Copy the code
2.3.2 Global Config Parsing
// There are omissions in the field parsing of the config global configuration
export default ({
optionMergeStrategies: Object.create(null), // The configuration of the various merge strategies is best left unchanged unless you are familiar with the mechanism
// object.create (null) This creates relatively purer objects
silent: false.// Whether to keep silent to disable console.warn outputproductionTip: process.env.NODE_ENV ! = ='production'.// A reminder to control the development modedevtools: process.env.NODE_ENV ! = ='production'.// DevTools switch
performance: false.// Whether to record performance data such as vue rendering time and compile time records
errorHandler: null.// You can customize error handling methods such as collecting vue error reports
warnHandler: null.// You can customize warn handling methods such as collecting VUE WARN reports
ignoredElements: [], // Ignore compiled custom tags
keyCodes: Object.create(null), // set of key values
isReservedTag: no, // Whether a tag is kept globally
isReservedAttr: no, // Whether attributes are kept globally
isUnknownElement: no, // Unknown element
getTagNamespace: noop, // Get the label namespace
parsePlatformTagName: identity,
mustUseProp: no, // Whether a prop must be passed in such as the selct tag must receive the value attribute as a prop
async: true._lifecycleHooks: LIFECYCLE_HOOKS // Lifecycle hooks beforeCreate, created, beforeMount, Mounted, beforeUpdate, updated, beforeDestory, destroyed, activated, deactivated, errorCaptured, serverPrefetch
}: Config)
Copy the code
2.3.3 Vue. Util
Vue.util = {
warn, // There is a post-processing detail for formatting the vue instance call stack
extend, // Extend method for... In loop Value Set value traverses the prototype chain extension attribute Assign does not
/ / https://chunmu.github.io/mylife/vue-2.6.0/api.html#_1-mergeOptions
mergeOptions, // options merge policy new Vue(options)
defineReactive // Observer tool methods
}
Copy the code
Vue.prototype.set = function () {} // set
/ / https://chunmu.github.io/mylife/vue-2.6.0/api.html#_1-nexttick
Vue.prototype.delete = function () {} // delete
Vue.prototype.nextTick = function () {} // nextTick
Vue.prototype.observable = function () {} // observable
Copy the code
2.3.4 Vue.options Initialization
Initializes options on the constructor as the ancestor of all subsequent options
/** * Vue.options = { * components: {}, * directives: {}, * filters: {} * } * */
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type= > {
Vue.options[type + 's'] = Object.create(null)
})
Vue.options._base = Vue // _base => Vue
extend(Vue.options.components, builtInComponents) // The global built-in keep-alive component
Copy the code
2.3.5 initUse Vue. Use
The implementation of vue. use, which provides an aggregation of global or instance specific logic or APIS to operate Vue, standardizes plug-in installation
- Use Definition section
Use (plugin1).use(plugin2)
Vue.use = function (plugin: Function | Object) {
// Check whether the same plug-in is registered twice
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > - 1) {
return this
}
// additional parameters
const args = toArray(arguments.1)
// The initial position argument to pass install is this = Vue
args.unshift(this)
// Trying to install plug-ins through Install can be considered as the standard format recommended by Vue for plug-in installation
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
Copy the code
- The official documentation will be copied again to make it very clear what it can do
MyPlugin.install = function (Vue, options) {
// 1. Add a global method or attribute
Vue.myGlobalMethod = function () {
/ / logic...
}
// 2. Add global resources
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
/ / logic...}... })// 3. Inject component options
Vue.mixin({
created: function () {
/ / logic...}... })// 4. Add instance methods
Vue.prototype.$myMethod = function (methodOptions) {
/ / logic...}}Copy the code
2.3.6 initMixin Vue. Mixins
- Vue. Mixin definition
The essence is to call mergeOptions directly to merge mixins options, which involves a key point we need to pay attention to, options merge strategy
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
Copy the code
2.3.7 initExtend core implementation
Vue.extend = function () {} // Component extension core methods are then parsed in actual code
/** * Each instance constructor, including Vue, has a unique * cid. This enables us to create wrapped "child * constructors" for prototypal inheritance and cache them. * /
Vue.cid = 0
let cid = 1
/** * Class inheritance */
Vue.extend = function (extendOptions: Object) :Function {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
// options mount _Ctor for storage
// If you want extended options = targetOptions, continue to extend with this option next time
// There are already existing extension Vue class constructors that meet the criteria
// Cache the extended constructor
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
// Extensions are used to build components
const name = extendOptions.name || Super.options.name
if(process.env.NODE_ENV ! = ='production' && name) {
validateComponentName(name)
}
// VUe-like constructor
const Sub = function VueComponent (options) {
this._init(options) Call _init when the call constructor is son = new Sub()
}
// Rewrite Sub's prototype; constructor points to Vue, inheriting method properties on all Vue instance prototype chains
Sub.prototype = Object.create(Super.prototype)
// Call the constructor of the prototype chain to execute Sub or point to Vue
Sub.prototype.constructor = Sub
Sub.cid = cid++ // constructor ID skips 1... If you use statistics, you lose one day. The total is right
// Call mergeOptions top-level vue here
// super. options with _base will be inherited to Sub options
Sub.options = mergeOptions(
Super.options,
extendOptions
)
/ / specify the super
Sub['super'] = Super
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
initProps(Sub) // Set the props agent to access this.xxx = this._props
}
if (Sub.options.computed) {
initComputed(Sub) // Design to immediate corresponding section follow up
}
// allow further extension/mixin/plugin usage
// Inherit properties and methods from super
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
// The conponents filters in Vue are cache inherited
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
// It can register itself as a component
if (name) {
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
/ / sealing options
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
// Note that the superId is used to store the constructor after the super execution
cachedCtors[SuperId] = Sub
return Sub
}
Copy the code
2.3.8 initAssetRegisters
This is the definition of vue.component, vue.directive, vue.filter global API
The three sections of registration logic are mixed together to add unnecessary type judgments, but it needs to be consistent with ASSET_TYPES. I can’t think of a better solution for now, but it’s not too expensive and it’s not a performance problem. This resource level processing can be broken up separately because even if a new resource is introduced it is likely to have logical changes so adding one registration method is acceptable
ASSET_TYPES.forEach(type= > {
Vue[type] = function (id: string, definition: Function | Object) :Function | Object | void {
if(! definition) {// If the registered content exists, return the corresponding ID. After registration, it is stored in the {components: {}, filters: {}, directives: {}} collection in the corresponding location vue. options
/** * Vue.component('my-component') * => * Vue.options = { * ... , * components: { * 'my-component': MyComponent * } * } * */
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if(process.env.NODE_ENV ! = ='production' && type === 'component') {
// Verify the validity of the component name
validateComponentName(id)
}
if (type === 'component' && isPlainObject(definition)) {
// Use the name attribute defined inside the Component object to name components preferentially. If not, use the id used when registering components
definition.name = definition.name || id
// extend is used here, which we'll talk about later
definition = this.options._base.extend(definition)
}
// If it is a directive and the directive is configured as a method, the default binding and updating of the directive is to call that method
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
this.options[type + 's'][id] = definition
return definition
}
}
})
Copy the code
2.3.10 validateComponentName
Verify component name validity
// Unicode characters are generally supported, including Chinese mathematical symbols and emojis, but it is best not to play this way
if (!new RegExp(`^[a-zA-Z][\\-\\.0-9_${unicodeRegExp.source}] * $`).test(name)) {
warn(
'Invalid component name: "' + name + '". Component names ' +
'should conform to valid custom element name in html5 specification.')}// Whether to reserve words, reserve labels, or built-in slot components; these cannot be used; keep-alive, etc. (built-in components)
if (isBuiltInTag(name) || config.isReservedTag(name)) {
warn(
'Do not use built-in or reserved HTML elements as component ' +
'id: ' + name
)
}
Copy the code
3. Remaining mount
3.1 SSR server rendering is relevant
Let’s not focus on that for now
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
Copy the code
3.2 Vue version implantation
Vue.version = '__VERSION__' // Get the version number of the main project package.json
// Look for the code block in srcipts/config that sets the rullup variable to replace the corresponding string in the packaged project
// built-in vars
const vars = {
__WEEX__:!!!!! opts.weex,__WEEX_VERSION__: weexVersion,
__VERSION__: version
}
Copy the code
Utils mounts and presets platform-specific built-in components and platform-specific __patch__ methods
// install platform specific utils
Vue.config.mustUseProp = mustUseProp // Determine whether the props must be introduced forcibly
Vue.config.isReservedTag = isReservedTag // Determine whether to keep the label
Vue.config.isReservedAttr = isReservedAttr // Determine whether to keep attributes
Vue.config.getTagNamespace = getTagNamespace // Get the namespace
Vue.config.isUnknownElement = isUnknownElement // Unrecognized component name
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives) // v-model&v-show
extend(Vue.options.components, platformComponents) // Transition&TransitionGroup
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop // Distribute the core of render communication
Copy the code
3.4 Mount Method Mount API core methods
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
) :Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
Copy the code
3.3.5 Warm hints related to development and Initialization of DevTools
// devtools global hook
/* istanbul ignore next */
if (inBrowser) {
setTimeout((a)= > {
if (config.devtools) {
if (devtools) {
devtools.emit('init', Vue) / / devtools use
} else if( process.env.NODE_ENV ! = ='production'&& process.env.NODE_ENV ! = ='test'
) {
console[console.info ? 'info' : 'log'] ('Download the Vue Devtools extension for a better development experience:\n' +
'https://github.com/vuejs/vue-devtools')}}if(process.env.NODE_ENV ! = ='production'&& process.env.NODE_ENV ! = ='test'&& config.productionTip ! = =false && // productionTip in config of new Vue({config})
typeof console! = ='undefined'
) {
console[console.info ? 'info' : 'log'] (`You are running Vue in development mode.\n` +
`Make sure to turn on production mode when deploying for production.\n` +
`See more tips at https://vuejs.org/guide/deployment.html`)}},0)}Copy the code
conclusion
The above is some initialization process done after the introduction of Vue, after such a process, the global API, instance API has a prototype, I have analyzed the above process has the execution process, as well as some simple method definition, but the core and key points are still not involved. In the next installment, we’ll look at the template parsing process for Vue