最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue源码解析(虚拟DOM)

    正文概述 掘金(Husky-Yellow)   2021-03-04   476

    虚拟DOM

    虚拟DOM介绍

    我们在最开始提到过,Vue2.0+版本开始就引入了虚拟DOM,也知道Vue中的虚拟DOM借鉴了开源库snabbdom (opens new window)的实现,并根据自身特色添加了许多特性。

    Vue1.0+版本还没有引入虚拟DOM的时候,当某一个状态发生变化时,它在一定程度上是知道哪些节点使用到了这个状态,从而可以准确的针对这些节点进行更新操作,不需要进行对比。但这种做法是有一定的代价的,因为更新的粒度太细,每一次节点的绑定都需要一个Watcher去观察状态的变化,这样会增加更多的内存开销。当一个状态被越多的节点使用,它的内存开销就越大。

    因此在Vue2.0+版本中,引入了虚拟DOM将更新粒度调整为组件级别,当状态发生变化的时候,只派发更新到组件级别,然后组件内部再进行对比和渲染。这样做以后,当一个状态在同一个组件内被引用多次的时候,它们只需要一个render watcher去观察状态的变化即可。

    Vue中的虚拟DOM

    虚拟DOM解决DOM更新的方式是:通过状态生成一个虚拟节点树,然后使用虚拟节点树进行渲染,在渲染之前会使用新生成的虚拟节点树和上一次生成的虚拟节点树进行对比,然后只渲染其不相同的部分(包括新增和删除的)。

    Vue中,根实例就是虚拟节点树的根节点,各种组件就是children孩子节点,树节点使用VNode类来表示。它使用template模板来描述状态与DOM之间的映射关系,然后通过parse编译将template模板转换成渲染函数render,执行渲染函数render就可以得到一个虚拟节点树,最后使用这个虚拟节点树渲染到视图上。

    因此根据上面这段话,我们可以得到Vue使用虚拟DOM进行模板转视图的一个流程。

    Vue源码解析(虚拟DOM)

    VNode介绍

    DOM元素中有不同类型的节点,例如:元素节点文本节点注释节点,如果VNode实例代表节点的话,那么VNode实例应该能创建不同类型的实例,用来表示不同类型的节点,而VNode也确实可以做到。VNode类定义在src/core/vdom/vnode.js文件中,其代码如下:

    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
      }
    }
    

    我们可以从以上代码看到,VNode有很多个属性,我们千万不要被这些属性吓到,最重要的属性只有几个:tagdatachildrenkey。其余很多属性只是在Vue中为适用不同的场景,额外添加的。

    Vue中的VNode实例有需要类型,具体如下:

    • 注释节点:注释节点可以使用textisComment两个属性来配合表示。
    <template>
      <!-- 一个注释节点 -->
    </template>
    

    Vue中,我们通过createEmptyVNode方法来定义注释节点,此方法跟VNode类是定义在同一个地方,其代码如下:

    export const createEmptyVNode = (text: string = '') => {
      const node = new VNode()
      node.text = text
      node.isComment = true
      return node
    }
    
    • 文本节点:文本节点比注释节点更简单,只需要text属性即可。
    <template>
      一个文本节点
    </template>
    

    创建文本节点可以使用createTextVNode方法,此方法跟VNode类是定义在同一个地方,其代码如下:

    export function createTextVNode (val: string | number) {
      return new VNode(undefined, undefined, undefined, String(val))
    }
    
    • 克隆节点:克隆节点是将现有某个节点的所有属性全部克隆到另外一个新节点,让新节点和某个节点属性保持一致,唯一的区别是新克隆节点的isCloned属性为true,我们会在后续组件章节介绍克隆节点的具体作用。
    // 原始文本节点
    const originNode = {
      text: '原始节点',
      isCloned: false
    }
    
    // 克隆文本节点
    const cloneNode = {
      text: '原始节点',
      isCloned: true
    }
    

    克隆一个节点,可以使用cloneVNode方法,它与VNode类是定义在同一个地方,其代码如下:

    export function cloneVNode (vnode: VNode): VNode {
      const cloned = new VNode(
        vnode.tag,
        vnode.data,
        // #7975
        // clone children array to avoid mutating original in case of cloning
        // a child.
        vnode.children && vnode.children.slice(),
        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
    }
    
    • 元素节点:元素节点是我们日常开发中接触最多的,它可以使用tagdatachildrencontext几个属性配合表示。
    <template>
      <div id="app" class="app-main">元素节点</div>
    </template>
    

    假设以上template模板,那么div元素节点可以使用VNode表示为:

    const vnode = {
      tag: 'div',
      data: {
        attrs: {
          id: 'app'
        }
        class: 'app-main'
      },
      children: [VNode],
      context: vm
    }
    
    1. tag表示为元素标签的类型,例如:pdiv或者ul等。
    2. data表示节点上的数据,包括attsstyleclass等。
    3. children表示子节点列表,它是一个VNode实例数组。
    4. context当前节点所处的编译作用域。
    • 组件节点:组件节点和元素节点有很多相似的地方,但它有两个独有的属性,分别是componentOptionscomponentInstance,其中componentOptions表示组件的options选项,componentInstance表示当前组件的实例。
    <template>
      <child-component />
    </template>
    

    起源地下载网 » Vue源码解析(虚拟DOM)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元