设计模式知识连载(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>
推荐阅读
-
Android设计模式理解(mvc mvp mvvm)
-
Android设计模式——MVP
-
JAVA知识点全总结——(七)设计模式
-
js面试之14种设计模式(6)-前端小而全的知识归纳-SegmentFault思否
-
Java设计模式之构建者模式知识总结
-
(转+分享)JAVA核心知识点整理,用于面试!!!【三】 ~ Hbase/MongoDB/设计模式/ 负载均衡/ 数据库/
-
Java基础知识(八)单例设计模式&继承和覆盖&super、this、final
-
关于java单例设计模式和spring框架核心容器功能的基础知识描述
-
基础知识--------常用的设计模式之单例模式
-
荐 设计模式总篇:从为什么需要原则到实际落地(附知识图谱)