$mount()
方法完整版中会先调用src/platforms/web/entry-runtime-with-compiler.js
中重写的$mount()
(即进行模板编译),其中:
options
中是否有render
,如果没有传递render
,调用compileToFunctions()
,生成render()函数options.render = render
然后调用原来的$mount()
(在src/platform/web/runtime/index.js
中定义),其中会调用mountComponent(this, el, hydrating)
beforeMount:callHook
(vm, ‘beforeMount’)`updateCompOnent= () => { vm._update(vm._render(), hydrating) }
vm._render()
生成虚拟DOM(vm._render()
定义在src/core/instance/render.js
中)vm._update()
将虚拟DOM转换成真是DOM(vm._update()
定义在src/core/instance/lifecycle.js
中)Watcher
实例,把updateComponent
传递进去,updateComponent
是在Watcher
中通过Watcher
的get()
实例方法执行的mounted
;返回实例return vm
首次渲染创建的是渲染Watcher
,创建完Watcher
实例后会调用一个get()方法,get()
中会调用updateComponent()
,updateComponent()
中会调用vm._update(VNode. hydrating)
,而其中的VNode
是调用vm._render()
创建VNode
vm._render()
的执行过程:
options
中的render
函数:const { render, _parentVnode } = vm.$options
render.call(vm.renderProxy, vm.$createElement)
(这个render
是创建实例new Vue()
时传入的render()
,或者是编译template
生成的render()
),最后返回VNode
然后执行vm._update(VNode, hydrating)
,其中:
vm.__patch__(vm.$el, vnode)
挂载真实DOM
vm.__patch__
的返回值记录在vm.$el
中Vue使用观察者模式来对其数据进行响应式处理,过程如下:
在创建 Vue
实例时,调用的 this._init(options)
中会执行 initInjections(vm)
、initState(vm)
,这两个方法中分别会对 inject
的成员、本实例的 props
和 data
进行响应式处理
initInjections(vm)
中会遍历 inject
的成员,通过 defineReactive(vm, key, result[key])
将每个成员转换成响应式属性(即劫持 getter/setter
)
initState(vm)
中调用 initProps(vm, opts.props)
,其中编辑 props
属性,通过 defineReactive(props, key, value)
将属性转换成getter
、setter
,然后存入 props
(也是 vm._props
)中
initState(vm)
中调用 initData(vm, opts.props)
,其中调用 observe(data, true /* asRootData */)
对 data
进行响应式处理
defineReactive(obj, key, val, customSetter?, shallow?)
key
换成响应式属性,即其劫持 getter/setter
( Object.defineProperty( obj, key, { ..., get(){...}, set(){...} } )
)key
生成一个 Dep
对象 dep
(const dep = new Dep()
);dep
会在 getter
中收集依赖(即相应属性的 Watcher
对象),在 setter
中调用 dep.notify()
派发更新;observe(val)
(let childOb = !shallow && observe(val)
),若 val
是个对象,则会为这个对象创建一个 Observer
对象,并返回。observe (value, asRootData?)
value
是否是对象,不是对象就返回Observer
对象 :ob = new Observer(value)
,并返回 ob
(在 defineReactive
中,这个返回的 ob
会在 getter
中收集依赖相应的依赖)Observer
构造函数中,会新建一个 Dep
对象 dep
,这里的 dep
是为传入的 观测对象(进行响应式处理的对象)收集依赖(Watcher
);与 defineReactive
中的 dep
不一样Observer
实例挂载到 观测对象 的 __ob__
属性:def(value, '__ob__', this)
value
不是数组,则执行 this.walk(value)
方法,遍历 value
中的每一个属性,然后调用 defineReactive(obj, keys[i])
value
是数组 Vue
并没有对数组对象的索引调用 defineReactive
来生成 getter/setter
,而是重写了原生数组中会更改原数组的方法,调用这些新方法后,数组对象对应的 dep
对象会调用 dep.notify 方法来驱动视图的更新'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'
this.observeArray(value)
,作用是当数组中的元素存在对象时,为数组中的每一个对象创建一个 observer
实例至此,创建观察者结束
$mount()
),会执行 mountComponent
方法,其中会创建一个渲染 Watcher
对象Watcher
对象构造函数的最后会执行 get()
方法,get()
中会先执行 pushTarget(this)
,pushTarget
中则会将 Dep.target 设置为该 watcher
(Dep.target = target
)this.getter.call(vm, vm)
,即执行了 vm._update(vm._render(), hydrating)
,而 vm._render()
中执行 render.call(vm._renderProxy, vm.$createElement)
以生成 vnode
,这个生成 VNode
的过程中,会触发 相应的响应式数据的 getter
,然后其中的 dep.depend()
则会收集当前实例 watcher
当生成完 VNode
后,就完成了响应式数据的的依赖收集
但修改某个响应式数据时,会触发该数据的 setter
childOb = !shallow && observe(newVal)
,将新增也进行响应式处理dep.notify()
派发更新,notify
会调用每个订阅者(watcher
)的 update
方法实现更新watcher
的 update
中使用 queueWatcher()
判断 watcher
是否被处理,若没有,则把 watcher
添加进 queue
队列中,并调用 flushSchedulerQueue()
flushSchedulerQueue()
中先触发 beforeUpdate
钩子函数,然后调用 watcher.run()
watcher.run()
中会调用 get()
方法,get
中执行 getter
,而 getter
就是传入的 updateComponent
方法,updateComponent
中执行 vm._update(vm._render(), hydrating)
,如此就完成了视图的更新flushSchedulerQueue()
后续代码中 还原更新步骤的初始状态、触发 actived
钩子函数、触发 updated
钩子函数Key
是用来优化 Diff
算法的。Diff
算法核心在于同层次节点比较,Key
就是用于在比较同层次新、旧节点时,判断其是否相同。
Key
一般用于生成一列同类型节点时使用,这种情况下,当修改这些同类型节点的某个内容、变更位置、删除、添加等时,此时界面需要更新视图,Vue
会调用 patch
方法通过对比新、旧节点的变化来更新视图。其从根节点开始若新、旧 VNode
相同,则调用 patchVnode
patchVnode
中若新节点没有文本,且新节点和旧节点都有有子节点,则需对子节点进行 Diff
操作,即调用 updateChildren
,Key
就在 updateChildren
起了大作用
updateChildren
中会遍历对比上步中的新、旧节点的子节点,并按 Diff 算法通过 sameVnode
来判断要对比的节点是否相同
Key
,则此时的每个新、旧子节点在执行 sameVnode
时会判定相同,然后再次执行一次 patchVnode
来对比这些子节点的子节点Key
,当执行 sameVnode
Key
不同 sameVnode
返回 false
,然后执行后续判断;Key
相同 sameVnode
返回 true
,然后再执行 patchVnode 来对比这些子节点的子节点即,使用了 Key
后,可以优化新、旧节点的对比判断,减少了遍历子节点的层次,少使用很多次 patchVnode
在完整版 Vue 中,src/platforms/web/entry-runtime-with-compiler.js
里先保留 Vue
实例的 mount
方法,然后重写该mount
方法,然后重写该mount
方法,这个重写的方法就是完整版 Vue
中的模板编译器,其中在 $options
上挂载了模板编译后生成的 render
函数。
$options
上的 render
函数是由 compileToFunctions(template, options, vm)
这个函数生成,即将 template
转换成了 render
函数。所以这里就是完成了首次加载时对模板的编译。
这里梳理下生成 render
函数的相关函数的调用过程
调用 compileToFunctions(template, options, vm)
// src/platforms/web/entry-runtime-with-compiler.js
// 把 template 转换成 render 函数
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
compileToFunctions
是由 src/platforms/web/compiler/index.js 里的 createCompiler(baseOptions)
生成的。baseOptions
里是一些关于指令、模块、HTML标签相关的方法,这里不予关心。
所以 第一步 中 compileToFunctions
是这里的 createCompiler
返回的函数。
// src/platforms/web/compiler/index.js
import { baseOptions } from './options'
import { createCompiler } from 'compiler/index'
const { compile, compileToFunctions } = createCompiler(baseOptions)
export { compile, compileToFunctions }
createCompiler
来自于 src/compiler/index.js,其中调用了 createCompilerCreator(function baseCompile (template, options))
方法
所以 第二步 中的 createCompiler
来自于这里的 createCompilerCreator
返回的函数,createCompilerCreator
中传入 函数 baseCompile
作为参数
那么 第一步 中的 compileToFunctions
就是这里的 createCompilerCreator
返回的函数执行(即执行 createCompiler(baseOptions)
)后返回的函数
// src/compiler/index.js
export const createCompiler = createCompilerCreator(function baseCompile (
template: string,
options: CompilerOptions
): CompiledResult {
// 把模板转换成 ast 抽象语法树
// 抽象语法树,用来以树形的方式描述代码结构
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
// 优化抽象语法树
optimize(ast, options)
}
// 把抽象语法树生成字符串形式的 js 代码
const code = generate(ast, options)
return {
ast,
// 渲染函数
render: code.render,
// 静态渲染函数,生成静态 VNode 树
staticRenderFns: code.staticRenderFns
}
})
createCompilerCreator
来自于 src/compiler/create-compiler.js
// src/compiler/create-compiler.js
export function createCompilerCreator (baseCompile: Function): Function {
return function createCompiler (baseOptions: CompilerOptions) {
function compile (
template: string,
options?: CompilerOptions
): CompiledResult {
const finalOptiOns= Object.create(baseOptions)
....
const compiled = baseCompile(template.trim(), finalOptions)
...
return compiled
}
return {
compile,
compileToFunctions: createCompileToFunctionFn(compile)
}
}
}
可以看出这个函数返回了 createCompiler(baseOptions)
函数,则往上推可知 第二步 中的
// src/platforms/web/compiler/index.js
const { compile, compileToFunctions } = createCompiler(baseOptions)
其实就是执行的这里的 function createCompiler (baseOptions: CompilerOptions){...}
,这个 createCompiler
函数里返回了方法 compile、compileToFunctions
方法 compile
中执行了传入的函数参数 baseCompile
,这个 baseCompile
是 第三步 中传入的,其返回值为 ast、render、staticRenderFns
而方法 compileToFunctions
正是 第一步 中调用的 compileToFunctions(template, options, vm)
,其来自于 createCompileToFunctionFn(compile)
createCompileToFunctionFn
来自于 src/compiler/to-function.js
// src/compiler/to-function.js
export function createCompileToFunctionFn (compile: Function): Function {
return function compileToFunctions (
template: string,
options?: CompilerOptions,
vm?: Component
): CompiledFunctionResult {
...
// 1. 读取缓存中的 CompiledFunctionResult 对象,如果有直接返回
const key = options.delimiters
? String(options.delimiters) + template
: template
if (cache[key]) {
return cache[key]
}
// 2. 把模板编译为编译对象(render, staticRenderFns),字符串形式的js代码
const compiled = compile(template, options)
// 3. 把字符串形式的js代码转换成js方法
res.render = createFunction(compiled.render, fnGenErrors)
res.staticRenderFns = compiled.staticRenderFns.map(code => {
return createFunction(code, fnGenErrors)
})
// 4. 缓存并返回res对象(render, staticRenderFns方法)
return (cache[key] = res)
}
}
createCompileToFunctionFn
返回函数 compileToFunctions
,即 第四步 中 createCompiler
函数返回的 compileToFunctions
,所以是 第一步 中调用的 compileToFunctions
就是在执行这里的 compileToFunctions
。
src/platforms/web/entry-runtime-with-compiler.js
中的 compileToFunctions(template, options, vm)
src/compiler/to-function.js
中的 compileToFunctions
,compileToFunctions
中调用 compile(template, options)
src/compiler/create-compiler.js
中的 compile
,compile
中调用 baseCompile(template.trim(), finalOptions)
src/compiler/index.js
传入 createCompilerCreator
中的函数参数 baseCompile(template, options),返回 ast、render、staticRenderFns
src/compiler/create-compiler.js
中 const compiled = { ast、render、staticRenderFns }
,返回 compiled
src/compiler/to-function.js
中返回 res
,即返回 render, staticRenderFns
方法src/platforms/web/entry-runtime-with-compiler.js
获取 render、staticRenderFns