虚拟 dom 及 Snabbdom源码解析

虚拟DOM

什么是虚拟DOM

        Virtual DOM(虚拟DOM),是由普通的JS对象来描述真实的DOM对象。因为不是真实的DOM对象,所以叫Virtual DOM。真实的DOM成员需要创建大量的属性等,使用virtual DOM来描述DOM将会十分的简洁,减少性能开销。

虚拟DOM的结构

 {
    
     sel:'div', data:{
    
    }, children:undefined, text:'Hello Virtual DOM' elm:undefined, key:undefined } 

虚拟DOM的节点类型

元素节点 :节点类型 1 ;
属性节点: 节点类型 2;
文本节点: 节点类型 3;
注释节点: 节点类型 8;
文档节点: 节点类型 9;

为什么使用virtual DOM

  1. 手动操作DOM比较麻烦,且需要考虑浏览器兼容性问题,虽然有JQuery等库可以简化DOM操作,但是随着项目的复杂,DOM的操作复杂性提升。

  2. 为了简化DOM的复杂操作于是出现了各种MVVM框架,其解决了视图和状态的同步问题。

  3. 为了简化视图的操作我们可以使用模板引擎,但是因为没有解决跟踪动态变化的问题,而出现了Virtual DOM。

  4. virtual DOM的好处是当状态改变时不需要立即更新DOM,只需要创建一个虚拟树来描述DOM,VIrtual DOM内部将弄清楚如何有效的更新DOM。

  5. 虚拟DOM可以维护程序的状态,跟踪上一次的状态。

  6. 通过比较前后两次状态差异更新真实DOM。

虚拟DOM有什么作用

  1. 维护视图和状态的关系
  2. 在复杂视图情况下提升渲染性能,除渲染DOM外,还可以实现SSR(Nuxt.js/Next.js)、原生应用(Weex/React Native)、小程序(mpvue/uni-app)等。

Virtual DOM 库之 Snabbdom

  • Vue 2.x内部使用的Virtual DOM 就是改造的Snabbdom

  • 大约200 SLOC (single line of code)

  • 通过模块可扩展

  • 源码使用TypeScript开发

  • 最快的Virtual DOM之一

注:Snabbdom的核心库并不能处理元素的属性、样式、事件等,如果需要处理的话,可以使用模块进行支持。

Snabbdom库的常用模块:

  • attribute

    • 设置Dom元素的属性,使用setAttribute()
    • 处理布尔类型的属性
  • props

    • 和attribute模块相似,设置Dom属性element[attr] = value
    • 不处理布尔类型的属性
  • class

    • 切换类样式
    • 注意:给元素设置类属性是通过sel选择器
  • dataset

    • 设置data-*的自定义属性
  • eventlisteners

    • 注册和移除事件
  • style

    • 设置行内样式,支持动画
    • delayed/remove/destory

Snabbdom源码解析

源码应该如何学习

  • 先宏观了解
  • 带着目标看源码
  • 看源码的过程不求甚解
  • 调试
  • 参考资料

Snabbdom 核心内容

  1. 使用h()函数创建JavaScript对象(VNode)描述真实DOM

  2. init()设置模块,创建patch() 这里init()是一个高阶函数 (在一个函数的内部返回一个函数)

  3. patch()比较新旧两个VNode

  4. 将变化的内容更新到真实的DOM上

Snabbdom 的源码剖析

h函数

h()函数介绍

  • 在使用vue时见过h函数

    new Vue({
          
          
        router,
        store,
        render: h => h(App)
    }).$mount('#app')
    
  • h()函数最早见于hyperscript,使用JavaScript创建超文本

  • Snabbdom中的h()函数不是用来创建超文本,而是创建VNOde

函数重载

  • 概念

    • 参数个数类型不同的函数
    • JavaScript中没有重载的概念
    • TypeScript中有重载,不过重载的实现还是通过代码调整参数
  • 重载的示意

    function add (a,b) {
          
          
        console.log(a,b)
    }
    function add (a,b,c) {
          
          
        console.log(a + b + c)
    }
    add(1, 2)
    add(1, 2, 3)
    

Modules(模块)

Snabbdom为了保证核心库的精简,把处理元素的属性/事件/样式等工作 交给模块处理。模块是可以按需引入 ,实现的核心是基于Hooks 。在 Snabbdom 中的模块是可以自己扩展的模块的钩子函数

  • pre patch函数开始执行的时候出发
  • init createElm函数开始执行之前触发 在把VNode转化为真实的 DOM 之前触发
  • create ceeateElm函数末尾调用 创建完真实DOM之后触发
  • insert patch函数末尾执行 真实 DOM 添加到 DOM 树中触发
  • prepatch patchVnode函数开头调用 开始对比两个 Vnode 差异之前触发
  • update patchVnode函数开头调用 两个 Vnode 对比过程中出发,比prepatch稍微晚一些
  • postpatch patchVnode的最末尾调用 两个 Vnode对比结束后执行
  • destroy removeVnodes => invokeDestoryHook 中调用 在删除元素之前触发,子节点的destory也被触发
  • remove removeVnodes 中调用 元素被删除的时候触发
  • post patch函数的最后调用

源码位置: src/h.ts

export function h(sel: string): VNode;
export function h(sel: string, data: VNodeData): VNode;
export function h(sel: string, children: VNodeChildren): VNode;
export function h(sel: string, data: VNodeData, children: VNodeChildren): VNode;
export function h(sel: any, b?: any, c?: any): VNode {
    
    
  var data: VNodeData = {
    
    }, children: any, text: any, i: number;
  if (c !== undefined) {
    
    
    data = b;
    if (is.array(c)) {
    
     children = c; }
    else if (is.primitive(c)) {
    
     text = c; }
    else if (c && c.sel) {
    
     children = [c]; }
  } else if (b !== undefined) {
    
    
    if (is.array(b)) {
    
     children = b; }
    else if (is.primitive(b)) {
    
     text = b; }
    else if (b && b.sel) {
    
     children = [b]; }
    else {
    
     data = b; }
  }
  if (children !== undefined) {
    
    
    for (i = 0; i < children.length; ++i) {
    
    
      if (is.primitive(children[i])) children[i] = vnode(undefined, undefined, undefined, children[i], undefined);
    }
  }
  if (
    sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g' &&
    (sel.length === 3 || sel[3] === '.' || sel[3] === '#')
  ) {
    
    
    addNS(data, children, sel);
  }
  return vnode(sel, data, children, text, undefined);
};

猜你喜欢

转载自blog.csdn.net/weixin_40599109/article/details/108096080
今日推荐