Giter VIP home page Giter VIP logo

widget's People

Contributors

afc163 avatar antife-yinyue avatar army8735 avatar bitdeli-chef avatar carlos121493 avatar crossjs avatar jaredleechn avatar leoner avatar lepture avatar lifesinger avatar lizzie avatar popomore avatar shaoshuai0102 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

widget's Issues

destroy 管理

现在 widget 的 destroy 只清除事件,子模块自己管理,比较乱。而且近期出现了内存泄漏的情况,对这块要进行梳理。

templatable.js中compile方法建议

templatable.js中compile方法中下段代码

template || (template = this.template)
model || (model = this.model)

建议修改成下面的样子

template = template || this.get('template')
model = model || this.get('model')

原来的代码在

this.set('model', {})

后在_onChangeModel调用

this.compile();

model还是初始化时候的model

模板解析问题

这样的模板,替换成注释是不是有问题?

<input {{attr}}  type="hidden" />

关于使用widget的几个问题

  • 关于Template使用model的问题

在使用Widget的时候发现如果实现Template的话,因为使用了另外的model属性,该model属性在改变的时候只能实现粗粒度的变更。
如果将一些配置写在attrs里又没法使用Template里的解析方法。

var Test = Widget.extend({
    Implements:[Template],
    model:{
        title:'Test',
        data:'test'
    },
    template:'<div>{{title}}<p>{{data}}</p></div>'
});
var test = new Test();

调用下面的方法皆不生效:

test.set('title','TEST2'); //推荐的实现调用方式
test.set('model',{
    title:'Test2',
    data:'test'
});

如果使用attrs的话,又没办法和template建立关联,这个关联必须自己来建立。

这样的话使用模版的话就有点食之无味弃之可惜的感觉~~感觉还是去除model这个配置,放在attrs里比较好,这样可以让使用者自己判断。

  • 初始化属性的问题

widget在初始化属性的时候,判断是否为空的时候建议可以忽略,因为很多时候这些其实应该在渲染的时候判断,因为你无法知道为空或者其他值得时候默认应该做些什么。

if (!isEmptyAttrValue(val)) {
    this[m](val, undefined, attr)
}
  • 模版解析的增强

很多时候我们需要访问到模版添加到dom后的对象,现在的方式是通过以下方式实现的

this.$('[data-role=title]')

可不可以增加一个扩展点,比如以下的方式:

template: <div data-role="title"></div>

通过模版解析后可以通过如下方式访问到该dom,而无需手动通过this.$方法来访问

this.title

plugin 机制

康辉之前提到过的给 instance 动态增加方法/属性 的机制
和 YUI3 的插件机制一样,我们做了简化

某些情况下 render/show() 死循环

在 autocomplete 组件下, 实例化为:

    var ac = new AutoComplete({
        trigger: '#acTrigger4',
        template: $('#acTrigger4-template').html(),
        dataSource: ['abc', 'abd', 'abe', 'acd'],
        data: [],   // 为空数组
        inputValue: []   // 为空数组
    }).render();

出现错误如下:

Uncaught RangeError: Maximum call stack size exceeded

原因是以下几行代码:

  1. autocomplete 在 onRenderData时有以下逻辑:
if ($.trim(this.$('[data-role=items]').text())) {
                this.show();
            } else {
                this.hide();
            }

https://github.com/aralejs/autocomplete/blob/1.2.0/src/autocomplete.js#L200

即, 当实例render()后, data/inputValue 为 [], 会执行 widget 的 _renderAndBindAttrs(), 并执行 autocomplete 的_onRenderData, 在执行到 上述代码后, 碰到 text() 不为空的时候, 执行 this.show(), 之后, overlay 的 show, 因为 此时 this.rendered 为false, 所以再次执行 this.render(), 导致循环.

建议是:

将 widget的 render() 中, this._renderAndBindAttrs() 和 this.rendered = true 换下位置.

https://github.com/aralejs/widget/blob/master/src/widget.js#L241

widget 1.1.0 版本

整理过去的一些 issue 和新提的 feature,准备升级一个版本。

升级注意

新增点

  1. data-widget-role

  2. delegateEvents 支持其他 DOM 的事件绑定

    this.delegateEvents(this.trigger, 'click p', function() {})
    
  3. 样式方案

    在 this.element 外部会包一层容器,保证样式独立。className 为 family-name-version,css 在编译的时候也会加一层命名空间,如

    .family-name-version .ui-dialog {}
    
  4. set 增加 override

    如果是简单对象,默认是用 merge 的方式,override 可以覆盖当前对象

    Widget.extend({
        attrs: {
            a: {b:1, c:2}
        }
    });
    this.set('a', {b:2}); // => {b:2, c:2}
    this.set('a', {}, {override: true}); // => {}
    
  5. before 能阻止原方法

    this.before('show', function() {
        return false; // 阻止了 show 方法的调用
    });
    

修改点

  1. 不再使用 this.model 和 this.template,使用的时候需要当作 attribute,如 this.get('model')。

  2. templatable 从 widget 中移除,单独为一个组件,修改的时候需要修改 template 的依赖

    "templatable": "arale/templatable/0.9.0/templatable"
    
  3. 注意 attribute 的默认值,当为 '' [] {} 是还是会触发 _onRender

  4. widget 的属性 style id className 默认值改为 null

具体修改点

widget

milestone 1.1.0

  • #36 当属性值为空字符串时的处理
  • #47 不再使用 this.templatethis.model
  • #33 handlebars runtime
  • #26 支持 data-widget-role
  • �[x] #14 delegateEvents 增强
  • #25 destroy 的 bug
  • #39 彻底隔离样式冲突的 widget 方案
  • #38 销毁 widget 创建的 element
  • #40 aralejs/base#14 简化 initAttrs 参数

base

milestone 1.1.0

events

handlebars 全局注册

现在 widget 使用全局注册 helper 的方式,render 后再去除 helper。是否可以替换成下面的方式

template(context, {helpers: helpers, partials: partials, data: data})

templatable 支持 handlebars runtime

接下来我们将使用 handlebars runtime,widget 的 templatable 可以删掉了。

下面介绍一下 runtime 的工作原理:

我们有一个模板:

{{ _ word }}

编译后得到一个 cmd 模块,这个模块返回一个 function,记为 fn。然后我们调用这个 fn:

fn({word: 'hello'}, {
  helpers: {
    '_': function(key) {
        return '你好'
    }
  }
})

就 ok 了。

实例化Widget类时能否覆写attrs的getter方法?

Hello,我遇到一个接口设计方面的问题,场景是一个倒计时功能组件,我期望UI层的内容展现格式可以通过getter方法来让开发者自定义,但是实践后发现实例化Widget时无法通过传入参数覆写getter方法。

示例场景代码如下:
(希望通过getter实现"小于10时十位补0"这类个性化需求)

// 实例化一个CountDown类(Widget的子类)
new CountDown({

    // 下面这个secLast的getter是我想覆写的项,但跑起来后发现没给覆写
    secLast        : { // 离目标时间剩下的秒数
        value : 0,
        getter: function( val ){
            return val < 10 ? ("0" + val) : val; // 剩余秒数小于10时,十位补0
        } 
    },

    element : '#cd',
    targetTime : 10000000..
});

期望效果

还剩 03秒

情况如上,请问我的设计合理吗,或者能否通过别的接口满足这个需求?


补充:试了下通过继承CountDown来覆写secLast的getter是可行的,通过继承来实现这类需求才是正确的做法吗?

var MyCountDown = CountDown.extend({
    attrs : {
        secLast        : {
            value : 0,
            getter: function( val ){
                return val;
            } 
        }   
    }
});

new MyCountDown({
    element : '#cd',
    targetTime : 10000000..
});

template 和 model 属性

目前 widget 有个潜规则,在实例化的时候会将4个属性 (element template model events) 拷贝到 this 上。然后就直接操作 this 上的,比如 this.model

var widget = new Widget({
  model: {},
  template: ''
});
console.log(widget.model);

elementevents 是可以理解的,element 是 widget 的一个核心元素,而 events 根本不应该通过实例化传入。

templatemodel 放到 this 上感觉没什么意义,只是为了操作上的方便。

我觉得使用 attribute 还有 _onRenderXxx 这种功能。比如在 data 变化时能做一些操作。

Widget.extend({
  _onRenderData: function(data) {
    this.renderPaital(...);
  }
})

而且 this.get('model') 和 this.model 使用上也没什么差别。

所以我觉得可以去除将 templatemodel 复制到 this 上的功能。

如何只render自己内部

现在render方法实现的是将element append到parentNode中,可是有时候,我希望能在模型改变后,只刷新这个组件的内部。我应该怎么做呢?

Widget.delegateEvents增强

Widget.delegateEvents绑定事件时,在处理函数中获取触发元素有两种办法:

  • this.$('.classname')
  • ev.target

第一种方式,影响效率,第二种方式可能获取不准确。

建议增强该特性。

Widget.delegateEvents方法中有该段代码:

var callback = function(ev) {
  if (isFunction(handler)) {
    handler.call(widget, ev)
  } else {
    widget[handler](ev)
  }
}

// delegate
if (selector) {
  widget.element.on(eventType, selector, callback)
}

更改为:

var callback = function(ev) {
  var context = this;
  if (isFunction(handler)) {
    handler.call(widget, ev, context)
  } else {
    widget[handler](ev, context)
  }
}

// delegate
if (selector) {
  widget.element.on(eventType, selector, callback)
}

propsInAttrs 的必要性

    // config 中的这些键值会直接添加到实例上,转换成 properties
    propsInAttrs: ['initElement', 'element', 'events'],

这里面的值,最开始是 element, events, model, template,初衷是提供一种简写方式:

this.element 等价 this.get('element')
this.model 等价 this.get('model')

是一种语法糖。后来因为发现 this.templatethis.model 实际上比较少用,所有在上次修改中去掉了。

今天 CR 代码,看到 events 还在,同时还增加了 initElement,再仔细回顾『初心』:

propsInAttrs 是一种简化书写的语法糖

越发觉得 propsInAttrs 是个糟糕的设计,是否应该直接都放到 attrs 里就好?

如果要保留的话,也仅保留 element 就行,让 this.element 等价 this.get("element"),同时在继承时,会自动处理好值的继承?

Widget在dom事件处理上的增强

@lifesinger

目前在widget内部处理element相关的dom事件都可以通过delegateEvents接口来解决,
但如果是 element之外的dom事件(比如popup中的trigger等) 却没办法通过delegateEvents接口来注册,
用jquery原生的dom事件注册又要考虑 context及unbind 的问题( 这2个问题在delegateEvents中已经解决 ),每次因为这个原因 多写一堆几乎相同的代码 真心蛋疼

建议: 能否提供一对类似于delegateEvents/undelegateEvents的接口来处理非element内部的dom事件 ,对 保持处理逻辑的一致性及widget的易用性 来说都有很大的帮助:)

isEmptyAttrValue 不包括 0

数值的初始值为 0,但是 render 时会调用一次。

如果设置成 ''null 不太符合数值初始值。不知道当时是出何考虑?

render 及 destory 方法是否应该支持多次调用?

RT,个人觉得 renderdestory 均为 widget 生命周期的特殊方法,不应该支持多次调用

所以 用 before/after 来限制调用更加适合,也不怕 方法被 override

this.before('render', function(){
    if(this.rendered) return false;
}).after('render', function(){
    this.rendered = true;
});

this.before('destory', function(){
    if(!this.rendered || this.destoryed) return false;
}).after('destory', function(){
    this.destoryed = true;
});

// 这样的话override方法就不要考虑 rendered 的状态了
render: function(){
     // dosomething

    Xxx.superclass.render.call(this);

    return this;
}

Widget 多版本并存时的问题

由于 Widget 上存在静态方法 query,该方法依赖与类相关的私有变量 cachedInstances
这导致 Widget 静态类自身也有状态

当一个页面中存在多个 Widget 时,如果存在 auto render,就会出现问题

需要想一个办法解决掉。

文档 api 增加说明

API 可以说明下产生的原因,必要性,历史渊源等等,这样可以更有说服力。

autoRenderAll 未提供回调机制

目前widget的自动渲染功能.autoRenderAll(),是异步模块加载的实现机制。调用此方法后可能无法立刻获取到对应组件的实例,需要提供一种回调方式,来监听autoRenderFinish。

从 data-api 构建实例时,element 的含义应该可配置

目前 data-api 存放在什么元素上,该元素就会被当成 widget 的 element

但 data-api 不一定仅存放在 widget element 上,还有可能存放在其他角色上,比如 trigger

因此可以考虑扩展:

<div data-element-role="{{data-element-role}}" data-xx=".."></div>

上面自动会构建:

new XX({

  '{{element-role}}': "该 div 元素",
  'xx': ...

})

目前 {{element-role}} 始终是 "element", 改成上面的方式后,就可以自定义了。

并且 data-api 也就可以存放在 widget 的非 element 元素上。

render 的处理方式

image

如上图所示,现在 render 是把节点插入到 body 最下面,这样限制了需要在 dom ready 之后才能进行 render ,否则会在 ie 下报错(中断渲染)。

而且实际在使用中我把代码放在了 $(function() {}) 中也遇到了中断渲染的问题。不知道是不是 jq 有问题,已经通过其他方式解决。

Anyway, 是否可以把处理变成把节点插入到 body 的上部。

关于destroy组件的问题

destroy中调用了undelegateEvents方法:

undelegateEvents: function(eventKey) {
            var args = {};
            // 卸载所有
            if (arguments.length === 0) {
                args.type = DELEGATE_EVENT_NS + this.cid;
            } else {
                args = parseEventKey(eventKey, this);
            }
            this.element.off(args.type, args.selector);
            return this;
        }

建议加上this.element非空判断

this.element&&this.element.off(args.type, args.selector);

这样在window.unload处理组件销毁时避免报错

渲染模版会触发两次的场景

示例

WidgetA = widget.extend({
  attr:{
    a: 1
  },

  _onRenderA: function(val) {
    this.renderPartial(selector);
  }
})

当实例化的时候,模版通过 parseElement 渲染后,又会调用 _onRenderA 再渲染一遍。

需求是 attr 改变后能渲染模版。因为实例化已经渲染过一次了,其实不需要再次渲染。

渲染模板的性能问题

templatable 的代码中,compile 方法每次都调用 Handlebars.compile(template)(model),在每个实例初始化的时候都要编译一遍模板。这对于大部分情况下是很浪费CPU的,特别在ie下,批量渲染 DOM 的时候性能非常差。有改进的空间。

可以做缓存。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.