欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

设计模式知识连载(49)---MVP模式:

程序员文章站 2022-06-08 22:21:28
...
<body>


<h3>设计模式知识连载(49)---MVP模式:</h3>
<div>
    <p>
         模型(Model)-视图(View)-管理器(presenter):View层不直接引用Model层内的数据,而是通过Presenter层实现对Model层内的数据访问。即所有层次的交互都发生在Presenter层中
    </p>

</div>


<hr>


<script type="text/javascript">


    /**
    *   模拟数据
    */
    M.data = {
        // 导航模块渲染数据
        nav : [
            {
                text : '新闻头条1',
                mode : 'news',
                url : 'https://www.baidu.com'
            },
            {
                text : '新闻头条2',
                mode : 'news',
                url : 'https://www.baidu.com'
            },
            {
                text : '新闻头条3',
                mode : 'news',
                url : 'https://www.baidu.com'
            },
            {
                text : '新闻头条4',
                mode : 'news',
                url : 'https://www.baidu.com'
            },
            {
                text : '新闻头条5',
                mode : 'news',
                url : 'https://www.baidu.com'
            },
            {
                text : '新闻头条6',
                mode : 'news',
                url : 'https://www.baidu.com'
            },
        ]
    };



    /**
    *   案例一:,方式一:初始
    */
    // MVP模块
    ~(function(window) {
        // MVP构造函数
        var MVP = function() {} ;

        // 数据层
        MVP.model = (function() {
            var M = {} ;
            M.data = {}
            M.conf = {}
            return {
                getData : function(m) {
                    return M.data[m] ;
                },
                /***
                *   设置数据
                *   @param      m       模块名称
                *   @param      v       模块数据
                **/
                setData : function(m, v) {
                    M.data[m] = v ;
                    return v ;
                },
                getConf : function(c) {
                    return M.conf[c]
                },
                /***
                *   设置配置
                *   @param      c       配置项名称
                *   @param      v       配置项值
                **/
                setConf : function(c, v) {
                    M.conf[c] = v ;
                    return v ;
                }
            }
        })() ;

        // 视图层
        MVP.view = MVP.view = (function() {
            // 子元素或者兄弟元素替换模板
            var REPLACEKEY = '__REPLACEKEY__' ;

            // 获取完整元素模板
            // function getHTML(str, replacePos) {}
            /***
            *   获取完整元素模板
            *   @param      str     元素字符串
            *   @param      type    元素类型
            **/
            function getHTML(str, type) {
                // 简化实现,只处理字符串中第一个{}里面的内容
                return str
                    .replace(/^(\w+)([^\{\}]*)?(\{([@\w]+)\})?(.*?)$/, function(match, $1, $2, $3, $4, $5) {
                            // 元素属性参数容错处理
                            $2 = $2 || '' ;
                            // {元素内容}参数容错处理
                            $3 = $3 || '' ;
                            // 元素内容参数容错处理
                            $4 = $4 || '' ;
                            // 去除元素内容后面添加的元素属性中的{}内容
                            $5 = $5.replace(/\{([@\w])+\}/g, '') ;

                            /*以str=div举例:。。。内容没写上来*/
                    })
                    // 处理特殊标识符#---id属性
                    .replace(/#([@\-\w]+)/g, 'id="$1"')
                    // 处理特殊标识符.---class属性
                    .replace(/\.([@\-\s\w]+)/g, 'class="$1"')
                    // 处理其他属性组
                    .replace(/\[(.+)\]/g, function(match, key) {
                        // 元素数组
                        var a = key
                            // 过滤其中引号
                            .replace(/'|"/g, '') 
                            // 以空格分组
                            .split(' ');
                        // 属性模板字符串
                        var h = '' ;
                        // 遍历属性组
                        for(var j = 0, len = a.length; j < len; j++) {
                            // 处理并拼接每一个属性
                            h += ' ' + a[j].replace(/=(.*)/g, '="$1"') ;
                        }
                        // 返回属性组模板字符串
                        return h ;  
                    })
                    // 处理可替换内容,可根据不同模板渲染引擎*处理
                    .replace(/@(\w+)/g, '{#$1#}') ; 
            }

            /***
            *   数组迭代器
            *   @param      arr     数组
            *   @param      fn      回调函数
            **/
            function eachArray(arr, fn) {
                // 遍历数组
                for(var i = 0, len = arr.length; i < len; i++) {
                    // 将索引值、索引对应值、数组长度传入回调函数中并执行
                    fn(i, arr[i], len) ;
                }
            }

            /***
            *   替换兄弟元素模板或者子元素模板
            *   @param      str     原始字符串
            *   @param      rep         兄弟元素模板或者子元素模板
            **/
            function formateItem(str, rep) {
                // 用对应元素字符串替换兄弟元素模板或者子元素模板
                return str.replace(new RegExp(REPLACEKEY, 'g'), rep) ;
            }

            // 模板解析器
            return function(str) {

                // 模板层级数组
                var part = str
                // 去除首位空白符
                    .replace(/^\s + | \s+$/g, '')
                // 去除>两端空白符
                    .replace(/^\s+(>)\s+/g, '$1')
                // 以>分组
                .split('>') ;       
                // 模块视图根模板
                var html = REPLACEKEY ;
                // 同层元素
                var item ;
                // 同级元素模板
                var nodeTpl ;
                // 遍历每组元素
                eachArray(part, function(partIndex, partValue, partLen) {
                    // 为同级元素分组
                    item = partValue.split('+') ;
                    // 设置同级元素初始模板
                    nodeTpl = REPLACEKEY ;
                    // 遍历同级每一个元素
                    eachArray(item, function(itemIndex, itemValue, itemLen) {
                        // 用渲染元素得到的模板去渲染同级元素模板,此处简化逻辑处理
                        // 如果itemIndex(同级元素索引)对应元素不是最后一个,则作为兄弟元素处理
                        // 否则,如果partIndex(层级索引)对应的层级不是最后一层,则作为父层级处理(该层级有子层级,即该元素是父元素)
                        // 否则,该元素无兄弟元素,无子元素
                        nodeTpl = formateItem(nodeTpl, getHTML(itemValue, itemIndex === itemLen - 1 ? (partIndex === partLen - 1 ? '' : 'in') : 'add')) ;
                    }) ;

                    // 用渲染子层级得到的模板法去渲染父层级模板
                    html = formateItem(html, nodeTpl) ;
                }) ;

                // 将参数字符串转换成期望模板
                return hmtl ;
            }
        }) () ;

        // 管理层
        MVP.presenter = (function() {
            var V = MVP.view ;
            var M = MVP.model ;
            var C = {
                /***
                *   导航管理器
                *   @param      M   数据层对象
                *   @param      V   视图层对象
                **/
                nav : function(M, V) {
                    // 获取导航渲染数据
                    var data = M.getData('nav') ;
                    // 处理导航渲染数据
                    data[0].choose = 'choose' ;
                    data[data.length - 1] = 'last' ;
                    // 获取导航渲染模板
                    var tpl = V() ; // 没有详细写出来

                    $
                    // 创建导航容器
                    .create()
                    // 插入导航视图
                    .html()
                    // 导航模块添加到页面中
                    .appendTo() ;

                    // 其他交互逻辑与动画逻辑
                    // ...
                }
            } ;
            return {
                // 执行方法
                init : function() {
                    // 遍历内部管理器
                    for(var i in C) {
                        // 执行所有管理器内部逻辑
                        C[i] && C[M, V, i]() ;
                    }
                }
            }
        }) () ;

        // MVP入口
        MVP.init = function() {
            this.presenter.init() ;
        }

        // 暴露MVP对象,这样即可在外部访问MVP
        window.MVP = MVP ;
    })(window) ;

    window.onload = function() {
        // 执行管理器
        MVP.init() ;
    }



    /**
    *   案例二:,方式一:在模块开发中的应用
    */
    // 将MVP封装在模块内
    F.module('lib/MVP', function() {
        // MVP构造函数
        var MVP = function(modName, pst, data) {
            // 在数据层中添加modName渲染数据模块
            MVP.model.setData(modName, data) ;
            // 在管理器层中添加modName管理器模块
            MVP.presenter.add(modName, pst) ;

        }
        // MVP实现
        // ......

        // 返回MVP
        return MVP;
    }) ;

    // 增添管理器
    MVP.presenter = (function() {
        // .......
        return {
            init : function() {},
            /***
            *   为管理器添加模块
            *   @param      modName     模块名称
            *   @param      pst         模块管理器
            **/
            add : function(modName, pst) {
                C[modName] = pst ;
                return this ;
            }
        } ;
    }) () ;

    // 增加一个模块
    // 网址模块
    F.module(['lib/MVP', 'lib/A'], function(MVP, $) {
        // 页面加载完成执行
        $(function() {
            // 为MVP对象添加一个网址模块
            MVP(
                // 模块名称
                'sites', 
                /***
                *   模块控制器
                *   @param      M       数据对象层引用
                *   @param      V       视图对象层引用
                *   @param      modName 模块名称
                **/
                function(M, V, modName) {
                    // 渲染模板<li><a href='#'>{#text#}</a></li>
                    var tpl = V('li>a[href="#"]{@text}') ;
                    $
                    // 创建网址模块容器
                    .create('ul', {
                        'class' : 'store-nav',
                        'id' : modName
                    })
                    // 向网址模块容器中插入网址模块视图
                    .html(
                        // 创建网址模块视图
                        $.formateString(tpl, M.getData(modName)) ;
                        )
                    // 插入页面中
                    .appendTo('#container') ;

                    // 其他交互与特效
                },
                /***
                *   数据模块
                **/
                [
                    '聚划算', 
                    '1号店', 
                    '九块邮', 
                    '优购网', 
                    '爱淘宝' 
                ]
            ) ;
        }) ;
    }) ;


    $(function() {
        MVP.init() ;
    }) ;

</script>    

</body>