美文网首页
手写虚拟DOM(六)—— 事件处理

手写虚拟DOM(六)—— 事件处理

作者: 青叶小小 | 来源:发表于2020-12-27 14:50 被阅读0次

    本文为系列文章:

    手写虚拟DOM(一)—— VirtualDOM介绍
    手写虚拟DOM(二)—— VirtualDOM Diff
    手写虚拟DOM(三)—— Diff算法优化
    手写虚拟DOM(四)—— 进一步提升Diff效率之关键字Key
    手写虚拟DOM(五)—— 自定义组件
    手写虚拟DOM(六)—— 事件处理
    手写虚拟DOM(七)—— 异步更新

    一、前言

    今天,我们继续在之前项目的基础上扩展功能。

    任何页面除了展示功能数据,也会涉及到人机交互。截止到目前,功能数据的展示我们已经大致实现,本章我们就实现最基本的事件交互(事件处理)。事件有很多,比如:

    • 点击事件;
    • 滚动事件;
    • 键盘事件;

    等等;

    二、实现事件处理

    事件的绑定一般是定义在元素或是组件的属性中:

    class DemoComp extends Component {
        ......
        click = () => {
            console.log('DemoComp.click');
        };
    
        render() {
            return (
                <div onClick={this.click}>
                    <div>This is DemoComp....props = {this.props.value}</div>
                    <div>DemoComp.state.name = {this.state.name}{this.state.value}</div>
                </div>
            );
        }
    }
    

    如果我们直接编译,不会有任何报错,也能够正常显示,如下图:


    31fdsf43f4wf44.png

    点击之后,在 Console 界面,就会提示报错(需要函数名):


    f43g9fd0gfdigweg904gfeisfjs.png
    将 click 由 class 属性改为方法:
    click() {
        console.log('DemoComp.click');
    };
    

    点击之后,没有任何效果。

    但是,我们在React中这么使用就不会有问题,是我们哪里不对么?嗯,肯定不对!
    我们回想一下,我们的代码中,哪里设置“属性”?
    一共有两处:

    • setProps:当新增/替换节点时,依次给该节点元素及子元素设置属性值;
    • diffProps:当更新节点时,则是比较dom与vdom的属性,以及各节点的tag;

    本章既然讲到了事件,我们应该用 addEventListener 来处理,而不再是 setAttribute。

    接下来,我们开始改造我们的属性设置的函数:
    事件都是以“on”开头,之后接着是首字母大写的事件名,比如:onClick

    function setAttribute(element, key, value) {
        if (key.substring(0, 2) === 'on') { // event
            element.addEventListener(key.substring(2).toLowerCase(), value.bind(element));
        } else {
            element.setAttribute(key, value);
        }
    }
    

    判断如果是on开头,则拿事件名,onClick 的事件名是 click,然后 addEventListener 设置事件名和回调函数;
    注:这里的回调函数需要bind该element!
    如果不是on开头,就设置属性就行。

    function setProps(element, props) {
        for (let k in props) {
            if (props.hasOwnProperty(k)) {
                setAttribute(element, k, props[k]); // 修改此处
            }
        }
    
        // 保存当前的属性,之后用于新VirtualDOM的属性比较
        element['props'] = props;
    }
    

    function diffProps(props, element) {
        ......
        // 遍历props的所有键值
        Object.keys(all).forEach(key => {
            ......
            // 老vdom没有该属性,or 该属性值与新vdom的属性值不一致
            if (ov === undefined || ov !== nv) {
                setAttribute(element, key, nv); // 修改此处
            }
           ......
        });
        ......
    }
    

    然后,编译运行,查看 Elements 及 Console:


    0fje939f032f90hf9ew0f39f3.png

    点击结果:


    mpecje0w9c203jv029v.png
    一切正常!

    三、总结

    本文基于上一个版本的代码,加入了对事件处理的支持。

    项目源码:
    https://github.com/qingye/VirtualDOM-Study/tree/master/VirtualDOM-Study-06

    相关文章

      网友评论

          本文标题:手写虚拟DOM(六)—— 事件处理

          本文链接:https://www.haomeiwen.com/subject/pocgnktx.html