- 创建Vue实例:在创建Vue实例时,会执行Vue的初始化逻辑,包括初始化生命周期、事件、嵌套组件等。Vue实例构造函数的内部实现,会对选项对象进行合并处理,最终得到一个完整的选项对象。
const vm = new Vue({
el: '#app',
data: { message: 'Hello Vue!' },
methods: {
greet() {
console.log(this.message);
}
}
})
- 解析模板并生成渲染函数:在Vue.js中,HTML模板通过Vue compile(编译)生成渲染函数,渲染函数是一个纯函数,即传入相同的数据,输出的内容永远是相同的,它会把数据编译成虚拟DOM树,并根据数据变化生成新的虚拟DOM树。
const template = `<div>{{ message }}</div>`;
const compiler = compileToFunctions(template);
function compileToFunctions(template) {
const compiled = compile(template);
const res = {
render: new Function(compiled.render),
staticRenderFns: []
}
return res;
}
- 创建虚拟DOM:在Vue.js中,虚拟DOM是在渲染函数运行期间创建的,当数据变化时,新的虚拟DOM树会通过Vue.js内部的diff算法,计算出需要更新的DOM节点,并按照指定的顺序,逐个更新到DOM树中。
function createElm(vnode) {
const { tag, data, children } = vnode;
if (typeof tag === 'string') {
vnode.elm = document.createElement(tag);
if (data) {
for (let key in data) {
vnode.elm.setAttribute(key, data[key]);
}
}
if (children) {
createChildren(vnode, children);
}
} else if (typeof tag === 'function') {
createComponent(vnode);
}
return vnode.elm;
}
function createChildren(vnode, children) {
for (let i = 0; i < children.length; i++) {
vnode.elm.appendChild(createElm(children[i]));
}
}
- 执行Watcher:Watcher是Vue.js中的重要概念,它是连接数据和视图的桥梁,在Vue.js中,所有的数据都是响应式的,当数据发生变化时,Watcher会收到通知,然后触发自身的更新渲染。Watcher的核心是收集依赖和派发更新,它会在模板中察看所有使用该数据的DOM节点,并将其记录在自身的依赖列表中,当数据变化时,会重新计算、收集依赖列表并派发更新。
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm
this.expOrFn = expOrFn
this.cb = cb
this.value = this.get()
}
get() {
Dep.target = this
const value = this.expOrFn.call(this.vm)
Dep.target = null
return value
}
update() {
const newValue = this.expOrFn.call(this.vm)
const oldValue = this.value
if (newValue !== oldValue) {
this.value = newValue
this.cb(newValue, oldValue)
}
}
}
- 更新DOM:Vue.js中更新DOM的方式有两种,一种是重新渲染整个组件,另一种是只更新和渲染变化的部分。当进行全量更新时,Vue.js会为新的虚拟DOM生成一个新的DOM树,并用新的DOM树去替换旧的DOM树,这是一种全量更新。而针对变量量变化,Vue.js会找出发生变化的部分,然后只重新渲染发生变化的部分。这个过程是由虚拟DOM进行渲染的,它通过对比旧的虚拟DOM树和新的虚拟DOM树,找出它们之间的差异来确定需要重新渲染的部分
function patch(oldVnode, vnode) {
if (typeof vnode === 'undefined') {
return
}
const isRealElement = oldVnode.nodeType !== undefined
if (!isRealElement && sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode)
} else {
const elm = createElm(vnode)
const parent = oldVnode.parentNode
parent.insertBefore(elm, oldVnode.nextSibling)
parent.removeChild(oldVnode)
}
}
function patchVnode(oldVnode, vnode) {
if (oldVnode === vnode) return
const elm = vnode.elm = oldVnode.elm
if (vnode.tag) {
if (oldVnode.tag !== vnode.tag) {
elm.innerHTML = vnode.tag
} else {
const newChildren = vnode.children || []
const oldChildren = oldVnode.children || []
newChildren.forEach((child, i) => {
patch(oldChildren[i], child)
})
}
}
}