[译]使用YUI 3开发Web应用的诀窍

YOLO26算法皮革表面缺陷检测+训练好的模型+124张数据集+pyqt可视化界面.zip 附训练好的 YOLO 权重与 PyQt 可视化检测界面,页面底部含可视化效果预览,毕设/课设开箱即用。 【数据集概况】 · 检测类别(中文):[颜色(color), 切(cut), 折叠(fold), 胶水(glue), 好的(good), 戳(poke)] 【训练曲线与评估图】 【模型训练配置】 参数 | 值 模型 | yolo26n 训练轮数 | 100 epochs 输入尺寸 | 640x640 批次大小 | 24 优化器 | auto 初始学习率 | 0.01 训练设备 【关键指标汇总】 训练了 68 个 epoch,最终轮指标: 指标 | 数值 mAP50 | **0.7582** mAP50-95 | 0.4306 Precision | 0.6226 Recall | 0.6520 train/box_loss | 0.8731 train/cls_loss | 2.3543 val/box_loss | 1.3709 val/cls_loss | 3.9767 【训练过程分析】 从一开始的配置就定下了基调,训练配置包括了 yolo26n 模型、100 个 epoch,学习率初始设为 0.01,输入尺寸定为 640x640,批次大小为 24。这组配置在开始阶段表现得不错,模型刚开始训练时的 loss 降低得很快,这表明模型能够迅速从数据中吸收信息。但到了中后期,loss 曲线变得愈发平缓,从第 50 个 epoch 开始,即使我增加了学习率衰减的频率,仍然感到曲线在挣扎,像是陷入了平台期。这期间模型的收敛节奏明显减缓,似乎有些“停滞不前”的迹象。到了最后,模型确实收敛了,但在第 68 个 epoch 时 mAP50 获得了 0.7582 的值,这是一个不错的成绩,但与理想值相比仍有提升空间。... 使用场景:毕业设计、课程设计、深度学习入门、工业场景落地验证。 立即下载

导语:这篇“基于YUI3开发web应用的诀窍”是比较经典的介绍 YUI3 工作机制的文章,文章发布在yuiblog上,总体难度适中,比较适合初学者认识、了解 YUI3。以此纠集了三名应届同学来翻译这篇文章:函谷、郝黎、张勉,并希望能对正在学习YUI3的同学有所帮助和启发。

原文标题:A Recipe for a YUI 3 Application
原文地址:http://www.yuiblog.com/blog/2011/04/01/a-recipe-for-a-yui-3-application/
译文:使用YUI 3开发Web应用的诀窍

我们知道,YUI3的设计始终围绕着“模块”展开。今天我不想过多解释什么是模块,因为Nicholas Zakas在他的文章”构建可伸缩的前端架构“中已经做了详尽说明。在这里我将着重阐述如何构建这些模块。文章中的大部分内容都可以从在线文档查阅,并有其他可代替的方法。当然在线API文档的内容大而全。本文只是介绍其中的精华的部分——基于YUI3开发web应用的诀窍,这里的“诀窍”更针对短小精悍的程序场景,不像Nicholas Zakas所指的架构级场景,毕竟仅凭本文的篇幅无法全部展开讲述YUI3。

模块的定义

首先我们要对模块进行定义,一种常见的方法是将页面可视区域的不同部分做切分,导航、菜单、正文、边栏面板等等。然后查一下YUI是否已经提供了这些模块,比如YUI3就没有提供“菜单”组件,但提供了Node-MenuNav插件,这个插件可以将结构化好的html代码(ul>li)渲染成具有交互行为的菜单。或者你可以直接去YUI Gallery中去找基础组件。不管怎样,你总会找到一种容器或者布局,可以让你往里填充你需要的东西,ok,让我们从这里开始。

我建议将每个模块都封装进一个文件,放在和文件名同名的目录中,比如weather模块应当放在/weather/weather.js中,这样做的原因是,有可能你的组件会依赖一些样式,包括css和img等,将这些样式和资源文件放到和js同一个目录下,YUILoader就会很方便的找到他们。这样,样式文件就可以放在weather/assets/skins/sam/weather.css,同样,其他图片和样式也可以按这种方式放置,当然我们假设你没有使用YUI Builder来打包你的项目,这就有点说来话长了。assets目录和skin目录的含义不言自明,但sam目录就搞不懂啥意思了,其实sam是YUI配置项中skin的默认值,指代YUI内嵌组件的默认样式,sam取名自其设计师Sam Lind。因此你也可以使用你的昵称作为你的组件皮肤名称,当然这需要你在YUI全局配置中传入skin参数,简单起见,我们这里只使用默认皮肤。

模块文件模板

这里是一段最常用的模块定义的代码:


/*jslint devel: true,  undef: true, newcap: true, strict: true, maxerr: 50 */ 
/*global YUI*/ 
/** 
 * The module-name module creates the blah blah 
 * @module module-name 
 */ 
YUI.add(‘module-name’, function (Y) { 
    "use strict"; 
    // handy constants and shortcuts used in the module 
    var Lang = Y.Lang, 
        CBX = ‘contentBox’, 
        BBX = ‘boundingBox’,
        NAME = ‘xxxx’; 

 
    /** 
     * The Xxxx class does …. 
     * @class Xxxx 
     * @extends Widget 
     * @uses WidgetParent 
     * @constructor 
     * @cfg {object} configuration attributes 
     */ 
    Y.Xxxx = Y.Base.create( 
        NAME, 
        Y.Widget, 
        [Y.WidgetParent], 
        { 
            // Instance members here 
        }, 
        { 
            // Static members here, specially: 
            ATTRS: { 
            } 
        } 
    ); 

}, ’0.99′, { 
    requires: ['widget','widget-parent'], 
    skinnable: true 
});

前两行是供JSLint过滤这段代码用的,JSLint是我非常推荐的一个javascript语法检查工具。在JSLint网页版本中,会有一个选项区来设置选项,这排checkbox在页面的底部,如果你使用YUI Builder,JSLint会自动去读取注释中的配置,来依此对你的代码进行语法检查。

接下来的注释是为了生成YUIDoc,这会为你省去不少麻烦,文档有固定格式的模板,你只要通过注释语法填充内容就可以了。当项目越来越大,你就会越来越依赖这种项目文档。

现在开始真正的代码段,YUI.add()语句。我们用YUI.add()来告知Loader模块名称、模块内容、以及其他的一些信息。模块名通常使用小写字母加中横线组成,这些模块名称就是你在YUIAPI文档左侧看到的名字列表。当然你也看到有很多不是小写字母加中横线的命名,毕竟这种命名方法并非强制,你可以根据你的习惯做模块命名。

YUI.add()的第二个参数是一个带入YUI实例的函数,这个实例通常被命名为Y,这个回调中的逻辑即是模块的主体部分,而你需要依赖的模块的功能方法都会被挂载到这个Y上。跳到代码的结尾,你会看到YUI.add()剩下的参数,版本号(也可以不需要)和模块配置项,模块配置项是一个对象,这里代码的含义就是告诉Loader这个模块依赖了widget和widget-parent,并包含皮肤。当然,在这个例子中widget是多余的,因为widget-parent依赖了widget,这时即便你写上widget,Loader也不会重复加载widget。而且你根本不必在意模块的顺序,Loader会计算他们的依赖关系并做好排序。你也会注意到YUI APIdoc中Loader包含一个addModule()方法,YUI.add的功能就和它很类似。

在函数体内,首先是“use strict”的声明,这个声明源自ECMAScript5,这使你的代码对于未来的解释器也具备足够好的适应能力,对于旧的解释器也只相当于定义了一个孤零零的字符串值而已,不妨碍程序运行。“use”声明指定了一个函数作用域,因此将这句声明放在YUI.add()内比放在文件最顶部要更安全一些,因为如果将其放在全局,这回影响后续加载的所有JS逻辑。

然后是定义常量和变量的缩写,JavaScript本身没有常量的概念,这里的“常量”只是一种约定,和在其他编程语言中一样,常量通常使用大写字母和下划线来命名。这样做的好处有两个,特别是字符串常量,一是当你多次写一个字符串的时候,如果字符串拼错则只能在运行时报错,当改为常量形式如果出错在JSLint语法检查时就能发现。二是让YUICompressor工作效率更高,使用常量可以节省不少字节数。通常,我们更多的把配置属性和事件名称定义为常量。

缩写,比如用Lang代替Y.Lang,写的更少,解释器执行更快(直接使用引用而不是逐级查找对象成员)。

在API docs所需的文档注释后是实际的代码,我们需要将自己的模块对象挂载在Y上, 比如 Y.Xxxx。我建议采用Y.Base.create()来创建模块,这样能创建Base的派生类(Widget即派生自Base),Base模板基本能满足大多数的模块功能需求,因此不必在用其他方式来代替Y.Base.create。其中的第一个参数是模块名称,NAME属性是Base的一个描述,通常用驼峰命名法,这个名字会用于事件的前缀(例如”io:success”),抑或是组件容器的class命名前缀(例如:”yui3-xxxx-content”),此外,NAME还是执行“实例.toString()”时的默认返回值,在debugger模式下经常看到很多类似的log。这里我们使用常量NAME来标示类模块的名称。

create()的第二个参数是要派生的父类。比如,你会使用Y.Base作为无UI组件的父类、使用Y.Widget作为带UI组件的父类、Y.Plugin.Base作为插件的父类、或者使用任意派生自Y.Base的子类作为父类,也就是说,任意通过Y.Base.create()创建的类都可以再次通过create()被继承。

第三个参数是一个参元类,摻元类的方法都将挂载到你的类上,常用的参元类包括从Base派生来的ArrayList、从Widget.Attribute派生来的类似Widget-Xxxx的子模块、EventTarget和PluginHost(这两个参元类已经被Base继承),YUI中的继承机制是如此强大,以至于你看Overlay的源码仅仅是一句从Widget的继承。子类可以继承自多个父类,因此第三个参数是一个数组。

最后是实际的代码实现部分。第四个和第五个参数都是对象,它们包含了实例,和类中的静态成员。实例属性通过深拷贝挂载到类的原型(prototy)上,通过this来访问他们,其他静态成员则是可公开访问的。

配置属性

静态成员中最重要的就是ATTRS,其中列出了你的类所需的所有配置参数。例如你需要一个名为value的成员,这个成员用于保存一个数字,默认初始值为0,可以这样传入第五个参数。

ATTRS: {
    value: {
        value: 0,
        validator: Lang.isNumber
    }
}

ATTRS中当然可以写入多个成员,每个成员都下辖各自的配置项,通过阅读addAttr()来了解更多参数配置。我们看到,validator使用了之前代码中定义的缩写Lang,验证函数得到一个实参的输入,返回一个布尔值,所有的Y.Lang.isXxxx都可以作为校验函数,当然,为了更严格细致的校验,你可能需要定义新的validator、setter和getter,这里推荐使用字符串作为句柄赋值给validator、setter或者getter,Attribute会将字符串对应到具体的函数。比如要定义一个validCodes属性,可以接收单个值或者一个数组,但应当统一返回一个数组:

ATTRS: {
    validCodes: {
        setter: ‘_setValidCodes’
    }
}

我们需要在Y.Base.create()的第四个参数中声明_setValidCodes方法,其他的成员也可以在这里追加声明。

_setValidCodes: function (value) {
    if (!Lang.isArray(value)) {
        value = [ value ];
    }
    return value;
}

除非函数足够简单,最好单独重写setter、setter和validator,传入他们的函数名作为实参,让Attribute将函数名对应到真实的函数。

总之,使用setter作为标准输入,这样会为组件带来一些非常有用的特性。比如配置项值的改变会触发beforeChange和afterChange事件,如果在beforeChange事件中停止事件,则可以阻止属性的更改,因此组件实例生成后,请善用自定义事件。比如你如果绑定了afterChange事件,如果触发了回调,那么一定是值被改变了,而不是即便试图更改属性值却在beforeChange中被阻止了。

validator和setter的校验强度是你来决定的,如果你的validator的校验逻辑很严格,就不用再在setter中添加严格的校验,甚至不需要setter,反之亦然,setter校验很严格的话也可以缺省validator。比如,下面两句代码,你可以任选其一:

validator: Y.Lang.isBoolean,   // to make the attribute accepts strictly a Boolean
setter: Boolean, // to make the attribute accept any value and have Boolean turn it into one

Setter也可以作为校验器来使用。如果输入的值不能转换成attribute可接受的值,setter就会返回Y.Attribute.INVALID_VALUE。这种情况下,attribute不会发生改变。

当我定义成员的时候,通常会指定这个常量,并放在模块的顶部(和CBX,BBX这些缩写声明一起),例如:

var VALUE = ‘value’,
    VALID_CODES = ‘validCodes’;

这样做的原因是,在一个模块内可能会多次用到这个值,使用常量更加方便。但是,请千万别在属性声明中使用这些常量。

ATTRS: {
    // *** Don’t do this *** //
    VALUE: {
        value: 0,
        validator: Lang.isNumber
    }
}

这样书写会生成一个名为VALUE而不是value的属性成员,同时记住,不要覆盖基类中的属性成员,Widget中已经有许多属性成员(见表格),如boundingBox、visible、disabled、height、width等,你也可以修改他们,Y.Base.create()会将父类和基类(Base)中的属性成员合并,所以要重载已有的属性成员,你只要在派生类中重复定义即可。

当你要定义的属性成员是数组或者对象的时候要尤为小心,对象(数组)是一个引用值,如果初始化的属性中含有object,他们(所有实例的这个成员)指向的会是同一个object。当你更改这个object或者删除的时候,所有的实例也都跟着改变(prototye永远指向父类的prototye),所以,Base提供了克隆的机制,在你传入object属性成员的时候克隆一份,这会避免引用重复的问题,其他objects的初始化,请使用valueFn参数,或者直接在类的构造函数(initialize)中构建他们。

其他静态成员

另外,你需要给Y.Base.create()的第五个参数上挂载两个静态成员,如果你开发一个插件(plugin),你需要传入NS(命名空间Namespace的缩写)参数,否则插件就无法工作,NS是一个字符串,作为在宿主对象中的一个属性,插件将挂载其上,不过你要特别小心,选择NS命名的时候不要覆盖已有的插件命名。

如果你开发一个需要渐进增强的组件(widget),你需要传入HTML_PARSER参数,HTML_PARSER是一个object,其成员可以是从配置参数传入的已有的HTML片段获取,也可以来自于一个(CSS3)选择器或者一个getter函数。请参考Widget使用指南中的Progressive Enhancement来了解更多。

当然,你有时会提供一些类静态成员(属性/方法),并暴露给开发者去调用,而类中的常量由于闭包的保护对外并不可见,如果你想对外暴露他们,则需要通过Y.Base.create()的第五个参数传入一个boject,object的成员都会被当作静态成员挂载在类本身上。例如WidgetStdMod中的HEADER,BODY和FOOTER常量就是用这种方法声明的静态常量,不过你需要通过Y.WidgetStdMod.BODY来访问。

实例成员

Y.Base.create()的第四个参数(object)会mix到类原型上,通常我们先声明属性后声明方法,没有原因一定要这样,只是惯例,声明的顺序也不影响程序的执行。这样约定是为了更工整的组织你的代码,尽管你在类构造器中也可以创建成员,但还是强烈建议先声明后赋值,声明的位置用来为APIdoc补充注释,这样的coding方式大大增强源码的可读性。

私有属性通常带有下划线前缀, 类的公共接口最好通过Attribute传入,而不是直接挂载实例属性,直接挂载属性很不安全,毕竟它不像通过Attribute可以带入校验器、适配参数类型(通过setter函数),并且可以支持AOP的一些特性诸如触发valuechange事件等,你会发现通过Attribute传参的好处不胜枚举。

在使用Attribute的时候,注意不要将成员初值设为对象或数组,因为它们都会指向同一个object而使程序出错,最好将那些object成员初值设为null,也不要将属性留空,如果实在不知道成员的数据类型,统统设置为null,因为在调试程序的时候,引用那些没有初值的成员会报一个“类型错误”。

Base中的实例方法

你可能会注意到,在子类中并没有定义类构造器,因为在构建实例的时候,Base会通过调用initializer方法(如果存在的话)对实例进行初始化,Initializer使用的参数即是实例的构造参数,所以可以把initializer看做构造器,所有派生自Base的类在创建之时先要被Base“扩充”(Argument)一下,Base这里被称之为“摻元类”,参元类在调用initializer之前通过Attribute来传入参数,对于设置了HTML_PARSER的Widget来说,Attribute中的属性是从HTML“标签”中获取,而不是直接从参数中读的。

Initialize方法干了这么几件事情,首先是将所有需要初始化的属性设置为object或array,然后注册(publish)自定义事件,虽然EventTarget可以直接在没有注册事件的情况下fire事件,但我还是建议先注册事件类型(为了事件可以冒泡),同样,类似先声明后赋值的原则,你可以在注册事件的地方补充APIDoc注释,乍一看似乎在函数体内写APIDoc注释很奇怪,但你也找不到更好的写APIDoc注释的方式。

接下来就是处理Initializer函数接受的参数,你也可以在参数中增加配置属性中没有的内容,比如我们将on,after, buggleTargets和plugins等Base原本没有的属性(或方法)挂到Base上(参见Base),也可以给Widget父类挂载新的子属性,尽管你在实例化一个对象的时候只传入一个参数,但这个参数所包含的内容不止与此。

Javascript没有析构函数的概念。Base允许你声明一个名为destructor的函数来释放内存资源,依此来模拟析构函数。但这只是一个折衷方案,在删除一个对象时,javascript编译器不会自动回收其内存,因此对于没用的对象是需要手动销毁的,即调用一下析构函数。

开发者在使用你的类的时候,不需要直接调用initializer和destructor,Base会在必要的时候调自动用他们。Initializer会在对象实例化时调用,destuctor会在用户调用destroy方法销毁对象时调用。

绑定的事件没有被解绑常常会造成内存泄漏。Widget会自动解除组件内部绑定的事件,其他的手动绑定的事件不会被自动解除。Base也不能解除所有事件监听,我通常使用这段代码来解除事件绑定,先在声明私有成员_eventHandlers:

_eventHandles: null,

然后在initializer方法内将_eventhandlers设为数组

initializer: function (cfg) {
    this._eventHandles = [];
    // … …
},

同样在initializer 函数(或者在Widget的bindUI函数)中,通过这段代码来绑定监听:

this._eventHandles.push(this.after(‘someAttributeChange’, this._afterSomeAttributeChange, this));

在destructor方法内:

destructor: function () {
    Y.each(this._eventHandles, function (handle) {
        handle.detach();
    });
},

这里在initializer中,有可能绑定了自定义事件(除了UI元素上的事件),就像上文提到的,Attribute是处理参数的标准方式。任何非数据的存储都由事件处理逻辑完成。在上面的例子中,我使用_afterSomeAttributeChange来监听someAttribut的属性变化。事件发生会触发回调,并带入即事件对象(我通常以ev表示),事件对象包含几个与事件相关的属性,其中一个属性newVal表示变化后的值。

Widget的实例属性

Widget有两个重要属性BOUNDING_TEMPLATE和CONTENT_TEMPLATE。初始值都是”<div></div>”,这为多数widget提供一个标准html结构,包含双层嵌套的div。但并不是所有的widget都适用,比如按钮使用<span> <a></a></span> 而不是<div><div></div></div>结构,其实Widget不强制要求使用一个大容器(contentBox),这两个属性可以使用任意html标签,比如我用a来描述按钮

BOUNDING_TEMPLATE: ‘<a>’,
CONTENT_TEMPLATE: null,</a>

在你不需要contentBox的时候就将其设置为null,这时contentBox的配置会继承自boundingBox。不要将widget的所有HTML都放在这两个属性中,这两个属性中的元素要尽量简单,在renderUI中使用代码创建其余的html标签。Widget会给所有你指定的标签加上id和class,形式诸如yui3-xxxx, yui3-xxxx-visible 或 yui3-xxxx-disabled,其中xxxx是小写的NAME属性。

Widget实例方法

Widget的初始化有多个步骤,除了会在对象实例化时调用initializer(构造函数)外,在销毁时也会调用destructor函数(析构函数),这两个方法都继承自Base。Widget又增加了renderUI,bindUI,syncUI三个函数,在widget的render方法被调用时,这三个函数会依次执行。

renderUI负责生成widget最基本的HTML骨架,包括boundingBox和contentBox,如果考虑到渐进增强,renderUI会先检查元素是否已存在于DOM中,这是因为如果设置HTML_PARSER属性,配置属性中定义的元素会被事先生成好。如果元素没有事先生成,我们才需要通过renderUI来创建。

最简单的生成HTML的方法(假设没有使用渐进增强)就是使用Y.Node.create方法:

renderUI: function () {
    var cbx = this.get(CBX);
    cbx.append(Y.Node.create(Y.substitute(Y.Xxxx.TEMPLATE, CLASS_NAMES)));
},

这段代码的运行需要特定的上下文,首先,要声明常量CBX(见本篇文章第一个代码块),其次,widget所需的模块都加载完全,加载widget之前会自动加载其依赖的Node,Y.substitute是可选模块,如果需要则要把’substitute’加入到requires中。此外还要有一个作为widget内容模板的名为TEMPLATE的静态变量,你可以和其它静态成员一起定义(通过ATTRS定义)它。最后常量CLASS_NAMES应当也已经事先定义好了。

我通常在模块开头,和BBX,CBX一起定义CLASS_NAMES(见本文第一个代码块):

var BBX = ‘boundingBox’,
    CBX = ‘contentBox’,
    NAME = ‘button’,
    // other constants and shortcuts ….
    YCM = Y.ClassNameManager.getClassName,
    getClassName = function () {
        var args = Y.Array(arguments);
        args.unshift(NAME);
        return YCM.apply(this, args).toLowerCase();
    },
    LABEL = ‘label’,
    PRESSED = ‘pressed’,
    ICON = ‘icon’,
    CLASS_NAMES = {
        pressed: getClassName(PRESSED),
        icon: getClassName(ICON),
        label: getClassName(LABEL),
        noLabel: getClassName(‘no’, LABEL)
    };

CLASS_NAME为一个object常量,其中的属性值由ClassNameManager(包含在Widget中)生成。在以上代码中,我先声明一个指向Y.ClassNameManager.getClassName的缩写YCM,然后声明一个只能在模块内部访问的私有函数getClassName,这个函数的功能与Widget中的同名函数getClassName的功能很类似,只是widget中的getClassName是个静态函数,可以用来生成静态变量。CLASS_NAMES中表示样式名的属性值就是用它生成的。现在,我就可以这样定义TEMPLATE中的样式名了:

TEMPLATE: ‘<label>’,</label>

这还不够,我还想将其它的值比如配置属性中的值加入模板中。可以这样做:

this.get(CBX).append(Y.Node.create(Y.substitute(TEMPLATE , CLASS_NAMES, Y.bind(function (key, suggested, arg) {
    return (key === ‘_’?this.get(arg):suggested);
},this))));

我给Y.substitute增加了第三个参数,一个函数。通常,Y.substitute中的占位符是由一对花括号包围的字符串,如果其中有空格,占位符就会被一分为二。空格前面的部分为key,空格后的部分为可选参数。当Y.substitute的第三个参数是一个函数时,这个特性十分有用。比如在此例中,第一个参数是key,第二个参数是用于替换的内容,此例中是CLASS_NAME,第三个是可选参数。所以在上面的语句之后,我可以这样使用模板:

TEMPLATE: ‘<label for="”{_" />’,

Y.substitute在执行时先碰到{label},开始在CLASS_NAME中搜索,并找到对应值‘yui3-button-label’。然后以‘label’, ‘yui3-button-label’ 和 undefined为参数调用替换函数。因为key不等于’_’,所以函数返回第二个参数中值,即原始的样式名。当遇到{_ id}时,因为在CLASS_NAMES中没有’_’属性,所以会以传入参数‘_’, undefined和 ‘id’调用替换函数,函数会从配置属性中读取’id’的值。在遇到占位符{_ value}时函数将执行同样的操作。

所有在模块的代码顶部声明的常量在模块外都是不可见的,但你可能想使一些常量可见,比如CLASS_NAMES。此时,你只需在Y.Base.create的最后一个参数中,即声明静态成员的区域声明:

CLASS_NAMES: CLASS_NAMES

这样这一对象及其包含的所有属性都可通过Y.MyWidget.CLASS_NAMES来访问。

我建议尽可能将widget的HTML字符串都调整好再执行Y.Node.create。因为在Javascript中,字符串操作的速度比DOM操作快很多。在Y.Node.create调用前做的越多,代码执行的速度就越快。

所有widget还都会用到的方法就是bindUI。你可以在这个函数创建元素绑定事件,例如你可以给上面TEMPLATE中的<input>绑定valueChange事件,使textbox中值和配置属性中的对应的值始终保持一致。代码和用户都有可能去修改input中的值。如果是来自内部代码的更新值,textbox要刷新以显示新值,如果改变来自用户输入则不用刷新。如不这样,就会陷入死循环:textbox的value属性发生改变,触发change事件,响应函数又改变textbox的值,这一改变又触发了change事件……让我们看看如何处理这一情况。我们在input上绑定自定义的valueChange事件,当然要先在模块的requires中加入event-valuechange模块。

this._eventHandles.push(this._inputEl.after(‘valueChange’, this._afterInputChange, this));

我们假设_inputEl对象指向textbox。事件响应函数如下:

_afterInputChange: function (ev) {
    this.set(VALUE, ev.target.get(VALUE),{source:UI});
},

这里假设我们事先已定义了表示value和ui的常量VALUE和UI。我们简单的将配置属性中的值设置为input中的值,我还给函数加了第三个参数{source:UI}。set函数可以接受第三个参数,这个参数是一个object对象,它的属性可以加入attribute change事件的事件对象(event facade)中,由此就可以区别textbox的valueChange事件是来自内部代码还是用户输入。在bindUI中,我们可能已经这样设置了事件监听:

this._eventHandles.push(this.after(‘valueChange’,this._afterValueChange));

这是一个监听value变化的事件监听,上一个例子也是对<input>的value的变化作监听。两个事件名称都一样,实际上,它们都是对一个叫做value的值的变化进行监听,但实际却不一样。通常,对属性变化的监听会放在initializer里,而此例涉及改变UI元素,所以把它放在bindUI中,也提醒我们这个事件响应涉及textbox。事件响应函数如下:

_afterValueChange: function (ev) {
    if (ev.source === UI) {
        return;
    }
    this._inputEl.set(VALUE, ev.newVal);
},

首先我们检查事件对象的source属性。如果事件来自UI,我们直接忽略。在这里,属性名UI和它的值都是任意的,你可以根据自己喜好定义。我在设定value的属性值时定义了UI和它的值,所以在这里我就可以访问UI这一属性,你也可以用其它的键值对。实际上,widget也提供了一个相同功能的常数Y.Widget.UI_SRC,只是名字有点长,所以我宁愿自己定义。

一个小技巧:你可以使用_set代替set来改变只读属性的值。_set方法本来是作为受保护方法,只能在类及其子类中访问的,但是javascript中对象成员都是公有的,所以_set实际上是个公共方法,外部也能访问。即使这样,我们还是会给只读属性声明readOnly:true,并且在API文档里也将这一属性标为只读。

最后一个实例方法是syncUI。前两个方法renderUI和bindUI会且仅会执行一次,但syncUI则至少被widget自身调用一次,你也可以在后面的程序中多次调用这个方法。syncUI的作用是根据对象的状态刷新其外观。对象的状态可能一直在变化, 界面也会跟着变化。不过,如何编写这个方法不能一概而论,要根据具体情况。对于简单的用户界面,syncUI可以在每次有变化发生时都重绘界面中的全部内容。而对于复杂的用户界面,重绘整个界面费时且会造成图像闪烁,所以你最好只重绘发生变化的部分,这样的话,你就需要将重绘不同部分的代码分别放在不同的函数中,syncUI会将每一部分只调用一次。还有,在先前的renderUI的例子中,我改变了textbox的值,而只有在syncUI执行之后,这一变化才能在屏幕上显示出来。

更常见的使用方法是给每个UI元素设置单独的重绘函数。这个函数会在初始化时被syncUI调用一次,之后会在配置属性的发生变化后,通过事件响应函数调用。例如

_valueUIRefresh: function (value) {
    this._inputEl.set(VALUE, value);
}

这一函数和其它相似功能的重绘函数会在syncUI中被调用:

syncUI: function () {
    this._valueUIRefresh(this.get(VALUE));
    // other such refreshers 
},

在after事件响应函数中的代码如下:

_afterValueChange: function (ev) {
    if (ev.source === UI) {
        return;
    }
    this._valueUIRefresh(ev.newVal);
},

与其他模块的通信

在实现一个模块之后,它会和其他模块进行交互。传统的方法是紧耦合(通过Nicholas Zakas的视频中我们可以了解什么是紧耦合什么是松耦合),也就是通过方法调用和属性赋值来将这些模块紧密联系在一起,在这儿就不赘述了。下面我们介绍另一种方法——自定义事件,Y.Base里面包含了你所需要的全部方法。

首先,在initializer中,你要发布这个自定义事件,让大家都知道它:

initializer: function (cfg) {
    this.publish(‘eventName’, { /*… options … */});
},

需要注意的是,事件名称最好是一个常见单词,因为在后面你会经常使用它,常见单词可以避免出现拼写错误。然后,假设你拥有一个对象,例如:

var myWidget = new Y.MyWidget({ /* .. attributes … */ });

此时,你可以为它绑定事件:

myWidget.after(‘eventName’, this._eventNameListener, this);

然而,这样做虽然不像直接的方法调用那样联系紧密,但是由于必须有一个myWidget的实例,所以其实质还是紧耦合。也就是说,在两个模块的通信中,一个模块必须知道另一个的细节,或者存在一个监视模块为它们建立连接。在这个过程中,有两个配置项是非常重要的,broadcast和emitFacade。

第一个,broadcast,可以让你在其他的模块中为这个事件设置监听器。broadcast默认值为0,此时只能用前面所示的那个方法。如果希望事件可以在任何地方被监听,你需要改变broadcast的值。如果只是在沙箱内,broadcast值为1,如果需要在各个沙箱间,则broadcast值为2。一个沙箱如下所示:

YUI().use( ‘module1′, …, ‘moduleN’, function (Y) {
    // this is your sandbox
});
在页面中可以有多个这样的沙箱:
YUI().use( ‘module1′, …, ‘moduleN’, function (Y) {
    // this is your sandbox
});
YUI().use( ‘moduleX-1′, …, ‘moduleX-N’, function (Z) {
    // this is another sandbox
});
如果你设置broadcast值为2,你就可以在沙箱2中监听在沙箱1中发布的事件,具体细节请看Event user guide。我们继续讨论简单沙箱的情况。

要在一个沙箱内监听另一个模块中发布的事件,必须知道的是这个模块的静态属性NAME的值和事件名称。回想下,Y.Base.create方法所带的第一个参数的值,就是NAME属性的值。因此,如果你创建了这样一个模块:
Y.MyWidget = Y.Base.create(
    ‘xxxx’,
    Y.Widget,
    // … and so on
然后在initializer发布了一个’help’事件:
initializer: function (config) {
    this.publish(‘help’, {
        broadcast: 1,
        emitFacade: true
    });
},
那么,要在其他模块的沙箱内监听这个事件,就可以这样做:
Y.after(‘xxxx:help’, function (ev) { … }, this);
在这里调用了Y.after,而不是myWidget.after,所以我不再需要一个实例才能触发这个事件。你也可以用同样的方法来监听DOM事件或者其他的自定义事件,比如’valueChange’等,不同的仅仅是引号前面的前缀。你也可以使用别的东西作为前缀,Y.base会接受这个值,但是通常情况下,Y.base提供的默认值已经足够了。你还需要设置emitFacade值,因为需要一个对象来触发事件,从而为ev.target提供门面值(facade value)。也许你会想,如果监听器所在的模块获得了注册事件模块的对象,那不是重新成为紧耦合了么。但事实并非如此,只要你在监听器模块中不保留这个对象,耦合就不复存在。此外,我们还有更好的办法。

在发布事件时,我们可以添加所有在事件对象中监听器所需要的信息,例如:
this.fire(‘help’, {helpTopic: ‘Event Broadcasting’});
fire()方法的两个参数分别为发布事件的名称(也就是Y.Base为它增加前缀的类的名称)和包含一些特性的对象(这些特性需要复制给事件对象)。这样监听器就不需要为了获取一些信息而遍历注册事件模块,从而达到了松耦合的目的。监听器通过“事件广播”知道有这样的一些模块,甚至可能有很多这样的模块会响应help事件,但并不需要关注是哪一个模块正在响应它。这种方法也简化了日后新模块的添加。

事件和默认行为

要改变一个类的行为,通常的办法是建立一个子类,然后重写它的方法。YUI也可以完成这些工作。你可以用Y.Base.create来创建一个基类Y.Widget,然后用Y.Base.create来创建一个新的类来作为基类的扩展,并给予其一些特殊的行为。例如,先创建基类:
Y.MySimpleWidget = Y.Base.create(
    ‘simpleWidget’,
    Y.Widget,
    [],
    {
        // instance members here, amongst them:
        renderUI: function () {
            this.get(CBX).append(Y.Node.create(‘ … whatever goes into the widget … ‘ ));
        }
    },
    {
        ATTRS: {
            // configuration attributes
        }
        // other static members
    }
);

</pre>

然后创建子类:

Y.MyFancyWidget = Y.Base.create(
    ‘fancyWidget’,
    Y.MySimpleWidget,
    [],
    {
        renderUI: function () {
            Y.MyFancyWidget.superclass.renderUI.apply(this, arguments);
            this.get(CBX).append(Y.Node.create(‘ … add some bells and whistles … ‘ ));
    }
    // Presumably the fancy version does not need any further static members so I skip the last argument
);

我们可以看到,MyFancyWidget通过添加一些细节改进了MySimpleWidget。但是这在某些情况下会有些问题,所以你需要设计一个更灵活,更容易改变的基类。自定义事件会对你有所帮助。

假设有个排序类,拥有key和direction两个参数,声明如下:

sort: function (key, direction) {
     // sorting happens here
},

如果这个函数的行为在某些情况下需要更改,您可以这么做,在initializer方法中添加自定义事件:

initializer: function (config) {
    // amongst many other things:
    this.publish(SORT, {defaultFn: this._defSortFn});
},

若SORT是一个具有排序功能的实例,你可以这样声明它的sort函数:

sort: function(key, direction) {
    this.fire(SORT, {key:key, direction:direction});
},

这样子,排序函数就转换为一个具有相同参数的事件触发函数。这样只是提供了一个转换接口,你仍然需要通过原始的排序函数来设计一个类:

_defSortFn: function (ev) {
    var key = ev.key, direction = ev.direction;
    // same code as the original sort function
},

这个_defSortFn类的函数体与原始的方法一模一样,达到相同的排序目的。但是你可以从事件对象中知道key和direction参数,只要一段简单的代码段就可以设置一个监听器,并且改变排序方法。

myObjectThatSorts.on(‘sort’, function (ev) {
    var key = ev.key, direction = ev.direction;
    ev.preventDefault();
    // now do your own sort
});

通过preventDefault我让myObjectThatSorts不再执行_defSortFn中的排序方法,从而可以根据我需要的结果做任何事,无论原来的排序是什么样。我甚至不用关心它是否停止,只要监听after事件来翻转UI上用来显示排序方向的箭头。

我也可以改变事件对象。当事件触发时,我们得到的是根据事件对象复制的一个对象,它从before监听器开始传播,通过默认的函数,到达after监听器,然后被丢弃。你可以在过程中改变它的一些属性的值。当然,在默认方法执行后再做任何改变是没有影响的,在执行之前改变事件对象才能对方法有所影响。例如。

myObjectThatSorts.on(‘sort’, function (ev) {
    ev.direction = (ev.direction===’desc’?'asc’:'desc’);
});

这样将得到倒置的排序结果。

YUI_config

在页面中调用一个模块的最简单的方法是通过scirpt标签来引用脚本,或者是将它放在script标签的url指向的combo文件中(combo可以通过手动连接或者支持combo的服务器自动完成)。而将自定义模块集成到YUI Loader中是一个更好的办法,可以极大的改进性能。这种方法很重要的一点是在使用YUI.add()插入模块时引入依赖文件(通过requires的最后一个参数),这样在调用use()时就可以按照正确的顺序调用它们。

对于小的web应用,你可以在页面load时加载所有内容,但是对于大型应用,这样是很不合理的,因为会花费太长的时间。你可以使用很多次use()方法去请求各个模块所需要的文件,然而这种让Loader在模块加载时去查找本模块的依赖文件是非常耗时的,它可能需要建立很多个请求,直到获得它需要的文件为止。相反,你可以预先告诉Loader各个模块和它的依赖文件,这样,当遇到这个模块时,加载器就可以并行的对它们进行处理。

为此,你需要为YUI Lodaer添加模块说明和要求来建立模块依赖关系,最简单的方法就是建立一个包含这些信息的yui_config.js文件(可以改为其他名字),如下所示:

YUI_config = {
    filter:’raw’,
    //combine:false,
    gallery: ‘gallery-2011.02.18-23-10′,
    groups: {
        js: {
            base: ‘build/’,
            modules: {
                ‘myWidget’: {
                    path: ‘myWidget/myWidget.js’,
                    requires: ['widget', 'widget-parent', 'widget-child', 'widget-stdmod', 'transition'],
                    skinnable: true
                },
                // other modules here
            }
        }
        // other groups here
    }
};

将这段代码放在常规的<script>标签内,并放在第一个YUI().use()代码段之前。它用来配置(YUI)库加载前需要的一些全局属性。,就像以前你必须放在YUI().use()的第一个参数一样,现在YUI可以代替你做这些。你可以使用这儿所列的任何配置项。

filter:“min”:产品代码(去除注释后的最简版本);

debug:bebug版本(带有一些log语句,包含console组件);

raw:非最简版本(不带log,含有注释);其中后两者通常只应用于开发环境中。

combine:这个配置项仅仅应用在combo后一些难解决的bug的查找时。

gallery:如果你使用了gallery模块,填上它的版本号。

group:这个属性用来描述你的模块。

首先是组的名称,这里叫‘js’,也可以是其他名称。 你可以为放置在相同位置的一系列文件创建一个这样的组,每个组的第一个参数用来声明文件的根路径(可以是相对路径或者绝对路径)。除base之外,还有一些组的基本属性,具体请参照这儿。

最后是modules属性,需要在这列举这个组的所有模块。调用每个模块的关键词是模块的名称,也就是你在YUI().add()和YUI().use()的第一个参数。path是模块相关文件的位置,可以是在base基础上的相对路径,如果文件放在其他地方,也可以用全路径。其他的属性可以在这儿找到,和放在YUI.add()结尾的一样。requires属性里面可以是YUI modules, gallery modules或者你自己创建的modules,无论是这个组的还是其他组的。此外,如果设置skinnable:true的话,皮肤会被自动加载,就像我在文章开头所讲的那样。

为了简化这些工作,我创建了一个Windows脚本文件,可以为我自动配置YUI_config。它会扫描并阅读每一个模块文件,并提取出每一个YUI.add中的参数。对于我来说,它很有用,但并不一定适合你。

结语

YUI3非常灵活,你可以通过很多方法建立你的模块。比如通过Y.Base来派生,其实我也不是经常这么做,只是偶尔会用到,但在复杂系统中,依然非常推荐使用Y.Base。

视频编码压缩11111 立即下载

相关推荐

别再手动找触点了!EPLAN线圈触点映像的3种显示方式与F键/Ctrl+B快捷键实战

本文详细解析EPLAN线圈触点映像的3种显示方式与F键/Ctrl+B快捷键实战技巧,帮助电气设计工程师提升图纸操作效率。通过精简编号、路径栏显示和元件旁可视化三种模式的应用场景对比,结合快捷键肌肉记忆训练法,实现触点快速定位与管理,大幅减少手动查找时间。

weixin_42522964的博客 481

YUI Global Object

YAHOO Global Object 提供所有YUI库代码驻村的单一全局命名空间。必须在所有使用YUI库的页面中包含,而且必须表现在其他的YUI部件前出现。包含了一系列方法。 Using the Utility Methods in YAHOOYAHOO.namespace YAHOO对象自动生成了YAHOO.util, YAHOO.Widget, YAHOO.e

typicalzoe的专栏 433

Antlr4入门(二)基本概念解析

在上一章中(Antlr4入门(一)IDEA中Antlr的安装与使用),我们安装了Antlr,并编写运行了第一个程序“Hello world”。而在本章中,我们将学习语言类应用程序相关的重要过程、术语和数据结构。 一. 术语 语言(language)是由一系列有意义的语句组成,语句(sentence)由词组组成,词组(phrase)是由更小的子词组(subphrase)和词汇符号(vocabulary symbols)组成。 举个例子,英语就是一种语言,“Keep on going never gi.

qq_37771475的博客 5208

YUI3学习(二)--YUI Global Object

前一篇 YUI3入门 YUI3 Global Object  http://developer.yahoo.com/yui/3/yui/   首先需要了解在YUI3 api文档模块列表的yui模块: YUI模块是YUI3.x实现的单个核心依赖。在使用YUI的页面中都必须包括YUI,这是唯一的依赖文件。YUI模块包含模块加载功能和模块依赖计算功能,YUI模块作为具体实现的一个种子,你只需要...

小猴的专栏 320

AUTOSAR网络管理配置详解:Vector DaVinci工具全面讲解

手把手带你用Vector DaVinci完成AUTOSAR网络管理模块的配置,涵盖NmChannel、NmPdu、重复帧抑制等关键参数设置,深入解析CAN总线上的NM报文调度与唤醒机制,助你扎实掌握AUTOSAR网络管理核心实践。

weixin_42504649的博客 168

保姆级教程:用CameraX API快速搞定Android多摄像头切换与预览(附避坑指南)

本文提供了一份详细的CameraX API教程,帮助开发者快速实现Android多摄像头切换与预览功能。通过简化API设计和生命周期感知能力,CameraX大幅降低了开发复杂度,同时解决了多摄像头兼容性、预览优化等常见问题。文章包含完整代码示例和实战避坑指南,特别适合需要高效集成相机功能的Android开发者。

weixin_42699679的博客 166

YUI Global Object小结

  入门:yui-min.js   基础种子:yui-base-min.js   核心种子:yui-core-min.js     两种加载方式 动态加载: 用use() 静态加载(Static Loading): 手动加载   模块的状态:   Not loaded:模板没有加载完毕,也没有实例化 Loaded: 模板已经加载完毕,但并没有实例化,可能...

shikun520的专栏 225

基于Simulink的异步电机定子磁链定向控制(SFOC)仿真

本文带你从零构建了异步电机SFOC系统​ 的完整Simulink仿真模型,实现了:✅ 搭建异步电机、逆变器、风机负载的主电路与传感器模型;✅ 用MATLAB Function实现定子磁链观测(电压模型)、坐标变换、双闭环PI控制;✅ 验证SFOC在转速跟踪(响应0.2s、超调4%)、转矩响应(恢复0.15s)中的高性能;✅ 对比RFOC,明确SFOC在参数鲁棒性(转子电阻+50%时波动<2%)上的独特优势;✅ 理解SFOC“定子磁链定向+解耦控制”的核心思想(低成本高鲁棒性驱动的工程实现)。

xiaoheshang_123的博客 389

YUI学习笔记

YUI 3 学习笔记(1)-YUI Global对象 YUI模块是YUI3中的单一核心,所有需要使用YUI3的页面都必须包含该 模块,该模块提供加载功能,可以使用该功能动态加载指定模块,其依 赖模块会被自动加载,所以YUI模块可以看成YUI的一个种子。 YUI模块创建了一个全局对象:YUI,一个页面可以共享一个YUI实例,也 可以使用多个实例。 1 要使用YUI全...

xiaomogu13的博客 418

如何给K8S配置一个具有永久有效的kubeconfig

在 Amazon EKS 环境中,Kubernetes 的认证机制与 AWS IAM 深度集成,通过aws-iam-authenticator动态生成临时凭证。这种设计虽然增强了安全性,但给需要持久化凭证的工具链(如 CI/CD 系统中的 KubeVela)带来了挑战。具体表现为:临时凭证失效​​:默认 Token 有效期为 15 分钟至 1 小时;​​IAM 角色限制​​:直接使用aws eks get-token生成的凭证无法持久化

小杨同学的博客 660

RKNN 端侧部署必看:YOLOv11 Pose 姿态估计模型转换与性能优化实战

随着人工智能技术在边缘计算领域的快速发展,姿态估计作为计算机视觉的重要分支,在智能监控、人机交互、体感游戏等场景中展现出巨大的应用潜力。本文深入研究了YOLOv11 Pose模型在Rockchip RKNN平台上的部署方案,从理论基础、模型转换、平台优化到实际部署进行了全面阐述。通过详细的技术分析和实验验证,证明了RKNN平台能够有效提升YOLOv11 Pose的推理性能,在RK3588硬件平台上实现了高达100 FPS的实时姿态检测,为边缘计算场景下的姿态估计应用提供了高效的解决方案。

m0_71589036的博客 183

《iOS应用开发指南——使用HTML5、CSS3和JavaScript》——1.5 设计始于思考,终于代码...

本节书摘来自异步社区《iOS应用开发指南——使用HTML5、CSS3和JavaScript》一书中的第1章,第1.5节,作者: 【美】Kristofer Layon 更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.5 设计始于思考,终于代码 “但是等等。”你思考着,“我认为所有的应用程序都应该使用Objective-C语言编写?” 是的。然而,...

weixin_33890526的博客 218

Windows环境下Ollama的本地部署方案

本文主要介绍了Windows环境下Ollama的本地部署方案,希望对使用大语言模型的同学们有所帮助。 文章目录 1. 前言 2. Windows环境安装 3. API调用示例代码

herosunly的博客 11万+

储能系统三大核心:SOC一致性、PCS效率、调度响应Δt

储能系统的核心性能不取决于算法或界面,而根植于物理层三大可测参数:荷电状态(SOC)一致性决定电池集群可用容量与寿命,功率变换系统(PCS)全工况效率直接影响能量损耗与经济性,调度响应时延(Δt)则直接关联电网AGC考核与收益兑现。这三者共同构成储能工程落地的刚性约束,其数值偏差会引发木桶效应、循环效率衰减和实时调度罚款。在工商业储能项目中,SOC离散度超±1.5%、PCS低载效率跌破92.4%、Δt超过100ms,已成为故障归因与收益下滑的最主要技术根源。本文聚焦现场可验证、可调试、可量化的三大硬核指标,

weixin_33937499的博客 273

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

YUI 3 学习笔记(1)-YUI Global对象

YUI3模块已经发布,配套的工具及部件虽然现在还不全,但也计划于2010 年全部推出,本人感觉YUI功能比较全面,而且有比较详细的文档,所以 一直比较关注,这是本人学习YUI文档的一系列笔记。 YUI模块是YUI3中的单一核心,所有需要使用YUI3的页面都必须包含该 模块,该模块提供加载功能,可以使用该功能动态加载指定模块,其依 赖模块会被自动加载,所以YUI模块可以看成YUI的一个...

一羽的专栏 288

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

YUI学习笔记1

用js做了几个项目,感觉水平还是不够,决定重新学习一个js框架,好好研究下js。 框架选择了YUI,先从翻译YUI.JS做起吧,正好也提升下英语水平。 YUI.JS YUI 3.8.1 [code="js"] /* YUI模块包含创建YUI种子文件的部件。这里包括脚本加载机制、一个简单的队列、程序库的核心工具包。 */ if (typeof YUI != 'undefi...

SecretGarden 379

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

基于yui的前端“团队开发”模式

原文来自:http://www.uedmagazine.com/ued/comments.php?y=09&amp;m=06&amp;entry=entry090612-094816#   如今web项目也越来越大,单人开发一个网站已经不是什么新鲜事了,这种web项目规模扩大的速度似乎超过我们的想象,对代码的管理,对项目的控制,对分 工的把握,对进度的高效推进……一系列的问题开始在web开发...

宪涛 226

Yui3使用指南一

1、开始使用 script> // Create a YUI sandbox on your page. YUI().use('node', 'event', function (Y) { // The Node and Event modules are loaded and ready to use. // Your code goes here! }); script>

sweetgirl520的博客 1334

YUI中文文档

<br /><br />根据网上别人的说明和综合各方面资料,网上说的要么结构不是很清楚,要么不是很全,呵呵,借别人的轮子总结一下哈,不用造轮子了,我对YUI的文档作一个总结,共同学习。.<br />打算从零开始学习YUI 。AJAX类库那么多,为什么我要学习YUI呢?首先,因为YUI很强大;其次因为YUI是Yahoo! 的。虽然Yahoo!现在好像没落了,但听同事说了YUI的强大,抑制不住学习的兴奋,今天就来学习一下!学习的东西很多,今天只是走马观花的看一个大概的说明,即每个控件有什么功能,具体怎么用就是后

liqiangcfc的专栏 1890

基于yui的前端"团队开发"模式

一篇关于web前端团队开发的文章。关键词:模块化开发module,YUI Combo减少http请求 ================================ 如今web项目也越来越大,单人开发一个网站已经不是什么新鲜事了,这种web项目规模扩大的速度似乎超过我们的想象,对代码的管理,对项目的控制,对分工的把握,对进度的高效推进……一系列的问题开始在web开发中显现出来,其实团队开发不

记录 1926

zepto.fullpage.js学习总结

zepto.fullpage.js插件学习

越努力,越幸运! 4144

Update after change

Update after change实现功能 点击之后文字转变为可编辑状态 编辑之后保持 Now here is the code:<html> <head> <style type="text/css"> body { font-family: 'helvetica neue'; color:#666; padding: 0 20%

三妞的专栏 360

Access宏学习总结

详细了解access中的宏 什么是宏   到底什么是宏呢? 我们把那些能自动执行某种操作的命令统称为“宏”。   宏也是一种操作命令,它和菜单操作命令都是一样的,只是它们对数据库施加作用的时间有所不同,作用时的条件也有所不同。菜单命 令一般用在数据库的设计过程中,而宏命令则用在数据库的执行过程中。菜单命令必须由使用者来施加这个操作,而宏命令则可以在数据库 中自动执

bcbobo21cn的专栏 1万+

YUI3学习笔记之二:自定义事件与AOP

对于我来说,javascript中最有趣而又最无奈的就是事件处理了。丰富前卫的交互体验、流畅便捷的浏览操作,都需要建立在良好的事件处理机制上。然而浏览器的差异在事件处理上达到了最大化,内存泄露在事件处理函数中又最容易引入,这让很多人都对javascript的事件处理很头痛。自定义事件就很少有这种天然的浏览器差异了。我们日常编码中接触自定义事件的机会也很多,比如一个xhr对象,一般都会定义onsuc...

weixin_33971977的博客 117
上一篇: Express框架之app.js配置文件说明
下一篇: MATLAB如何在有限域上求逆矩阵
Vivian_Chang
博客等级 码龄14年 2粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值