热门标签 | 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
    推荐阅读
    • 本文介绍了多种Eclipse插件,包括XML Schema Infoset Model (XSD)、Graphical Editing Framework (GEF)、Eclipse Modeling Framework (EMF)等,涵盖了从Web开发到图形界面编辑的多个方面。 ... [详细]
    • iOS 小组件开发指南
      本文详细介绍了iOS小部件(Widget)的开发流程,从环境搭建、证书配置到业务逻辑实现,提供了一系列实用的技术指导与代码示例。 ... [详细]
    • vue引入echarts地图的四种方式
      一、vue中引入echart1、安装echarts:npminstallecharts--save2、在main.js文件中引入echarts实例:  Vue.prototype.$echartsecharts3、在需要用到echart图形的vue文件中引入:   importechartsfrom"echarts";4、如果用到map(地图),还 ... [详细]
    • 本文详细介绍了利用JavaScript实现的五种不同的网页弹出窗口技术,包括全屏窗口、全屏模式窗口、带收藏链接工具栏的窗口、网页对话框及HTA窗口。 ... [详细]
    • Eclipse JFace Text框架中IDocument接口的getNumberOfLines方法详解与编程实例 ... [详细]
    • 集群与负载均衡技术解析
      本文探讨了集群(Cluster)的概念,即通过网络连接的一组计算机系统,它们作为一个整体提供服务,实现分布式计算。文章还详细介绍了负载均衡技术,旨在提高网络服务的效率和可靠性。 ... [详细]
    • 本文介绍了在解决Hive表中复杂数据结构平铺化问题后,如何通过创建视图来准确计算广告日志的曝光PV,特别是针对用户对应多个标签的情况。同时,详细探讨了UDF的使用方法及其在实际项目中的应用。 ... [详细]
    • 本文详细介绍了PHP中的几种超全局变量,包括$GLOBAL、$_SERVER、$_POST、$_GET等,并探讨了AJAX的工作原理及其优缺点。通过具体示例,帮助读者更好地理解和应用这些技术。 ... [详细]
    • 本文由公众号【数智物语】(ID: decision_engine)发布,关注获取更多干货。文章探讨了从数据收集到清洗、建模及可视化的全过程,介绍了41款实用工具,旨在帮助数据科学家和分析师提升工作效率。 ... [详细]
    • ArcBlock 发布 ABT 节点 1.0.31 版本更新
      2020年11月9日,ArcBlock 区块链基础平台发布了 ABT 节点开发平台的1.0.31版本更新,此次更新带来了多项功能增强与性能优化。 ... [详细]
    • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
    • 2023年,Android开发前景如何?25岁还能转行吗?
      近期,关于Android开发行业的讨论在多个平台上热度不减,许多人担忧其未来发展。本文将探讨当前Android开发市场的现状、薪资水平及职业选择建议。 ... [详细]
    • 如何将955万数据表的17秒SQL查询优化至300毫秒
      本文详细介绍了通过优化SQL查询策略,成功将一张包含955万条记录的财务流水表的查询时间从17秒缩短至300毫秒的方法。文章不仅提供了具体的SQL优化技巧,还深入探讨了背后的数据库原理。 ... [详细]
    • 现在越来越多的人使用IntelliJIDEA,你是否想要一个好看的IDEA主题呢?本篇博客教你如何设置一个美美哒IDEA主题,你也可以根据 ... [详细]
    • 织梦系统多条件联动筛选功能详细教程及删除操作指南
      多条件联动筛选功能广泛应用于图片展示、装修设计、机械设备和在线商城等场景,通常筛选条件应聚焦于用户最关心的要素,而非涵盖所有可能的选项。在DedeCMS中,多条件筛选的PHP开发并未内置删除已选条件的功能,但通过理解PHP筛选与JS筛选的不同机制,实现这一功能相对简单且易于操作。 ... [详细]
    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社区 版权所有