数据驱动
- 数据驱动是Vue的核心思想之一,指视图由数据驱动生成,通过修改数据来实现了对视图的修改,而非直接操作DOM。
- DOM变成了数据的映射,我们把重点放在了数据的逻辑处理上,提高了开发效率。
- VirtualDOM就是一个js对象去描述一个DOM节点,在Vue中是用VNode这个class去描述。
Vue的挂载过程
挂载的目标就是把模板渲染成为最终的DOM。
主要是分为 render 和 update 两个过程。
$mount () -> mountComponent () -> vm._render () + vm._uptate ()
1、Render
Vue的_render方法是vue实例的一个私有方法,作用是生成一个VNode。
(src/core/instance/render.js)
在 initRender () 中定义了两种方法,vm._c ⽅法,它是被模板编译成的 render 函数使⽤,⽽vm.$createElement 是⽤户⼿写 render ⽅法使⽤的, 这俩个⽅法⽀持的参数相同, 并且内部都调⽤了 createElement ⽅法
export function initRender (vm: Component) {
// bind the createElement fn to this instance
// so that we get proper render context inside it.
// args order: tag, data, children, normalizationType, alwaysNormalize
// internal version is used by render functions compiled from templates
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
// normalization is always applied for the public version, used in
// user-written render functions.
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
}
}
_createElement ()
1、参数
export function _createElement (
context: Component, //VNode的上下文环境
tag?: string | Class<Component> | Function | Object, //标签 字符串类型、Component类型/
data?: VNodeData, //VNode的数据
children?: any,//VNode的子节点,为任意类型
normalizationType?: number//规范化类型
): VNode | Array<VNode> {
.... }
2、重点流程
- 子节点的规范化
- 由于Virtual DOM是树状结构,一个VNode都可能有很多个子节点,这些子节点也应该是VNode类型。 要将类型为any的 children 规范为VNode类型。
- 根据 normalizationType 的不同分别调用 normalizeChildren 和 simpleNormalizeChildren。
- 其中simpleNormalizeChildren 是用Array.prototype.concat 将数组拉平为一维数组,深度只有一层。
// 1. When the children contains components - because a functional component
// 返回的是一个数组而不是一个根节点
// normalization is needed - if any child is an Array, we flatten the whole 返回的是一个数组而不是一个根节点
// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep
// because functional components already normalize their own children.
export function simpleNormalizeChildren (children: any) {
for (let i = 0; i < children.length; i++) {
if (Array.isArray(children[i])) {
return Array.prototype.concat.apply([], children)
}
}
return children
}
- 当子节点包含生成嵌套数组的构造函数时
- 当编译 template, slot, v-for 这些标签或者是用户自定义的render函数
// 2. When the children contains constructs that always generated nested Arrays,
// e.g. <template>, <slot>, v-for, or when the children is provided by user
// with hand-written render functions / JSX. In such cases a full normalization
// is needed to cater to all possible types of children values.
export function normalizeChildren (children: any): ?Array<VNode> {
return isPrimitive(children)
? [createTextVNode(children)]
: Array.isArray(children)
? normalizeArrayChildren(children)
: undefined
}
如果是子节点一个基础类型,就调用createTextVNode 创建一个文本节点的VNode。如是一个数组类型就调用 normalizeArrayChildren
normalizeArrayChildren()
- 参数是子节点 和 嵌套的索引
- 该方法的主要逻辑就是遍历子节点,取到单个节点 c ,接着判断 c 的类型,如果是数组就递归处理这个节点;如果是一个基础类型,则通过createTextVNode方法转换为VNode类型;否则就是VNode类型。
- 经过对子节点的规范化处理, 原来类型为any的children就成了一个VNode节点集合。
function normalizeArrayChildren (children: any, nestedIndex?: string): Array<VNode> {
const res = []
let i, c