Loading

MVVM架构方式

一、概念

①常见的架构方式或者开发思想还有MVC、MVP和MVVM,参考文章:MVC,MVP 和 MVVM 的图示

②MVC:视图(View),指用户界面;控制器(Controller),指业务逻辑;模型(Model),指数据保存

③MVP:将 Controller 改名为 Presenter,同时改变了通信方向

④MVVM:M,即model 业务数据模型,指操作数据的类;V,即view 视图界面,HTML用户界面;VM,viewModel 视图数据模型,指驱动视图改变的data数据。核心思想就是数据驱动视图。

二、MVVM的优点

低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码。
可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

三、vue原理简单剖析

  <div id="app">
        {{message}}
        <h1>{{message}}</h1>
    </div>
    <script>
        function Vue(options){
            var {el,data}=options;
            // 根节点
            var rootEl=document.querySelector(el);
            var childNodes=rootEl.childNodes;
            var domAndKey={

            };
            // 编译模板
            for(var i=0;i<childNodes.length;i++){
                let node=childNodes[i];
                switch(node.nodeType){
                    case 1: 
                        //console.log(node,'标签节点')
                        break
                    case 3: 
                        //console.log(node,'文本节点')
                        var matches=/{{(.+)}}/.exec(node.textContent);
                        if(matches){
                            var dataKey=matches[1].trim();
                            //console.log(dataKey);
                            // 记住这个节点以及这个节点所绑定的数据成员
                            domAndKey[dataKey] = node;
                        }
                        break
                    default: 
                        break
                }
            }
            // 数据观测
            for(let key in data){
                Object.defineProperty(this,key,{
                    get(){
                        
                    },
                    set(val){
                        domAndKey['message'].textContent = val
                    }
                })
            }
        }
        var app =new Vue({
            el:'#app',
            data:{
                message:'hello vue'
            },
        });
    </script>

四、双向绑定原理

①事件订阅发布模型

// 设计模式:事件发布/订阅模型
/* 构造函数 */
function EventEmit(){
    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){
    if(!this.callbacks[eventName]){
        return
    }
    this.callbacks[eventName].forEach(fn => {
        fn()      
    });
}

//创建实例对象
var el=new EventEmit();
// 订阅事件
el.on('message',function(){
    console.log('aaa')
}) 
el.on('message',function(){
    console.log('bbb')
})
el.on('message',function(){
    console.log('ccc')
})
// 发布事件
el.emit('a')

②双向绑定实现

    <div id="app">
        {{message}}
        <input type="text" v-model='message'>
        <h1>{{message}}</h1>
        <div>
            <div>{{message}}</div>
            <div>
                <h1>{{message}}</h1>
            </div>
        </div>
    </div>
    <!-- 引入事件订阅发布模型文件 -->
    <script src="eventemit.js"></script><!--  -->
    <script>
        //双向绑定实现原理的核心就是Object.defineProperty和事件订阅发布模型
        (function(){
            function Vue(options){
                var {el,data}=options;
                // 根节点
                var rootEl=document.querySelector(el);
                var _date={};
                var _event=new EventEmit();
                // 数据观测:当date中的数据发生变化时,发出事件通知,所有订阅了该事件的DOM都会得到更新
                for(let key in data){
                    _date[key]=data[key];//把data中的数据往_data里复制一份,当通过 实例访问data中的数据时,实际上访问的是_data中的数据
                    Object.defineProperty(this,key,{
                        get(){
                            return _data[key]
                        },
                        set(val){
                            _date[key]=val;
                            _event.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();
                                    _event.on(dataKey,()=>{
                                        node.textContent=_date[dataKey];
                                    })
                                }
                            break
                        }
                    });
                }
                compile.call(this,rootEl.childNodes)
            } 
            window.Vue=Vue
        })();
        var app =new Vue({
            el:'#app',
            data:{
                message:'hello vue',
            },
        });
    </script>

posted @ 2018-08-01 16:25  澎湃_L  阅读(352)  评论(0编辑  收藏  举报