preface
Almost one in the past, from the practice to become a full member has in the past seven or eight months, the content of the work feeling more and more boring, a day to move the brick to let his body be emptied their thinking, go on like this, I feel I will waste, so that this article is to force yourself to think about, and to adjust their own state.
In addition, the new understanding of the source code is not necessarily correct, but also hope that you give directions.
The preparatory work
The address of the project: https://github.com/vuejs/vue.git
Version: 2.6.14
Project Structure:
A word:
As we know, the.vue file will become a JS object after being packaged by the Vue-loader of Webpack, and this object will have a render attribute from the template of the.vue file. That’s what compilers do.
In the package. The json:
We’ll see that vue is packaged with rollup, so we need to install rollup globally, then install dependencies, and finally run NPM run dev. [Note: I added –sourcemap in dev so that debug can see where in the source file later]
{
"name": "vue"."version": "2.6.14"."main": "dist/vue.runtime.common.js"."module": "dist/vue.runtime.esm.js"."sideEffects": false."scripts": {
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"."dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-cjs-dev"."dev:esm": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-esm". }},Copy the code
As you can see, NPM run dev executes the scripts/config.js file and specifies the web-full-dev parameter.
scripts/config.js
const aliases = require('./alias')
const resolve = p= > {
const base = p.split('/') [0]
if (aliases[base]) {
return path.resolve(aliases[base], p.slice(base.length + 1))}else {
return path.resolve(__dirname, '.. / ', p)
}
}
const builds = [
...
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.js'),
format: 'umd'.env: 'development'.alias: { he: './entity-decoder' },
banner
},
...
]
Copy the code
alias.js
const path = require('path')
const resolve = p= > path.resolve(__dirname, '.. / ', p)
module.exports = {
vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
compiler: resolve('src/compiler'),
core: resolve('src/core'),
shared: resolve('src/shared'),
web: resolve('src/platforms/web'),
weex: resolve('src/platforms/weex'),
server: resolve('src/server'),
sfc: resolve('src/sfc')}Copy the code
The web/entry-runtime-with-compiler.js file is obviously the entry file, but looking back at our directory, it doesn’t seem to have the Web folder either. Note that resolve should do something about it, and then examine the code:
resolve('web/entry-runtime-with-compiler.js')
-> base = 'web';
-> alias[web] = 'src/platforms/web';
-> path.resolve('src/platforms/web/entry-runtime-with-compiler.js')
Copy the code
Ohhhhh!!!!!! Finally found the entrance!! We can finally put an end to the long preparations.
entry-runtime-with-compiler.js
What’s up??
Render (el, render, el); render (el, el, el); render (el, el, el); The sequence is clearly indicated in the code.
/* @flow */
import Vue from "./runtime/index";
const idToTemplate = cached((id) = > {
const el = query(id);
return el && el.innerHTML;
});
// Extend the $mount method
const mount = Vue.prototype.$mount;
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
) :Component {
el = el && query(el);
const options = this.$options;
// resolve template/el and convert to render function
// Check to see if the render option is available, then mount it directly
// If not, check to see if the template option exists
// If template starts with #, take its innerHTML
// If template is an element, take its innerHTML
// Otherwise return the instance directly
//
// If not, check to see if there is an EL option
// If the el has outerHTML, fetch its outerHTML
// If there is and el has no outterHTML, create a div container around it and take the innerHTML of the container
// Template = getHTML(el)
// With the above steps, if the template option exists, compile it to get the render function
if(! options.render) {let template = options.template;
if (template) {
if (typeof template === "string") {
if (template.charAt(0) = = ="#") { template = idToTemplate(template); }}else if (template.nodeType) {
template = template.innerHTML;
} else {
return this; }}else if (el) {
template = getOuterHTML(el);
}
if (template) {
// Compile to convert template to render function
const { render, staticRenderFns } = compileToFunctions(
template,
{
outputSourceRange: process.env.NODE_ENV ! = ="production",
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments,
},
this
);
// Get the render function assigned to the option for future useoptions.render = render; options.staticRenderFns = staticRenderFns; }}// Perform the default mount
return mount.call(this, el, hydrating);
};
/** * Get outerHTML of elements, taking care * of SVG elements in IE as well. */
function getOuterHTML(el: Element) :string {
if (el.outerHTML) {
return el.outerHTML;
} else {
const container = document.createElement("div");
container.appendChild(el.cloneNode(true));
return container.innerHTML;
}
}
Vue.compile = compileToFunctions;
export default Vue;
Copy the code
Conclusion:
- Packaged entry
- Dealing with compilation
src/platforms/web/runtime/index.js
We noticed above that Vue was introduced from this file, so there should be information about Vue here, so let’s move on.
/* @flow */
import Vue from 'core/index'
import config from 'core/config'
import { mountComponent } from 'core/instance/lifecycle'
// install platform patch function
// Install the update function, which converts vDOM to DOM
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method
// Implement a $mount method calling mountComponent
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
) :Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
export default Vue
Copy the code
Conclusion:
- Install patch function to achieve VDOM -> DOM
- Achieve $mount
src/core/index
The Vue of the above file is introduced from this file. This time, it has been turned into core. It should be the end of it.
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'
// Initialize the global API: vue. use/component/set...
initGlobalAPI(Vue)
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
})
Vue.version = '__VERSION__'
export default Vue
Copy the code
Conclusion:
- Initialize the global API
src/core/instance/index
Heck,, it must be the end of instance.
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')}// init, but where is _init?
this._init(options)
}
// Initialize instance methods and properties
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
Copy the code
Confused:
- Constructor
_init
Where did the method come from?
Conclusion:
- Declaration of the Vue constructor
- Initialize the properties and methods of the instance
src/core/instance/init
With that in mind, I clicked on the initMixin definition and found the answer.
export function initMixin(Vue: Class<Component>) {
Vue.prototype._init = function (options? :Object) {
const vm: Component = this;
// a uid
vm._uid = uid++;
// a flag to avoid this being observed
vm._isVue = true;
// merge options
// User options are merged with system default 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
/ / initialization
vm._self = vm;
// Declare period-dependent attributes to initialize $parent
initLifecycle(vm);
// Customize listening for component events
initEvents(vm);
// Slot processing, $createElement
initRender(vm);
// Call the hook function that declares the period
callHook(vm, "beforeCreate");
// Next is the component data and state initialization
initInjections(vm); // resolve injections before data/props
// data/props/methods/computed/watch
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, "created");
// Mount with el option
if(vm.$options.el) { vm.$mount(vm.$options.el); }}; }Copy the code
Conclusion:
- Defines the initialization process for Vue instances
- Initialization operations are performed in sequence
- InitLifecycle – Initializes lifecycle related properties,
$parent
/$root
/$ref
/$children
Etc. - InitEvents – Initializes the properties and functions that custom events listen for
- InitRender – slot as well
$createElement
The processing of - callHook(vm, “beforeCreate”); – call
beforeVreate
- Initinjection – Injection Injection
- InitState – Initialization
data
/props
/methods
/computed
/watch
- InitProvide – Handles the Provide option
- CallHook (VM, “created”) – Calls
created
- If you have one of the choices
el
, the call$mount
- InitLifecycle – Initializes lifecycle related properties,
summary
Through the above series of tracking, we have a basic understanding of the process of New Vue, and at this time we can learn the answers to some common questions:
Q: What does new Vue({el: “#app”}) do?
A: Perform initialization -> get a root instance -> perform mount function -> get render function and execute -> generate VDOM -> convert vDOM to DOM via patch function -> append dom to # EL
Q: When the el, Render, and template options appear in an option, which one will be executed first?
Render > template > el, render function first
Q: Can injection and data be accessed in beforeCreate and Created?
A: Neither can be accessed in beforeCreate and neither can be accessed in Created