snabbdom源码解析(六) 模块

匿名网络
• 阅读 2472

模块

./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源码解析(七) 事件处理

个人博客地址

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
半臻 半臻
3年前
Python基础4——模块与包
12模块与包模块通俗地理解为.py文件,里面定义了变量、函数和类。需要的时候就可以导入这些模块。执行步骤1.在python模块加载路径中查找相应的模块文件2.将模块文件编译成中间代码3.执行模块文件中的代码12.1模块分类1.内置模块,也叫标准库,比如说random,time,大概有200多个2.第三方模块,也称为第三方库,使用pipins
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Noark入门之异步事件
引入异步事件主要是为了各模块的解耦,每当完成一个动作时,向系统发布一个事件,由关心的模块自己监听处理,可选择同步处理,异步处理,延迟处理。何时发布事件,当其他模块关心此动作时<br比如获得道具时,任务系统模块要判定完成进度,BI模块需要上报等等都可以监听此事件,已达模块解耦0x00事件源一个实现xyz.noark.core.event
Stella981 Stella981
3年前
Noark入门之协议映射
0x00消息控制器消息控制器,主要作用就是为每个模块提供消息处理的入口.这里的消息不仅仅是协议,还有内部指令,事件等等逻辑入口,这也是为了响应线程模型作出的一种支撑,只要入口在此消息控制器内,那必然走期望的线程调度。@Controller用于标识一个类为当前模块的消息控制器入口.@Controller(threadGroup
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(