之前说了vue是数据驱动的,挂载($mount)之后,vue将要通过render将实例渲染为vnode,即虚拟dom;
vnode和dom的区别
先看以下代码段,了解一下VNode的具体结构:
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
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
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
devtoolsMeta: ?Object; // used to store functional render context for devtools
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
}
// DEPRECATED: alias for componentInstance for backwards compat.
/* istanbul ignore next */
get child (): Component | void {
return this.componentInstance
}
}
通过上面VNdode类,我们能发现虚拟dom跟真实dom有着明显的区别,真是dom有着各种方法,事件......,而虚拟dom只有各种属性,包括节点名、子节点、键值、标签、内容、父节点等等,这便是虚拟dom为什么会更快的原因了,也是为什么我们直接操作dom会更”昂贵“的原因;因为每当我们操作dom的时候除了各种属性外,它还会生成各种方法,事件等,而虚拟只生成了属性,这便是数据驱动的优势,我们通过改变数据来改变view;
render是怎么生成虚拟dom
在render方法下调用了createElement方法,该方法传入多个参数,再调用_createElement方法,仔细看代码能发现,其实它们传入的参数是一样的;
export function createElement (
context: Component,
tag: any,
data: any,
children: any,
normalizationType: any,
alwaysNormalize: boolean
): VNode | Array<VNode> {
if (Array.isArray(data) || isPrimitive(data)) {
normalizationType = children
children = data
data = undefined
}
if (isTrue(alwaysNormalize)) {
normalizationType = ALWAYS_NORMALIZE
}
return _createElement(context, tag, data, children, normalizationType)
}、
export function _createElement (
context: Component,
tag?: string | Class<Component> | Function | Object,
data?: VNodeData,
children?: any,
normalizationType?: number
): VNode | Array<VNode>、
越看越觉得尤雨溪厉害,这里_createElement才是真正创建vnode的入口,但是通过createElement让传入的参数类型更加丰富,能看到createElement中tag和data的数据类型都是any,能传入任意类型;
在_createElement中,如果tag不存在,直接createEmptyElement;tag存在,调用normalizeChildren或者simpleNormalizeChildern生成虚拟dom,在生成虚拟dom之前规范传入的节点,如果是数据,将数组扁平化后才通过createTextVNode创建新的虚拟dom节点
将虚拟dom渲染成真实dom
vue.js通过_update方法将虚拟dom渲染成真实dom,而_update方法只有在首次渲染和被更新的时候才会调用。_update方法的核心是__patch__方法,该方法根据不同的平台是不一样的,比如web中,__pach__定义在src/platforms/web/runtime/index.js中(感概一下目录结构清晰明了,命名也很易懂,值得学习)
Vue.prototype.__patch__ = inBrowser ? patch : noop
同样是web下,但是又因是否是服务端渲染而不一样,具体的渲染还看的不是很懂,先过一遍吧,有了整体,再追究细节才能明白为什么要这么写,感觉源码只少要看个3遍吧
网友评论