模拟Vue双向数据绑定

事件对象

function EventEmit(){
    // {
        // "message":['事件1','事件2']
    // }
    this.callbacks={}
}

EventEmit.prototype.on=function(eventName,fn){
  if(!this.callbacks[eventName]){
      this.callbacks[eventName]=[]
  }
  this.callbacks[eventName].push(fn)
}

EventEmit.prototype.emit=function(eventName,fn){
    if(!this.callbacks[eventName]){
        return 
    }
    this.callbacks[eventName].forEach(fn=>{
        fn()
    })
}

// 事件对象
// var e1=new EventEmit();
// // on订阅事件
// e1.on('message',function(){
//     console.log('操作h1')
// })
// e1.on('message',function(){
//     console.log('操作h2')
// })
// e1.on('message',function(){
//     console.log('操作p')
// })

vue.js

;(function(){
    function Vue(options){
        var {el,data}=options
        //根节点
        var rootEl=document.querySelector(el);
        var _data={}
        var _events=new EventEmit();

        // 数据观测
        // 当data中的数据发生变化,发出事件通知,所以订阅了该事件的DOM都会得到更新
        for(let key in data){
            _data[key]=data[key]
            Object.defineProperty(this,key,{
                get(){
                    return _data[key]
                },
                set(val){
                    _data[key]=val
                    _events.emit(key)
                }
            })
        }

        // 递归解析模板,注册数据绑定事件
        function compile(childNodes){
            childNodes.forEach((node,index)=>{
                switch(node.nodeType){
                    case 1:
                        // 处理input标签
                        if(node.nodeName==='INPUT'){
                            const vModel=node.attributes['v-model']
                            if(!vModel){
                                return 
                            }
                            var dataKey=vModel.value.trim();
                            node.oninput=()=>{
                                this[dataKey]=node.value
                            }
                        }
                        // 标签节点继续递归调用
                        compile.call(this,node.childNodes)
                        break;
                    case 3:
                        var matches=/{{(.+)}}/.exec(node.textContent)
                        if(matches){
                            var dataKey=matches[1].trim()
                            node.textContent=_data[dataKey]
                            _events.on(dataKey,()=>{
                                node.textContent=_data[dataKey]
                            })
                        }
                        break;
                }
            })
        }
        compile.call(this,rootEl.childNodes)

    }
    window.Vue=Vue;
})()

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="app">
        {{message}}
        <p>{{message}}</p>
        <div><p>{{message}}</p></div>
        <input type="text" v-model="message">
    </div>

    <script src="js/eventemit.js"></script>
    <script src="js/vue.js"></script>

    <script>
        // 模拟实现{{}} v-model
        var app=new Vue({
            el:'#app',
            data:{
                message:'hello vue'
            }
        })
    </script>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/zhong242526/article/details/80383947