手写vue——简版Vue的实现2

112 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

上一篇我们已经可以实现简版Vue的大部分功能:

  • 将data数据转换为响应式数据
  • 能够解析插值和一些表达式,如h-text、h-html
  • 改变数据能够将页面更新 现在我们要实现数组的响应式,@click点击事件和h-model双向绑定,接下来开始吧。

代码实现

数组响应式

  • 找到数组的原型
  • 覆盖那些能够修改数组的更新方法,使其通知更新。
  • 将改造好的数组原型设置到数组实例原型上

具体代码实现等我学习完相关源码再更新。

事件绑定

首先我们要给页面绑定点击事件。为了不影响观看,我们把sum去掉。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>简版vue的实现</title>
</head>
<body>
    <div id="app">
        <p>{{counter}}</p>
        <p h-text="counter"></p>
        <p h-html="htmlText" @click="doubleCounter"></p>
    </div>
</body>
<!-- <script src="../node_modules/vue/dist/vue.js"></script> -->
<script src="./hvue.js"></script> 
<script> 
    const app = new Hvue({
        el:'#app',
        data:{
            counter:1,
            htmlText:'<span style="color:red">我成功啦</span>'
        },
        methonds:{
            doubleCounter(){
                this.counter = this.counter * 2;
            }
        }
    })
</script>
</html>

此刻的页面,我们绑定了个方法,当我们点击“我成功啦”的时候,counter的值会变双倍。

image.png 我们实现点击事件绑定的解析。回到hvue.js文件。首先要解析绑定,看代码。之前在解析插值和h-xxx表达式的时候,有一个compileElement的编译函数,我们就要在这里判断是否为@xxx形式的事件绑定,然后进行处理。

// 处理指令 编译
    compileElement(n){
        const attrs = n.attributes;
        Array.from(attrs).forEach(attr=>{
            const attrName = attr.name;
            const exp = attr.value;
            // 判断是否为特定的属性指令 h-
            if(this.isDir(attrName)){
                this.commandHandler(n,attrName,exp);
            }
            //事件绑定处理 @xxxx
            if(this.isEvent(attrName)){
                //@click
                const dir = attrName.substring(1); //click
                //事件监听
                this.eventHandler(n,exp,dir);
            }
        })
    }
     //是否为事件绑定
    isEvent(attrName){
        return attrName&&attrName.startsWith('@');
    }
    
    //事件处理
    eventHandler(node,exp,dir){ //exp就是方法名doubleCounter
        const fn = this.$vm.$options.methods&&this.$vm.$options.methods[exp];//拿到绑定的事件方法
        node.addEventListener(dir,fn.bind(this.$vm));//进行事件绑定
    }

现在我们的页面已经可以进行点击操作,并且页面也会更新了

image.png

数据的双向绑定

双向绑定其实要实现两件事,一件是value的设定,另一件是事件的监听。这里只是简单实现input的元素

//hvue.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>简版vue的实现</title>
</head>
<body>
    <div id="app">
        <p>{{counter}}</p>
        <p h-text="counter"></p>
        <p h-html="htmlText" @click="doubleCounter"></p>
        <input type="text" h-model="htmlText"/>
    </div>
</body>
<!-- <script src="../node_modules/vue/dist/vue.js"></script> -->
<script src="./hvue.js"></script> 
<script> 
    const app = new Hvue({
        el:'#app',
        data:{
            counter:1,
            htmlText:'<span style="color:red">我成功啦</span>',
            array:['一','二','三','四']
        },
        methods:{
            doubleCounter(){
                this.counter = this.counter * 2;
            }
        }
    })
</script>
</html>

image.png 此时页面的输入框内还是空白,数据并没有填充到里面。一下的功能的实现代码,h-model也是个指令属性,所以我们可以像h-text和h-html一样,再添加个modelCommandHandler的处理函数就可以了。只是要实现两件事,一个是赋值更新,另一件事是事件监听。

   //h-model
    modelCommandHandler(node,exp){
        //赋值更新
        node.value = this[exp];
        //
        node.addEventListener('input',e=>{
            this[exp]=e.target.value
        })
    }

现在我们可以看到输入框已经有值了,将red改为blue,页面中的文字颜色也发生了变化。

image.png

源码