The Widget
mode refers to breaking down a page into components by borrowing the idea of Web Widget
. It involves developing components aimed at creating a complete page. Web Widget
refers to a block of code that can be executed on any page. The Widget
mode does not belong to the category of the 23 design patterns defined in general. It is usually considered a broad architectural design pattern.
Modular development allows the functions of a page to be refined, and each functional module can be implemented one by one to meet the system's requirements. This is a very good programming practice. With the help of a simple template engine implemented in the simple template pattern, this can be very easily achieved. This approach is more suitable for collaborative development by multiple teams, reducing the probability of coupling effects caused by the creation of functions or views. Diversification of components will also help in building richer pages and increase the reuse rate of components.
// 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() {
/***
* Template engine, the entry point for compiling templates
* @param str Module container id or template string
* @param data Rendering data
**/
var _TplEngine = function(str, data) {
// If the data is an array
if (data instanceof Array) {
// Cache the template rendering result
var html = "";
// Data index
var i = 0;
// Data length
var len = data.length;
// Iterate through the data
for (; i < len; i++) {
// Cache the template rendering result, or you can write it as
// html += arguments.callee(str, data[i]) ;
html += _getTpl(str)(data[i]);
}
// Return the final result of template rendering
return html;
} else {
// Return the template rendering result
return _getTpl(str)(data);
}
};
/***
* Get template
* @param str Template container id, or template string
**/
var _getTpl = function(str) {
// Get the element
var ele = document.getElementById(str);
// If the element exists
if (ele) {
// If it is an input or textarea form element, then get the value of the element, otherwise get the content of the element
var html = /^(textarea | input)$/i.test(ele.nodeName) ? ele.value : ele.innerHTML;
// Compile the template
return _compileTpl(html);
} else {
// Compile the template
return _compileTpl(str);
}
};
// Handle the template
var _dealTpl = function(str) {
// Left delimiter
var _left = "{%";
// Right delimiter
var _right = "%}";
// Convert to string
return String(str)
// Escape < inside tags, e.g.: <div>{%if(a<b)%}</div> -> <div>{%if(a<b)%}</div>
.replace(/</g, "<")
// Escape >
.replace(/>/g, ">")
// Filter out line breaks, tabs, and carriage returns
.replace(/[\r\t\n]/g, "")
// Replace content
.replace(new RegExp(_left + "=(.*?)" + _right, "g"), "',typeof($1) === 'undefined' ? '' : $1, '")
// Replace left delimiter
.replace(new RegExp(_left, "g"), "');")
// Replace right delimiter
.replace(new RegExp(_right, "g"), "template_array.push('");
};
/***
* Compile and execute
* @param str Template data
**/
var _compileTpl = function(str) {
// Compile function body
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('') ;";
// Compile function
return new Function("templateData", fnBody);
};
// Return
return _TplEngine;
});
<!-- demo.html -->
<!DOCTYPE html>
<html>
<head>
<title>Widget mode</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") {
// This is very important. loadModule must be asynchronous. An advice in EffectiveJS says: never synchronously call an asynchronous function, this is very important.
setTimeout(callback(_module.exports), 0);
} else {
// Call when the loading is completed
_module.onload.push(callback);
}
} else {
// First load
moduleCache[moduleName] = {
moduleName: moduleName,
status: "loading",
exports: null,
onload: [callback]
};
loadScript(getUrl(moduleName));
}
}
F.module = function(...args) {
// Get the module constructor function (the last member in the arguments array)
let callback = args.pop();
// Get dependent modules (adjoining the callback function as an array and data type is an array)
let deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [];
// The URL of the module (module ID)
let url = args.length ? args.pop() : null;
// Dependent modules sequence
let params = [];
// Number of unloaded dependent modules
let depsCount = 0;
if (deps.length) {
deps.forEach((v, i) => {
// Increase the count of unloaded dependency modules
depsCount++;
// Asynchronously load dependency modules
loadModule(deps[i], function(mod) {
// Reduce the count of dependency modules in the sequence by one
depsCount--;
params[i] = mod;
// If all dependency modules are loaded
if (depsCount === 0) {
// Correct the module in the module cache and execute the constructor function
setModule(url, params, callback);
}
});
})
} else { // No dependency modules, execute the callback function directly
// Correct the module in the module cache and execute the constructor function
setModule(url, [], callback);
}
};
})((() => window.F = ({}))());
</script>
<!-- Template content -->
<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>
<!-- Custom template -->
<!--===============End of template type===========-->
<script type="text/javascript">
// Simulated data
var data = {
tagCloud: [
{ is_selected: true, title: "Pattern", text: "Design Pattern" },
{ 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) {
// Logic for retrieving data from the server
// Creating component view logic
var str = template("tpl", data);
dom.html("app", str);
// Other component interaction logic
});
</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