美文网首页
vue源码2

vue源码2

作者: e80c3fbed5b2 | 来源:发表于2020-06-20 22:18 被阅读0次

    之前说了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遍吧

    new Vue到生成dom的总体过程

    new-vue.png

    相关文章

      网友评论

          本文标题:vue源码2

          本文链接:https://www.haomeiwen.com/subject/xwfnxktx.html