vue核心原理实现

放肆的年华 提交于 2021-02-13 03:50:02
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>vue 核心源码</title>
    </head>
    <body>
        <div id="app">
            
        </div>
    </body>
    <script>
        /* 
         观察者  包含可观察对象  订阅与发布方法
         */
        var tempSubscript='';//存储可观察者对象,方便加入到观察者队列中
        function Observer(){
            this.$queue=[];
        }
        /* 可观察者对象加入到观察者队列中 */
        Observer.prototype.subscirpt=function(){
            this.$queue.push(tempSubscript);
        };
        /* 通知队列中的可观察者对象更新结点内容 */
        Observer.prototype.notify=function(){
            for(let i=0;i<this.$queue.length;i++){
                this.$queue[i].update();
            }
        }
        
        /* 
            可观测者对象
            包含更新方法 
        */
        function Observerable(propName,node,data){
            this.$propName=propName;
            this.$node=node;
            this.$data=data;
        }
        Observerable.prototype.update=function(){
            if(this.$node.nodeType==1){
                // input节点
                this.$node.value=this.$data[this.$propName];
            }else if(this.$node.nodeType==3){
                this.$node.nodeValue=this.$data[this.$propName];
            }else {
                // ...
            }
        }
        /* 
            创建构造函数 参数为对象
            获取对象中的所有数据
         */
        function MVVM(mvvmObj){
            this.$mvvmObj=mvvmObj;
            this.$el=mvvmObj.el;
            this.$data=mvvmObj.data;// 此时接收到的为函数 fn
            this.$$data=this.$data();// 执行 返回 包含数据的对象
            this.$template=mvvmObj.template;
//             console.log(this.$el)
//             console.log(this.$$data)
//             console.log(this.$template)
            this.initMvvm();//  初始化操作
        }
        /* 初始化操作 */
        MVVM.prototype.initMvvm=function(){
            this.traverseObj(this.$$data,this.$$data['name']);//遍历对象 参数为对象、值 这里为了方便直接输入改对象中的属性值
            this.compileDom(this.$el,this.$template,this.$$data);// 解析dom    
        }
        MVVM.prototype.traverseObj=function(obj,propNameValue){
            // console.log(obj,propNameValue)
            for(var key in obj){
                 /* 
                    为每一个属性创建一个观察者 
                    set函数触发时 将可观察对象加入到观察者中
                    get函数触发时 执行可关注发布模式,可观察者对象更新
                 */
                var observer=new Observer();
                Object.defineProperty(obj,key,{
                    get:function(){
                        console.log('触发get');
                        if(tempSubscript){
                            observer.subscirpt();
                            tempSubscript='';
                        }
                        return propNameValue
                    },
                    set:function(value){
                        console.log('触发set')
                        propNameValue=value;
                        observer.notify();
                    }
                })
            }
        }
        /* 
            解析dom
            区分  input结点  与 text结点
            并区分指令  如 v-model  v-on:click 等
        */
        MVVM.prototype.compileDom=function(ele,temStr,data){
            /* console.log(temStr,data) */
            // 获取页面中的元素并将template字符串插入其中 再获取其中的结点
            var vueDom=document.querySelector(ele);
            vueDom.innerHTML=temStr;
            // console.dir(vueDom);
            var domList=vueDom.children[0].childNodes;
            var regText=/.*\{\{(.*)\}\}.*/;//匹配文本结点
            var regV=/^v-(.*)$/;//匹配属性 v-
            for(var i=0;i<domList.length;i++){
                console.dir(domList[i]);
                // 根据 nodeType 区分不同的结点
                let nodeEle=domList[i];
                if(nodeEle.nodeType==1){
                    // input 结点 获取属性 type="text" placeholder="姓名" v-model="name" 属性值都包含name value
                    var result=nodeEle.attributes;
                    // console.log(result)
                    if(result){
                        for(var j=0;j<result.length;j++){
                            // 获取v-model 中的model
                            //根据不同指令 v-做相应的处理 
                            // 以相应的指令做key 执行不同的方法
                            var attrV=regV.exec(result[j].name);
                            // console.log(attrV)
                            if(attrV){
                                // console.log(attrV[1])
                                this.moduleAction[attrV[1]](result[j].value,nodeEle,data)
                            }
                        }
                    }
                }else if(nodeEle.nodeType==3){
                    // text结点获取 nodeValue
                    var result=regText.exec(nodeEle.nodeValue);// 返回值  第一位为匹配值 第二位 获取值
                    // 获取到的值要去this.$$data匹配 触发get 
                    // console.log(result[1])
                    if(result){
                        this.propNameMatch(result[1],nodeEle);
                    }
                }else{
                    //...
                }
            }
        }
        MVVM.prototype.propNameMatch=function(propName,nodeEle){
            // 此时创建改结点的观察者对象
            // 观察者对象全局存储
            // 与this.$$data匹配 触发get函数 触发get函数时 将该节点的可观察者对象加入到观察者队列中;
            tempSubscript=new Observerable(propName,nodeEle,this.$$data);
            nodeEle.nodeValue=this.$$data[propName];
        }
        MVVM.prototype.moduleAction={
            // v-model 指令  监听该dom 值改变时 
            // 结点初始值时创建改结点的观察者对象
            // 
            model:function(propName,node,data){
                tempSubscript=new Observerable(propName,node,data);
                node.nodeValue=data[propName];// 触发get函数 将该节点的可观察者对象加入到观察者队列中
                node.addEventListener('input',function(e){
                    data[propName]=e.target.value;// 触发set函数 
                })
            }
        }
        var mvvm =new MVVM({
            el:'#app',
            data(){
                return {
                    name:'xqzi',
                }
            },
            template:`<div><input type="text" placeholder="姓名" v-model="name">{{name}}</div>`
        })
    </script>
</html>

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!