Widget模式

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-12-15

Web Widget指的是一块可以在任意页面中执行的代码块,Widget模式是指借用Web Widget思想将页面分解成部件,针对部件开发,最终组合成完整的页面。

Widget模式是一种页面视图开发模式,它将页面粒度化,分解成一个个组件,当然一个组件也对应着一个模块,一个完整的组件包含该模块的完整的视图和一套完整的功能。

所以Widget模式开发中一个组件就要对应一个文件了,而不是某个功能或者某个视图,因此在这个组件文件中你要做两件事,第一创建视图,第二添加相应的功能。

而创建视图就要有一个模板渲染方法,才能使开发更高效。

以下就是一个模块引擎的实现:

// lib/template.js
// F.module 参照上一章 异步模块模式
// 模块引擎模块
F.module('lib/template', function() {
  var _TplEngine = function(str, data) {
    // 如果数据是数组,则遍历数组逐一渲染。如果是对象则直接渲染,将返回的字符串拼接然后返回
    if (data instanceof Array) {
      var html = '';
      for (var i = 0; i < data.length; i++) {
        html += _getTpl(str)(data[i]);
      }
      return html;
    } else {
      return _getTpl(str)(data);
    }
  }

  // 获取模板
  // 如果str是一个id且元素存在,则判断是input或textarea表单元素,是则获取该元素的value,反之获取元素的innerHTML
  // 如果不是,则将str看作模板字符串
  var _getTpl = function(str) {
    var el = document.getElementById(str);
    if (el) {
      var html = /^(textarea|input)$/i.test(el.nodeName) ? el.value : el.innerHTML;
      return _compileTpl(html);
    }
    return _compileTpl(str);
  };

  // 处理模板 <a>{%=test%}</a> 转换成 如下js语句字符串
  // template_array.push('<a>',typeof(test) === 'undefined' ? '' : test,'</a>')
  var _dealTpl = function(str) {
    var _left = '{%';
    var _right = '%}';
    
    var s = String(str)
      // 转译标签内的<和>  比如 <p> m&lt;n </p> 转换成 <p> m<n </p> 
      .replace(/&lt;/g, '<')
      .replace(/&gt;/g, '>')
      // 过滤回车、换行、制表符
      .replace(/[\r|\n|\t]/g, '')
      // {%= %}会优先替换dom中使用的变量
      .replace(new RegExp(_left + '=(.*?)' + _right, 'g'), `',typeof($1) === 'undefined' ? '' : $1,'`)
      // 所有的写在js中的语句都使用 {% %} 包裹,包括 {} 大括号
      // 所有写在html中的语句不需要包裹
      // 将 {% 替换为 ');  是因为最后会在前面拼接一个template_array.push(' 所以 {% 其实是每个js语句的结尾,将dom内容使用js语句push进去
      .replace(new RegExp(_left, 'g'), `');`)
      // 将 %} 替换为 template_array.push(', 是因为最后会在后面拼接 ') 所以 %} 其实是每个js语句的开始 将dom内容使用js语句push进去
      .replace(new RegExp(_right, 'g'), `template_array.push('`);

    return `template_array.push('${s}')`; // 返回都是一个能够直接eval的js字符串
  };

  // 编译执行
  var _compileTpl = function(str) {
    return function(templateData) {
      var template_array = [];
      
      // 自执行
      (function(data) {
        var template_key = '';
        for (var key in data) {
          template_key += `var ${key} = data['${key}'];`;
        }
        eval(template_key);
        eval(_dealTpl(str));
        template_key = null;
      })(templateData);
      
      return template_array.join('');
    }
  };
  return _TplEngine;
})

这样使用:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="test"></div>
  <!-- 参照上一张 异步模块模式的main.js -->
  <script src="main.js"></script>
  <script type="text/template" id="demo_script">
    <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>
    var data = {
      tagCloud: [{
          is_selected: true,
          title: '这是一本设计模式书',
          text: '设计模式'
        },
        {
          is_selected: false,
          title: '这是一本HTML',
          text: 'HTML'
        },
        {
          is_selected: null,
          title: '这是一本CSS',
          text: 'CSS'
        },
        {
          is_selected: '',
          title: '这是一本javascript',
          text: 'javascript'
        },
      ],
      test: 'abc',
      is_selected: false
    }

    // dom模块的实现 参照上一章 异步模块模式
    F.module(['lib/template', 'lib/dom'], function (template, dom) {
      var str = template('demo_script', data);
      dom.html('test', str);
    })

    // F.module(['lib/template', 'lib/dom'], function (template, dom) {
    //   var str = template('{% if (is_selected) { %} <a>{%=test%}</a> {% } else { %} <a>test2</a> {%}%}', data);
    //   dom.html('test', str);
    // })
  </script>
</body>

</html>

在上面的使用中,就结合 template 实现的模板引擎功能,完成了一个标签云组件模块。

Widget架构模式是页面开发模式,不仅仅是页面功能,甚至页面的每个组件模块都可以独立地开发,这更适合团队中多人开发。并且降低相互之间因功能或者视图创建的耦合影响概率。一个组件即一个文件,也让我们更好地管理一个页面,当然组件的多样化也会组建一个更丰富的页面,同样也会让组件的复用率提高。