当前位置: 首页 > 工具软件 > S-MVP > 使用案例 >

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

颛孙庆
2023-12-01
<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>
 类似资料: