资讯专栏INFORMATION COLUMN

snabbdom源码解析(六) 模块

zone / 2785人阅读

摘要:模块在里面,定义了一系列的模块,这些模块定义了相应的钩子。主要接受两个参数,。传送门事件模块待续。。。源码解析系列源码解析一准备工作源码解析二函数源码解析三对象源码解析四方法源码解析五钩子源码解析六模块源码解析七事件处理个人博客地址

模块

./src/modules 里面,定义了一系列的模块 , 这些模块定义了相应的钩子 。这些钩子会在 patch 的不同阶段触发,以完成相应模块的功能处理

了解生命周期更多的内容,请查看 钩子

主要的模块有 :

attributes.ts

class.ts

dataset.ts

eventlisteners.ts

hero.ts

module.ts

props.ts

style.ts

其中 attributes class dataset props 四个比较简单,都是定义了 create update 两个钩子,

eventlisteners hero style 这三个模块就复杂一点。

另外 module.ts 只是定义了这些模块所用到的一些钩子

// 定义模块的钩子
export interface Module {
    pre: PreHook;
    create: CreateHook;
    update: UpdateHook;
    destroy: DestroyHook;
    remove: RemoveHook;
    post: PostHook;
}

接下来我们来看看其他的模块

attributes 模块

文件位置 : ./src/modules/attributes.ts

我们先拉到最后

// 创建以及更新的钩子
export const attributesModule = {
    create: updateAttrs,
    update: updateAttrs
} as Module;
export default attributesModule;

attributesModule 导出了两个方法, 都是调用了 updateAttrs

这个表示,在创建元素的时候,以及更新的时候,都会触发这两个钩子,来更新 attribute

updateAttrs

updateAttrs 主要接受两个参数,oldVnodevnode

主要逻辑如下:

遍历新 vnode 所有的属性,判断在 oldVnode 中是否相等,修改不相等的属性

删除不存在于 vnode 的属性

代码如下:

/**
 * 更新属性
 */
function updateAttrs(oldVnode: VNode, vnode: VNode): void {
    var key: string,
        elm: Element = vnode.elm as Element,
        oldAttrs = (oldVnode.data as VNodeData).attrs,
        attrs = (vnode.data as VNodeData).attrs;

    if (!oldAttrs && !attrs) return;
    if (oldAttrs === attrs) return;
    oldAttrs = oldAttrs || {};
    attrs = attrs || {};

    // update modified attributes, add new attributes
    // 遍历新的属性,修改不相等的
    for (key in attrs) {
        const cur = attrs[key];
        const old = oldAttrs[key];
        if (old !== cur) {
            if (cur === true) {
                elm.setAttribute(key, "");
            } else if (cur === false) {
                elm.removeAttribute(key);
            } else {
                if (key.charCodeAt(0) !== xChar) {
                    // 如果不是 x 开头
                    elm.setAttribute(key, cur);
                } else if (key.charCodeAt(3) === colonChar) {
                    // Assume xml namespace
                    elm.setAttributeNS(xmlNS, key, cur);
                } else if (key.charCodeAt(5) === colonChar) {
                    // Assume xlink namespace
                    elm.setAttributeNS(xlinkNS, key, cur);
                } else {
                    elm.setAttribute(key, cur);
                }
            }
        }
    }
    // remove removed attributes
    // use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value)
    // the other option is to remove all attributes with value == undefined
    // 删除多余的属性
    for (key in oldAttrs) {
        if (!(key in attrs)) {
            elm.removeAttribute(key);
        }
    }
}
class 模块

文件位置 : ./src/modules/class.ts

attribute 类似 , class 也是定义了 createupdate 两个钩子,统一由 updateClass 处理

这块逻辑比较简单 ,直接看代码吧

function updateClass(oldVnode: VNode, vnode: VNode): void {
    var cur: any,
        name: string,
        elm: Element = vnode.elm as Element,
        oldClass = (oldVnode.data as VNodeData).class,
        klass = (vnode.data as VNodeData).class;

    // 新老的 className 都没有
    if (!oldClass && !klass) return;

    // 新老的 className 没变
    if (oldClass === klass) return;

    oldClass = oldClass || {};
    klass = klass || {};

    // 删除不存在与新的 classList 的 className
    for (name in oldClass) {
        if (!klass[name]) {
            elm.classList.remove(name);
        }
    }

    // 新增 或删除 class
    for (name in klass) {
        cur = klass[name];
        if (cur !== oldClass[name]) {
            (elm.classList as any)[cur ? "add" : "remove"](name);
        }
    }
}
dataset 模块

文件位置 : ./src/modules/dataset.ts

attribute 类似 , dataset 也是定义了 createupdate 两个钩子,统一由 updateDataset 处理

这块逻辑比较简单 ,直接看代码吧

const CAPS_REGEX = /[A-Z]/g;

/**
 * 更新或创建 dataset
 */
function updateDataset(oldVnode: VNode, vnode: VNode): void {
    let elm: HTMLElement = vnode.elm as HTMLElement,
        oldDataset = (oldVnode.data as VNodeData).dataset,
        dataset = (vnode.data as VNodeData).dataset,
        key: string;

    // 不变的情况下不处理
    if (!oldDataset && !dataset) return;
    if (oldDataset === dataset) return;

    oldDataset = oldDataset || {};
    dataset = dataset || {};
    const d = elm.dataset;

    // 删除多余的 dataset
    for (key in oldDataset) {
        if (!dataset[key]) {
            if (d) {
                if (key in d) {
                    delete d[key];
                }
            } else {
                // 将驼峰式改为中划线分割  eg: userName ----> user-name
                elm.removeAttribute(
                    "data-" + key.replace(CAPS_REGEX, "-$&").toLowerCase()
                );
            }
        }
    }

    // 修改有变化的 dataset
    for (key in dataset) {
        if (oldDataset[key] !== dataset[key]) {
            if (d) {
                d[key] = dataset[key];
            } else {
                elm.setAttribute(
                    // 将驼峰式改为中划线分割  eg: userName ----> user-name
                    "data-" + key.replace(CAPS_REGEX, "-$&").toLowerCase(),
                    dataset[key]
                );
            }
        }
    }
}
props 模块

文件位置 : ./src/modules/props.ts

attribute 类似 , props 也是定义了 createupdate 两个钩子,统一由 updateProps 处理

这块逻辑比较简单 ,直接看代码吧

function updateProps(oldVnode: VNode, vnode: VNode): void {
    var key: string,
        cur: any,
        old: any,
        elm = vnode.elm,
        oldProps = (oldVnode.data as VNodeData).props,
        props = (vnode.data as VNodeData).props;

    if (!oldProps && !props) return;
    if (oldProps === props) return;

    oldProps = oldProps || {};
    props = props || {};

    // 删除多余的属性
    for (key in oldProps) {
        if (!props[key]) {
            delete (elm as any)[key];
        }
    }

    // 添加新增的属性
    for (key in props) {
        cur = props[key];
        old = oldProps[key];
        // key为value的情况,再判断是否value有变化
        // key不为value的情况,直接更新
        if (old !== cur && (key !== "value" || (elm as any)[key] !== cur)) {
            (elm as any)[key] = cur;
        }
    }
}
eventlisteners 模块

eventlisteners 这一块内容稍微多一点,故将其独立出来一个章节。 传送门 : 事件

style 模块

待续。。。

hero 模块

待续。。。

snabbdom源码解析系列

snabbdom源码解析(一) 准备工作

snabbdom源码解析(二) h函数

snabbdom源码解析(三) vnode对象

snabbdom源码解析(四) patch 方法

snabbdom源码解析(五) 钩子

snabbdom源码解析(六) 模块

snabbdom源码解析(七) 事件处理

个人博客地址

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100419.html

相关文章

  • snabbdom源码解析(一) 准备工作

    摘要:阅读源码的时候,想了解虚拟结构的实现,发现在的地方。然而慢慢的人们发现,在我们的代码中布满了一系列操作的代码。源码解析系列源码解析一准备工作源码解析二函数源码解析三对象源码解析四方法源码解析五钩子源码解析六模块源码解析七事件处理个人博客地址 前言 虚拟 DOM 结构概念随着 react 的诞生而火起来,之后 vue2.0 也加入了虚拟 DOM 的概念。 阅读 vue 源码的时候,想了解...

    defcon 评论0 收藏0
  • snabbdom源码解析(七) 事件处理

    摘要:这种解决方式也是相当优雅,值得学习源码解析系列源码解析一准备工作源码解析二函数源码解析三对象源码解析四方法源码解析五钩子源码解析六模块源码解析七事件处理个人博客地址 事件处理 我们在使用 vue 的时候,相信你一定也会对事件的处理比较感兴趣。 我们通过 @click 的时候,到底是发生了什么呢! 虽然我们用 @click绑定在模板上,不过事件严格绑定在 vnode 上的 。 event...

    Kross 评论0 收藏0
  • snabbdom源码解析(二) h函数

    介绍 这里是 typescript 的语法,定义了一系列的重载方法。h 函数主要根据传进来的参数,返回一个 vnode 对象 代码 代码位置 : ./src/h.ts /** * 根据选择器 ,数据 ,创建 vnode */ export function h(sel: string): VNode; export function h(sel: string, data: VNodeData...

    Jensen 评论0 收藏0
  • snabbdom源码解析(三) vnode对象

    摘要:对象是一个对象,用来表示相应的结构代码位置定义类型定义类型选择器数据,主要包括属性样式数据绑定时间等子节点关联的原生节点文本唯一值,为了优化性能定义的类型定义绑定的数据类型属性能直接用访问的属性样式类样式数据绑定的事件钩子创建对象根据传入的 vnode 对象 vnode 是一个对象,用来表示相应的 dom 结构 代码位置 :./src/vnode.ts 定义 vnode 类型 /** ...

    willin 评论0 收藏0
  • snabbdom源码解析(五) 钩子

    摘要:元素从父节点删除时触发,和略有不同,只影响到被移除节点中最顶层的节点在方法的最后调用,也就是完成后触发源码解析系列源码解析一准备工作源码解析二函数源码解析三对象源码解析四方法源码解析五钩子源码解析六模块源码解析七事件处理个人博客地址 文件路径 : ./src/hooks.ts 这个文件主要是定义了 Virtual Dom 在实现过程中,在其执行过程中的一系列钩子。方便外部做一些处理 /...

    Worktile 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<