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. SV 并发线程

    内容 assign d = a & b; assign e = b | c; begin...end之间的语句是串行执行的 fork....join语句是并行执行的 逻辑仿真工具中的并发性 仿 ...

  2. [转帖]性能调优:理解Set Statistics IO输出

    https://www.cnblogs.com/woodytu/p/4535658.html 性能调优是DBA的重要工作之一.很多人会带着各种性能上的问题来问我们.我们需要通过SQL Server知识 ...

  3. [转帖]集群监控之 —— ipmi操作指南

    https://www.cnblogs.com/gaoyuechen/p/8506930.html 这两天,配置了一堆500来个节点的大型集群,被ipmi的问题困扰了一天半,到下午16:40,终于解决 ...

  4. [转帖]Linux shell 单引号和双引号

    https://www.cnblogs.com/airoot/p/15324883.html 在编写shell脚本的时候经常会用到引号,有些时候却老是忘记单引号和双引号之间的区别,所以就整理一下供以后 ...

  5. JVM内存配置的再次思考

    JVM内存配置的再次思考 摘要 最近研究过不少内存分配相关的处理 今天晚上突然感觉还不是非常系统. 还是想能够细致的在学习一下. 希望能够慢慢的拾遗,提高自己 操作系统内存的使用情况 本文主要想思考l ...

  6. [翻译]-hugePage的简要说明--部分内容

    hugePage的简要说明 本篇文档的主旨给linux内核支持的大页内存做一个简要的概述. 大页内存的实现是建立在大多数现代架构所都支持的多级页大小的特性之上的. 举例: x86架构下大部署CPU 的 ...

  7. sed 删除包含某字符的一行 给包含某字符的一行添加 逗号的简单方法

    今天处理环境折腾死了 方法: #给包含 configdata 的一行 添加 逗号结尾 find . -name "*.json" |xargs sed -i '/configdat ...

  8. vite配置开发环境和生产环境

    为什么需要境变量的配置 在很多的时候,我们会遇见这样的问题. 开发环境的接口是:http://test.com/api 但是我们的生产环境地址是:http://yun.com/api 此时,我们打包的 ...

  9. kettle系统列文章01---安装与配置

    1).到官网下载需要安装的kettle版本,目前最新版本4.2,官网地址:http://kettle.pentaho.org,我们是使用的版本是kettle3.2 2).本地安装jdk 1.4或以上版 ...

  10. 简单的git拉取修改提交用法

    打开终端,进入要存放代码的本地文件夹,并使用git clone命令克隆远程仓库到本地: git clone https://github.com/username/repo.git 这里的userna ...