Widget模式

Widget模式是指借用Web Widget思想将页面分解成组件,针对部件开发,最终组合成完整的页面,Web Widget指的是一块可以在任意页面中执行的代码块,Widget模式不属于一般定义的23种设计模式的范畴,而通常将其看作广义上的架构型设计模式。

描述

模块化开发使页面的功能细化,逐一实现每个功能模块来完成系统需求,这是一种很好的编程实践,在简单模板模式实现的模板引擎的帮助下可以非常方便的完成这个实例,这将更适合多人团队开发,降低相互之间因为功能或者视图创建的耦合影响概率,组件的多样化也能够组建更加丰富的页面,同样也会让组件的复用率提高。

实现

// dom.js
F.module("./dom", function() {
return {
g: function(id) {
return document.getElementById(id);
},
html: function(id, html) {
if (!html) return this.g(id).innerHTML;
else this.g(id).innerHTML = html;
}
}
});
// template.js
F.module("./template", function() { /***
* 模板引擎,处理数据的编译模板入口
* @param str 模块容器id或者模板字符串
* @param data 渲染数据
**/
var _TplEngine = function(str, data) {
// 如果数据是数组
if (data instanceof Array) {
// 缓存渲染模板结果
var html = "";
// 数据索引
var i = 0;
// 数据长度
var len = data.length;
// 遍历数据
for (; i < len; i++) {
// 缓存模板渲染结果,也可以写成
// html += arguments.callee(str, data[i]) ;
html += _getTpl(str)(data[i]);
}
// 返回模板渲染最终结果
return html;
} else {
// 返回模板渲染结果
return _getTpl(str)(data);
}
};
/***
* 获取模板
* @param str 模板容器id,或者模板字符串
**/
var _getTpl = function(str) {
// 获取元素
var ele = document.getElementById(str);
// 如果元素存在
if (ele) {
// 如果是input或者textarea表单元素,则获取该元素的value值,否则获取元素的内容
var html = /^(textarea | input)$/i.test(ele.nodeName) ? ele.value : ele.innerHTML;
// 编译模板
return _compileTpl(html);
} else {
// 编译模板
return _compileTpl(str);
}
};
// 处理模板
var _dealTpl = function(str) {
// 左分隔符
var _left = "{%";
// 右分隔符
var _right = "%}";
// 显示转化为字符串
return String(str)
// 转义标签内的<如:<div>{%if(a&lt;b)%}</div> -> <div>{%if(a<b)%}</div>
.replace(/&lt;/g, "<")
// 转义标签内的>
.replace(/&gt;/g, ">")
// 过滤回车符,制表符,回车符
.replace(/[\r\t\n]/g, "")
// 替换内容
.replace(new RegExp(_left + "=(.*?)" + _right, "g"), "',typeof($1) === 'undefined' ? '' : $1, '")
// 替换左分隔符
.replace(new RegExp(_left, "g"), "');")
// 替换右分隔符
.replace(new RegExp(_right, "g"), "template_array.push('"); };
/***
* 编译执行
* @param str 模板数据
**/
var _compileTpl = function(str) {
// 编译函数体
var fnBody = "var template_array=[];\nvar fn=(function(data){\nvar template_key='';\nfor(key in data){\ntemplate_key +=(''+key+'=data[\"'+key+'\"];');\n}\neval(template_key);\ntemplate_array.push('" + _dealTpl(str) + "');\ntemplate_key=null;\n})(templateData);\nfn=null;\nreturn template_array.join('') ;";
// 编译函数
return new Function("templateData", fnBody);
}; // 返回
return _TplEngine;
});
<!-- demo.html -->
<!DOCTYPE html>
<html> <head>
<title>Widget模式</title>
</head> <body>
<div id="app"></div>
</body>
<script type="text/javascript">
(function(F) {
const moduleCache = {}; function getUrl(moduleName) {
return String(moduleName).replace(/\.js$/g, "") + ".js"
} function loadScript(src) {
let _script = document.createElement("script");
_script.type = "text/javascript";
_script.charset = "UTF-8";
_script.async = true;
_script.src = src;
document.body.appendChild(_script);
} function setModule(moduleName, params, callback) {
let _module = null,
fn = null;
if (moduleCache[moduleName]) {
_module = moduleCache[moduleName];
_module.status = "loaded";
_module.exports = callback ? callback.apply(_module, params) : null;
while (fn = _module.onload.shift()) {
fn(_module.exports)
}
} else {
callback && callback.apply(null, params);
}
} function loadModule(moduleName, callback) {
let _module = "";
if (moduleCache[moduleName]) {
_module = moduleCache[moduleName];
if (_module.status === "loaded") {
// 这个很重要,loadModule一定是异步的,effectiveJS 上的某一条建议有写,永远不要同步的调用异步函数,这非常重要
setTimeout(callback(_module.exports), 0);
} else {
// 加载完成的时候调用
_module.onload.push(callback);
}
} else {
// 第一次加载
moduleCache[moduleName] = {
moduleName: moduleName,
status: "loading",
exports: null,
onload: [callback]
};
loadScript(getUrl(moduleName));
}
} F.module = function(...args) {
// 获取模块构造函数(参数数组中最后一个参数成员)
let callback = args.pop();
// 获取依赖模块(紧邻回调函数参数,且数据类型为数组)
let deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [];
// 该模块url(模块ID)
let url = args.length ? args.pop() : null;
// 依赖模块序列
let params = [];
// 未加载的依赖模块数量统计
let depsCount = 0; if (deps.length) {
deps.forEach((v, i) => {
// 增加未加载依赖模块数量统计
depsCount++;
// 异步加载依赖模块
loadModule(deps[i], function(mod) {
// 依赖模块序列中添加依赖模块数量统一减一
depsCount--;
params[i] = mod;
// 如果依赖模块全部加载
if (depsCount === 0) {
// 在模块缓存器中矫正该模块,并执行构造函数
setModule(url, params, callback);
}
});
})
} else { // 无依赖模块,直接执行回调函数
// 在模块缓存器中矫正该模块,并执行构造函数
setModule(url, [], callback);
}
} })((() => window.F = ({}))());
</script>
<!-- srcpt模板内容 -->
<script type="text/template" id="tpl">
<div id="tag_cloud">
{% for(var i = 0; i < tagCloud.length; i++){
var ctx = tagCloud[i] ; %}
<a href="#" class="tag_item
{% if(ctx["is_selected"]){ %}
selected
{% } %}" title="{%=ctx["title"]%}">{%=ctx["text"]%}
</a>
{% } %}
</div>
</script>
<!-- 自定义模板 -->
<!--===============模板种类结束===========-->
<script type="text/javascript">
// 模拟数据
var data = {
tagCloud: [
{ is_selected: true, title: "Pattern", text: "设计模式" },
{ is_selected: false, title: "HTML", text: "HTML" },
{ is_selected: null, title: "CSS", text: "CSS" },
{ is_selected: "", title: "JavaScript", text: "JavaScript" },
]
} F.module(["./template", "./dom"], function(template, dom) {
// 服务器端获取到data数据逻辑
// 创建组件视图逻辑
var str = template("tpl", data);
dom.html("app", str);
// 组件其他交互逻辑
});
</script>
</html>

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://en.wikipedia.org/wiki/Web_widget
https://segmentfault.com/a/1190000019541819
https://blog.csdn.net/yuzhiboyouzhu/article/details/78998860

Widget模式的更多相关文章

  1. js设计模式总结5

    1.同步模块模式 随着页面功能的增加,系统的业务逻辑越来越复杂.多人开发的功能经常耦合在一起.有时分配任务给多人实现的时候,常常因为某一处功能耦合了很多人的代码,出现排队修改的现象,这很不利于团队开发 ...

  2. Flex 加载pdf

    如果想要在flex加载pdf,虽然pdf格式是开源的,但是自己去解析太麻烦了,pdf还要分页之类的,现在网上各种文档上传可以在线看很多都是pdf,当然也有word,excel之类,其实很多都是转了sw ...

  3. Sencha ExtJS 6 Widget Grid 入门

    最近由于业务需要,研究了一下Sencha ExtJS 6 ,虽然UI和性能上据相关资料说都有提升,但是用起来确实不太顺手,而且用Sencha cmd工具进行测试和发布,很多内部细节都是隐藏的,出了问题 ...

  4. Android开发之MVP模式的使用

    前几天发现,在Android项目代码里有一个Activity类行数居然有1000多行,而600行左右都是逻辑控制,真正和页面控件处理相关的代码不多,虽然可以用#region <>...#e ...

  5. MVP模式(Android)

    以前在写项目的时候,没有过多考虑架构模式的问题,因为之前一直做J2EE开发,而J2EE都是采用MVC模式进行开发的,所以在搭建公司项目的时候,也是使用类似MVC的架构(严格来讲,之前的项目还算不上MV ...

  6. Android Activity的加载的模式

    ---恢复内容开始--- 本文来自http://www.cnblogs.com/lwbqqyumidi/p/3771542.html launchMode在多个Activity跳转的过程中扮演着重要的 ...

  7. Android开发之---Activity启动模式

    在Android开发中,启动一个新的activity我们可以使用startActivity或startActivityForResult,Android系统使用栈的方式来管理一个APP的页面显示与保存 ...

  8. Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解.[size=1.8em]Handler+Runna ...

  9. 彻底弄懂Activity四大启动模式

    最近有几位朋友给我留言,让我谈一下对Activity启动模式的理解.我觉得对某个知识点的理解必须要动手操作才能印象深刻,所以今天写一篇博文,结合案例理解Activity启动模式.由于之前看过" ...

  10. Extjs MVC开发模式详解

    Extjs MVC开发模式详解   在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开发模式, ...

随机推荐

  1. 【MLA】一种内存泄漏分析方法

    项目地址:skullboyer/MLA (github.com) 介绍 MLA 即 Memory Leak Analyzer,是一个排查内存泄漏的分析器 实现机制是在malloc时记录分配位置信息,在 ...

  2. 有了Composition API后,有些场景或许你不需要pinia了

    前言 日常开发时有些业务场景功能很复杂,如果将所有代码都写在一个vue组件中,那个vue文件的代码量可能就几千行了,维护极其困难.这时我们就需要将其拆分为多个组件,拆完组件后就需要在不同组件间共享数据 ...

  3. SpringBoot03:首页国际化

    页面国际化 有的时候,我们的网站会去涉及中英文甚至多语言的切换,这时候我们就需要学习国际化! 1.配置文件编写 首先在resources资源文件下新建一个i18n目录,存放国际化配置文件 新建一个lo ...

  4. SpringMVC03—RestFul和Controller

    控制器Controller 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现. 控制器负责解析用户的请求并将其转换为一个模型. 在Spring MVC中一个控制器类可以包含多个 ...

  5. [转帖]tiup cluster restart

    https://docs.pingcap.com/zh/tidb/stable/tiup-component-cluster-restart   注意 重启过程中会有一段时间服务不可用. 语法 tiu ...

  6. [转帖]KingbaseES V8R6 中walminer的使用

    https://www.cnblogs.com/kingbase/p/17315750.html 前言 walminer工具可以帮助dba挖掘wal日志中的内容,看到某时间对应数据库中的具体操作.例如 ...

  7. stress-NG 磁盘测试结果-全国产信创部分验证

    stress-NG 磁盘测试结果 摘要 前几天分别还是用了redis-benchmark还有specjvm2008进行了多种系统的压测 得出了信创CPU的一些简单结论 但是一直还没有压测磁盘, 今天想 ...

  8. Oracle的awr的学习与整理

    Oracle的awr的学习与整理 背景 本来想上周末进行一下总结和汇总 因为周末两天进行了一次长时间的培训.所以没有成行. 只能在工作之余找时间进行总结. 数据库部分自己一个不是很强. 其实也比较抗拒 ...

  9. [转帖]windows使用net user add用户并加入管理员,从而支持rdp远程登陆访问

    C:\phpstudy_pro\WWW> net user test2 /add 命令成功完成.    C:\phpstudy_pro\WWW> net user test2 Huawei ...

  10. 【计数,DP】ABC306Ex Balance Scale

    Problem Link 现在有 \(n\) 个球,每个球有一个重量,重量未知.接下来会进行 \(m\) 次称重,每次给定 \(a_i\) 和 \(b_i\),比较这两个球的重量,结果可能是 \(&g ...