Widget模式
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<b)%}</div> -> <div>{%if(a<b)%}</div>
.replace(/</g, "<")
// 转义标签内的>
.replace(/>/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模式的更多相关文章
- js设计模式总结5
1.同步模块模式 随着页面功能的增加,系统的业务逻辑越来越复杂.多人开发的功能经常耦合在一起.有时分配任务给多人实现的时候,常常因为某一处功能耦合了很多人的代码,出现排队修改的现象,这很不利于团队开发 ...
- Flex 加载pdf
如果想要在flex加载pdf,虽然pdf格式是开源的,但是自己去解析太麻烦了,pdf还要分页之类的,现在网上各种文档上传可以在线看很多都是pdf,当然也有word,excel之类,其实很多都是转了sw ...
- Sencha ExtJS 6 Widget Grid 入门
最近由于业务需要,研究了一下Sencha ExtJS 6 ,虽然UI和性能上据相关资料说都有提升,但是用起来确实不太顺手,而且用Sencha cmd工具进行测试和发布,很多内部细节都是隐藏的,出了问题 ...
- Android开发之MVP模式的使用
前几天发现,在Android项目代码里有一个Activity类行数居然有1000多行,而600行左右都是逻辑控制,真正和页面控件处理相关的代码不多,虽然可以用#region <>...#e ...
- MVP模式(Android)
以前在写项目的时候,没有过多考虑架构模式的问题,因为之前一直做J2EE开发,而J2EE都是采用MVC模式进行开发的,所以在搭建公司项目的时候,也是使用类似MVC的架构(严格来讲,之前的项目还算不上MV ...
- Android Activity的加载的模式
---恢复内容开始--- 本文来自http://www.cnblogs.com/lwbqqyumidi/p/3771542.html launchMode在多个Activity跳转的过程中扮演着重要的 ...
- Android开发之---Activity启动模式
在Android开发中,启动一个新的activity我们可以使用startActivity或startActivityForResult,Android系统使用栈的方式来管理一个APP的页面显示与保存 ...
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存 下面看他们的理解.[size=1.8em]Handler+Runna ...
- 彻底弄懂Activity四大启动模式
最近有几位朋友给我留言,让我谈一下对Activity启动模式的理解.我觉得对某个知识点的理解必须要动手操作才能印象深刻,所以今天写一篇博文,结合案例理解Activity启动模式.由于之前看过" ...
- Extjs MVC开发模式详解
Extjs MVC开发模式详解 在JS的开发过程中,大规模的JS脚本难以组织和维护,这一直是困扰前端开发人员的头等问题.Extjs为了解决这种问题,在Extjs 4.x版本中引入了MVC开发模式, ...
随机推荐
- 【scikit-learn基础】--『回归模型评估』之偏差分析
模型评估在统计学和机器学习中具有至关重要,它帮助我们主要目标是量化模型预测新数据的能力. 本篇主要介绍模型评估时,如何利用scikit-learn帮助我们快速进行各种偏差的分析. 1. **R² ** ...
- [转帖]如何理解 kernel.pid_max & kernel.threads-max & vm.max_map_count
https://www.cnblogs.com/apink/p/15728381.html 背景说明 运行环境信息,Kubernetes + docker .应用系统java程序 问题描述 首先从Ku ...
- [转帖]TiKV 多副本丢失以及修复实践
https://tidb.net/blog/ad45bad9#6%E6%80%BB%E7%BB%93 1实验目的 随着tidb使用场景的越来越多,接入的业务越来越重要,不由得想试验下tidb组件的高可 ...
- [转帖]TiDB 6.1 单机环境 On openEular 2003 SP3
https://tidb.net/book/book-rush/best-practice/other-practice/tidb61-on-openEular2003 背景 最近对国产操作系统很感 ...
- [转帖]通过配置优化KingbaseES服务器性能
目录 1. 概述 2. 数据库应用类型 3. 服务器参数 3.1. max_connections 3.2. shared_buffers 3.3. effective_cache_size 3.4. ...
- [转帖]写给想了解"集成电路"的朋友
https://zhuanlan.zhihu.com/p/602627000 寒假和朋友小聚,每当就专业问题展开谈话,很容易形成"一边热火朝天,一边大脑宕机"的局面.俗话说隔行如隔 ...
- 袋鼠云数栈产品中 AI+ 实现原理剖析
生产力工具 + AI 是不可逆转的趋势,慢慢的大模型能力通过 AI Agent 落地的工程化能力也开始趋于成熟.作为大数据产品的数栈也必然是需要借助 AI 能力提升产品竞争力. 去年 12 月,我们在 ...
- ToneGenerator Init failed Crash 崩溃
需求需要在扫码时产生一个短促的提示音, 搜了下像这样实现.测试时发现多次扫码后,会触发程序崩溃问题. 异常如下 java.lang.RuntimeException: Init failed at a ...
- logstash 与ElasticSearch:从CSV文件到搜索宝库的导入指南
logstash 与ElasticSearch:从CSV文件到搜索宝库的导入指南 使用 logstash 导入数据到 ES 时,由三个步骤组成:input.filter.output.整个导入过程可视 ...
- mysql删除表中重复记录
1.创建测试表,并插入数据 create table test( id int(11) primary key auto_increment comment '用户编号', username varc ...