热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用avalonmsui绑定实现基于组件的开发

使用avalonms-ui绑定实现基于组件的开发让日子过得轻松,必须让代码不断往上抽象。avalon的一切就是为这个崇高的目的而迸进——操作数据即操作DOM,远离DOM进行前端开发。javascri

使用avalon ms-ui绑定实现基于组件的开发

让日子过得轻松,必须让代码不断往上抽象。avalon的一切就是为这个崇高的目的而迸进——操作数据即操作DOM,远离DOM进行前端开发。Javascript之所以在生命的前十年碌碌无为,都是因为开发者被这些兼容性问题搞怕了。在前十年,人们都是用着那些很底层的原生DOM方法,JS方法进行编程,效率异常低下。随着Prototype.js, jQuery等库的出现, 把几乎所有能封装都给封装了, 我们的生活才变得美好起来。但开发企业内部管理系统,却鲜有听说用jQuery来搞,大家都爱用开箱即用的EXT UI框架。UI组件无疑是比jQuery这些DOM操作对象强上一截,是更高程度的抽象与封装。但组件的制定性一般很差,比如说zTree,它就使用插件化来进行扩展。对于目前业界给出的方案,avalon是怎么做的呢?

首先,像ms-class, ms-hover, ms-on其实是在jQuery的事情,不同的是,它没有依赖Sizzle这些动则上千行的选择器引擎,而是通过扫描与转换绑定属性实现。绑定属性会转换一个求值函数,求值函数内部与ViewModel挂钩,因此能即时同步,外部套着一个DOM操作函数,比如说ms-class,就是根据求值函数的返回值的真假来调用toggleClass, ms-hover则是通过绑定mouseenter, mouseleave来toggleClass, ms-checked是处理表单元素的checked属性, ms-visible负责元素的显示隐藏, ms-if负责将元素移出或插入DOM……短短一个绑定属性就包含非常高密度的DOM操作,比jQuery更加write less, do more, do simple!这个园子已经有人在用我的avalon,反映都是很不错的,当然也提了不少改进见意。

其次是组件层面,avalon与angular一样,提供面向组件的开发,但比angular用法简单多了。avalon参考了bootstrap这个twitter开发的UI库,利用data-*属性进行功能定制(当然,它还有其他形式的配置方式)。并且像bootstrap那样,只要引入JS,在相关元素上添加ms-ui="组件名",元素节点就会转换为UI组件,一行代码也不用你写。

ms-ui绑定能给我们带来以下好处:将元素上的绑定属性减到最少,只需添加ms-ui="组件名",或最多添加data-id="xxx", 保证对HTML最少限度的干扰。也就是说,美工与前端有了非常明确的分工。而data-id中的值则是对应ViewModel的ID,我们可以通过此ID在avalon.models[ID]中取得ViewModel,从而实现操作数据即操作DOM。

基于组件,使得我们的表现层变成由一个个“积木”堆积而成。Page规则转向如何协调这些组件的运作,而不是盯着一个个元素节点。不过avalon已经帮你搞定了大部分了。比如说,基本URL远程请求不同的资源,然后转换为ViewModel,然后生成某个页面的区域。还有消息中心(也叫事件中心),由于ViewModel本来就是用观察者模式实现的,一个活生生的消息中心,你不需再造轮子。随着我们项目的深入,我们积累的各种积木也就越来越多,也就是,我们的工作就越轻松,工作效率也就越来越高。

与其他UI库不一样的,avalon编写UI太简单,太方便了,这得益于它强大的双向绑定机制。

拿avalon.tabs组件的源码举例吧:

(function(avalon) {
    var defaults = {
        active: 0,
        event: "click", //可以使用click, mouseover
        collapsible: false,
        bottom: false,
        removable: false
    };
    avalon.ui.tabs = function(element, id, opts) {
        var el, tabsParent, tabs = [], tabpanels = [];
        var $element = avalon(element);
        //1,设置参数对象optiOns= defaults + opts + $element.data()
        var optiOns= avalon.mix({}, defaults);
        if (typeof opts === "object") {
            avalon.mix(options, opts.$json || opts);
        }
        avalon.mix(options, $element.data());
         
        $element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");
 
        //2, 清空它内部所有节点,并收集其内容,构建成tabs与tabpanels两个数组
        while (el = element.firstChild) {
            if (!tablist && (el.tagName === "UL" || el.tagName === "OL")) {
                tabsParent = el;
            }
            if (el.tagName === "DIV") {
                tabpanels.push(el.innerHTML);
            }
            element.removeChild(el);
        }
 
        for (var i = 0; el = tabsParent.children[i++]; ) {
            tabs.push(el.innerHTML);
        }
        //3 设置动态模板
        var tablist = '
     +
                ' ms-class-ui-corner-all="!bottom"  ms-class-ui-corner-bottom="bottom" ms-each-tab="tabs">' +
                '
  •  +
  •                 ' ms-class-ui-corner-top="!bottom"' +
                    ' ms-class-ui-corner-bottom="bottom"' +
                    ' ms-class-ui-tabs-active="active == $index"' +
                    ' ms-class-ui-state-active="active == $index"' +
                    ' ms-' + options.event + '="activate"' +
                    ' ms-hover="ui-state-hover"' + // float: left; margin: 0.4em 0.2em 0 0; cursor: pointer;这样jquery ui没有封装进去
                    ' >{{tab|html}}';
            var panels = '
     +
                    ' ms-class-ui-corner-bottom="!bottom"' +
                    ' ms-visible="active == $index" >{{panel|html}}
    ';
            //4 构建组建的ViewModel
            var model = avalon.define(id, function(vm) {
                vm.active = options.active;
                vm.collapsible = options.collapsible;
                vm.tabs = tabs;
                vm.tabpanels = tabpanels;
                vm.removable = options.removable;
                vm.activate = function(e) {
                    e.preventDefault();
                    vm.active = this.$scope.$index;
                };
                vm.remove = function(e) {
                    e.preventDefault();
                    var index = this.$scope.$index;
                    vm.tabs.removeAt(index);
                    vm.tabpanels.removeAt(index);
                    avalon.nextTick(function() {
                        vm.active = 0;
                    });
                };
                vm.bottom = options.bottom;
            });
             
            avalon.nextTick(function() {
                //5 当这一波扫描过来,再将组建的DOM结构插入DOM树,并绑定ms-*属性,然后开始扫描
                element.innerHTML = options.bottom ? panels + tablist : tablist + panels;
                element.setAttribute("ms-class-ui-tabs-collapsible", "collapsible");
                element.setAttribute("ms-class-tabs-bottom", "bottom");
                avalon.scan(element, model);
            });
            return model;
        };
    })(window.avalon);

    它基本分为五个步骤,首先怎么也有5个参数给你传过来,element为绑定了ms-ui的元素节点,id为data-id的值,没有框架为你随机生成一个,opts,这个是ms-ui-optsName="uiName"的一个参数,opts就是框架在众多ViewModel通过hasOwnerProperty操作分别出来的参数对象。然后第一步,就是设置配置对象,它由defaults + opts + $element.data()组成,制定性应该非常强与灵活。

    第二步,将element元素的内部节点进行处理,有时它们也有绑定属性,但我们不需要立即处理它们——扫描器是从上到下扫描,扫描过后来移除它们,因此阻止它们此时被扫描到——最好方法是将它们移出DOM树。这个过程可能还要做些操作,随你喜欢。

    第三步,编写UI的HTML结构。

    第四步,定义ViewModel,这时它的ID为我们上面的传参。

    第五步,重新开始扫描,但这时,我们不需要整个页面都扫描了,只从这个元素开始就行。

    有关这个tabs的示例可到这里查看。

    这里有两个重点,一个是扫描。扫描总是从上到下,从左到右,想让它不被扫描,有三个办法,ms-skip,这几乎是永久性的,ms-important,只有ViewModel匹配才进行此区域,最后一个是移出DOM树。扫描不但是为了转换绑定,还起到存储这些关键节点的作用(竟然这些节点做了绑定,说明要通过JS处理,而在以前这些都是通过选择器引擎来做,但一般的jQueryer很少自觉将它们缓存起来,每次用到时都重新选择,不断地遍历DOM树)。第二个是UI组件的ViewModel的构建,它是我们远离DOM编程的关键。它拥有所有关键参数与方法,相当于后端的XML配置文件。当然你也可以什么也不做,defaults其实已经调试好一切了。

     
     
     
    标签: Javascript
    推荐阅读
    • Web开发框架概览:Java与JavaScript技术及框架综述
      Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
    • 解决Bootstrap DataTable Ajax请求重复问题
      在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
    • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
      秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
    • 深入解析HTML5字符集属性:charset与defaultCharset
      本文将详细介绍HTML5中新增的字符集属性charset和defaultCharset,帮助开发者更好地理解和应用这些属性,以确保网页在不同环境下的正确显示。 ... [详细]
    • Python 数据可视化实战指南
      本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
    • 全面解析JavaScript代码注释技巧与标准规范
      在Web前端开发中,JavaScript代码的可读性和维护性至关重要。本文将详细介绍如何有效地使用注释来提高代码的可读性,并探讨JavaScript代码注释的最佳实践和标准规范。通过合理的注释,开发者可以更好地理解和维护复杂的代码逻辑,提升团队协作效率。 ... [详细]
    • 帝国CMS中的信息归档功能详解及其重要性
      本文详细解析了帝国CMS中的信息归档功能,并探讨了其在内容管理中的重要性。通过归档功能,用户可以有效地管理和组织大量内容,提高网站的运行效率和用户体验。此外,文章还介绍了如何利用该功能进行数据备份和恢复,确保网站数据的安全性和完整性。 ... [详细]
    • 本文深入解析了 jQuery 中用于扩展功能的三个关键方法:`$.extend()`、`$.fn` 和 `$.fn.extend()`。其中,`$.extend()` 用于扩展 jQuery 对象本身,而 `$.fn.extend()` 则用于扩展 jQuery 的原型对象,使自定义方法能够作为 jQuery 实例的方法使用。通过这些方法,开发者可以轻松地创建和集成自定义插件,增强 jQuery 的功能。文章详细介绍了每个方法的用法、参数及实际应用场景,帮助读者更好地理解和运用这些强大的工具。 ... [详细]
    • 触发器的稳态数量分析及其应用价值
      本文对数据库中的SQL触发器进行了稳态数量的详细分析,探讨了其在实际应用中的重要价值。通过研究触发器在不同场景下的表现,揭示了其在数据完整性和业务逻辑自动化方面的关键作用。此外,还介绍了如何在Ubuntu 22.04环境下配置和使用触发器,以及在Tomcat和SQLite等平台上的具体实现方法。 ... [详细]
    • 本文介绍了如何利用ObjectMapper实现JSON与JavaBean之间的高效转换。ObjectMapper是Jackson库的核心组件,能够便捷地将Java对象序列化为JSON格式,并支持从JSON、XML以及文件等多种数据源反序列化为Java对象。此外,还探讨了在实际应用中如何优化转换性能,以提升系统整体效率。 ... [详细]
    • 为开发者提供了一系列实用的参考网站和资源链接,包括HTML速查手册( 和 ),帮助开发者快速查找和学习相关技术知识。此外,还涵盖了其他重要的开发工具和文档,为编程工作提供全面支持。 ... [详细]
    • 本文全面解析了JavaScript中的DOM操作,并提供了详细的实践指南。DOM节点(Node)通常代表一个标签、文本或HTML属性,每个节点都具有一个nodeType属性,用于标识其类型。文章深入探讨了DOM节点的创建、查询、修改和删除等操作,结合实际案例,帮助读者更好地理解和掌握DOM编程技术。 ... [详细]
    • JavaScript XML操作实用工具类:XmlUtilsJS技巧与应用 ... [详细]
    • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
    • 在掌握Promise调用链的过程中,理解其在异步执行中的核心作用至关重要。链式调用不仅简化了代码结构,提高了可读性,还增强了程序的健壮性和维护性。类似于jQuery中常用的链式调用,如 `$(#app).show().css('color', 'red')`,Promise的链式调用通过 `.then()` 方法实现了异步操作的无缝衔接,使得复杂的异步流程更加直观和高效。掌握这些技巧将有助于开发者更好地处理异步编程中的常见问题,提升开发效率。 ... [详细]
    author-avatar
    一恒谢永泰_661
    这个家伙很懒,什么也没留下!
    PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有