Vue源码探究之虚拟节点的实现

页面初始化的所有状态都准备就绪之后,下一步就是要生成组件相应的虚拟节点—— VNode 。初次进行组件初始化的时候, VNode 也会执行一次初始化并存储这时创建好的虚拟节点对象。在随后的生命周期中,组件内的数据发生变动时,会先生成新的 VNode 对象,然后再根据与之前存储的旧虚拟节点的对比来执行刷新页面 DOM 的操作。页面刷新的流程大致上可以这样简单的总结,但是其实现路程是非常复杂的,为了深入地了解虚拟节点生成和更新的过程,首先来看看 VNode 类的具体实现。

VNode 类

VNode 类的实现是支持页面渲染的基础,这个类的实现并不复杂,但无论是创建Vue组件实例还是使用动态JS扩展函数组件都运用到了渲染函数 render ,它充分利用了 VNode 来构建虚拟DOM树。

// 定义并导出VNode类 export default class VNode { // 定义实例属性 tag: string | void; // 标签名称 data: VNodeData | void; // 节点数据 children: ?Array<VNode>; // 子虚拟节点列表 text: string | void; // 节点文字 elm: Node | void; // 对应DOM节点 ns: string | void; // 节点命名空间,针对svg标签的属性 context: Component | void; // rendered in this component's scope // 组件上下文 key: string | number | void; // 节点唯一键 componentOptions: VNodeComponentOptions | void; // 虚拟节点组件配置对象 componentInstance: Component | void; // component instance // 组件实例 parent: VNode | void; // component placeholder node // 组件占位符节点 // 严格内部属性,有些属性是服务器渲染的情况使用的,暂时还不了解 // strictly internal // 是否包含原始HTML。只有服务器端会使用 raw: boolean; // contains raw HTML? (server only) // 是否静态节点,静态节点将会被提升 isStatic: boolean; // hoisted static node // 是否在根节点插入,进入转换检查所必需的 isRootInsert: boolean; // necessary for enter transition check // 是否空注释占位符 isComment: boolean; // empty comment placeholder? // 是否拷贝节点 isCloned: boolean; // is a cloned node? // 是否一次性节点 isOnce: boolean; // is a v-once node? // 异步组件工厂方法 asyncFactory: Function | void; // async component factory function // 异步源 asyncMeta: Object | void; // 是否异步占位符 isAsyncPlaceholder: boolean; // 服务器端上下文 ssrContext: Object | void; // 功能节点的实际实例上下文 fnContext: Component | void; // real context vm for functional nodes // 方法配置选项,只在服务器渲染使用 fnOptions: ?ComponentOptions; // for SSR caching // 方法作用域id fnScopeId: ?string; // functional scope id support // 构造函数,参数均可选,与上面定义对应 constructor ( tag?: string, data?: VNodeData, children?: ?Array<VNode>, text?: string, elm?: Node, context?: Component, componentOptions?: VNodeComponentOptions, asyncFactory?: Function ) { // 实例初始化赋值 this.tag = tag this.data = data this.children = children this.text = text this.elm = elm this.ns = undefined this.context = context this.fnContext = undefined this.fnOptions = undefined this.fnScopeId = undefined this.key = data && data.key this.componentOptions = componentOptions this.componentInstance = undefined this.parent = undefined this.raw = false this.isStatic = false this.isRootInsert = true this.isComment = false this.isCloned = false this.isOnce = false this.asyncFactory = asyncFactory this.asyncMeta = undefined this.isAsyncPlaceholder = false } // 定义child属性的取值器 // 已弃用:用于向后compat的componentInstance的别名 // DEPRECATED: alias for componentInstance for backwards compat. /* istanbul ignore next */ get child (): Component | void { return this.componentInstance } } // 定义并导出createEmptyVNode函数,创建空虚拟节点 export const createEmptyVNode = (text: string = '') => { // 实例化虚拟节点 const node = new VNode() // 设置节点文字为空,并设置为注释节点 node.text = text node.isComment = true // 返回节点 return node } // 定义并导出createTextVNode函数,创建文字虚拟节点 export function createTextVNode (val: string | number) { // 置空实例初始化的标签名,数据,子节点属性,只传入文字 return new VNode(undefined, undefined, undefined, String(val)) } // 优化浅拷贝 // 用于静态节点和插槽节点,因为它们可以在多个渲染中重用, // 当DOM操作依赖于它们的elm引用时,克隆它们可以避免错误 // optimized shallow clone // used for static nodes and slot nodes because they may be reused across // multiple renders, cloning them avoids errors when DOM manipulations rely // on their elm reference. // 定义并导出cloneVNode函数,拷贝节点 export function cloneVNode (vnode: VNode): VNode { // 拷贝节点并返回 const cloned = new VNode( vnode.tag, vnode.data, vnode.children, vnode.text, vnode.elm, vnode.context, vnode.componentOptions, vnode.asyncFactory ) cloned.ns = vnode.ns cloned.isStatic = vnode.isStatic cloned.key = vnode.key cloned.isComment = vnode.isComment cloned.fnContext = vnode.fnContext cloned.fnOptions = vnode.fnOptions cloned.fnScopeId = vnode.fnScopeId cloned.asyncMeta = vnode.asyncMeta cloned.isCloned = true return cloned }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/f6766c8e3d53ed9581a8fd0182821fb4.html