上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

[油猴脚本开发指南]简明diff(二)

[复制链接]

159

主题

1105

帖子

618

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
618
发表于 2021-10-9 00:46:40 | 显示全部楼层 | 阅读模式

前文

之前我们聊到了diff是同级比较

那同级比较到底做了什么呢?

这里我采取最为普遍的Snabbdom源码来与大家一起进行探讨

原因有三

1.市面上的采取diff的源码其实大同小异,我们选取知识最多的一个源码分析

2.Vue采取了Snabbdom的基础上进行了修改,学Snabbdom=学Vue的diff

3.我他妈学的Snabbdom,没学过其他的

源码可以在https://github.com/snabbdom/snabbdom看

进入这个页面的上方可以通过

图片.png

搜索源码。

关于如何定位可以查看开发文档,然后再翻阅源码。

这里我直接给出了,是patch函数

在https://github.com/snabbdom/snabbdom/blob/79457fdf14ea5b8b8e7769119d139daf238e2e61/src/init.ts中

  return function patch(oldVnode: VNode | Element, vnode: VNode): VNode {
    let i: number, elm: Node, parent: Node;
    const insertedVnodeQueue: VNodeQueue = [];
    for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]();

    if (!isVnode(oldVnode)) {
      oldVnode = emptyNodeAt(oldVnode);
    }

    if (sameVnode(oldVnode, vnode)) {
      patchVnode(oldVnode, vnode, insertedVnodeQueue);
    } else {
      elm = oldVnode.elm!;
      parent = api.parentNode(elm) as Node;

      createElm(vnode, insertedVnodeQueue);

      if (parent !== null) {
        api.insertBefore(parent, vnode.elm!, api.nextSibling(elm));
        removeVnodes(parent, [oldVnode], 0, 0);
      }
    }

    for (i = 0; i < insertedVnodeQueue.length; ++i) {
      insertedVnodeQueue[i].data!.hook!.insert!(insertedVnodeQueue[i]);
    }
    for (i = 0; i < cbs.post.length; ++i) cbs.post[i]();
    return vnode;
  };
}

我们来看看具体干了什么

patch的参数oldVnode: VNode | Element, vnode: VNode

是传入了旧虚拟dom和新虚拟dom

    const insertedVnodeQueue: VNodeQueue = [];
    for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]();

声明了一个数组,我们暂时先不用管。

  const cbs: ModuleHooks = {
    create: [],
    update: [],
    remove: [],
    destroy: [],
    pre: [],
    post: [],
  };

查询cbs定义可以发现是一个钩子对象,触发了pre钩子。

    if (!isVnode(oldVnode)) {
      oldVnode = emptyNodeAt(oldVnode);
    }

由于可以传入真实dom,所以这里判断了是否是虚拟dom,如果不是的话则转换为虚拟dom

    if (sameVnode(oldVnode, vnode)) {
      patchVnode(oldVnode, vnode, insertedVnodeQueue);
    } else {
      elm = oldVnode.elm!;
      parent = api.parentNode(elm) as Node;

      createElm(vnode, insertedVnodeQueue);

      if (parent !== null) {
        api.insertBefore(parent, vnode.elm!, api.nextSibling(elm));
        removeVnodes(parent, [oldVnode], 0, 0);
      }
    }

这里使用了sameNode进行判断两个是否相等

function sameVnode(vnode1: VNode, vnode2: VNode): boolean {
  const isSameKey = vnode1.key === vnode2.key;
  const isSameIs = vnode1.data?.is === vnode2.data?.is;
  const isSameSel = vnode1.sel === vnode2.sel;

  return isSameSel && isSameKey && isSameIs;
}

sameVnode传入了两个参数

通过key,data,sel等结合判断了是否是相同的虚拟dom(一新一旧)

如果是的话,则调用

patchVnode(oldVnode, vnode, insertedVnodeQueue);

发起更新,同时传入了insertedVnodeQueue数组。

      elm = oldVnode.elm!;
      parent = api.parentNode(elm) as Node;

      createElm(vnode, insertedVnodeQueue);

      if (parent !== null) {
        api.insertBefore(parent, vnode.elm!, api.nextSibling(elm));
        removeVnodes(parent, [oldVnode], 0, 0);
      }

如果不是的话,获取旧虚拟dom的elm属性(elm是真实dom)

并获取真实dom的父对象。

并根据新虚拟dom创建一个真实dom,挂载到新虚拟dom的elm节点上

判断旧dom的父对象是否为null

如果不为null

则将新虚拟dom的真实dom插入到该对象的下一个兄弟对象的前面,也就是他的后边

并移除旧的dom对象。

    for (i = 0; i < insertedVnodeQueue.length; ++i) {
      insertedVnodeQueue[i].data!.hook!.insert!(insertedVnodeQueue[i]);
    }
    for (i = 0; i < cbs.post.length; ++i) cbs.post[i]();

循环调用insertedVnodeQueue,并判断是否存在data,hook,inset,如果存在inset,则调用并传入insertedVnodeQueue[i]

这里的!是ts语法,判断是否为undefined,如果为undefined则不执行

接下来的for cbs.post依然是一个钩子函数。

return vnode;

返回了新虚拟dom节点。

加强

cbs的钩子是哪里来的?

我们可以搜索cbs

找到了

 export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
  const cbs: ModuleHooks = {
    create: [],
    update: [],
    remove: [],
    destroy: [],
    pre: [],
    post: [],
  };

  const api: DOMAPI = domApi !== undefined ? domApi : htmlDomApi;

  for (const hook of hooks) {
    for (const module of modules) {
      const currentHook = module[hook];
      if (currentHook !== undefined) {
        (cbs[hook] as any[]).push(currentHook);
      }
    }
  }

循环hooks,并获取每个module

那hooks是什么?

const hooks: Array<keyof Module> = [
  "create",
  "update",
  "remove",
  "destroy",
  "pre",
  "post",
];

那么这里就真相大白了

循环每个hook以及module

获取module下的hook钩子,并且投入到cbs对象下不同的数组内

在合适的情况下激发钩子。

结语

码头领稿把,给你置办卡地亚,2021,名满天下!

图片.png
混的人。

发表回复

本版积分规则

快速回复 返回顶部 返回列表