Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

为什么 .add() API 没有采用队列机制? #22

Open
cssmagic opened this issue Dec 24, 2014 · 1 comment
Open

为什么 .add() API 没有采用队列机制? #22

cssmagic opened this issue Dec 24, 2014 · 1 comment

Comments

@cssmagic
Copy link
Owner

为什么 .add() API 没有采用队列机制?

本问题摘自 C4 前端交流会的现场观众提问。

背景

大家最熟悉的 DOM 事件 API .addEventListener() 是队列机制——可以为元素的同一事件多次添加事件处理函数,当事件触发时,这些事件处理函数会被依次调用。

因此,当大家看到 action.add() 这个 API 时,很自然就会发出疑问:它的工作方式是队列式吗?如果不是,为什么?

文档

实际上 .add() API 并没有设计队列机制。对于重复添加同名动作的情况,文档是这样描述的:

如果在定义动作时使用了已经存在的动作名,则相当于用新的动作函数替换原有的动作函数。原因在于 action.add() 方法添加的是 “动作”,不是事件监听器;而每个动作名只能对应一个动作函数。

如果你觉得 .add() 这个接口名容易误解,可以自行创建并使用 .define().register() 这样的别名。

设计

从上述文档可以看出,Action 的设计力求 “简单易用”:

  • 用 “动作” 的概念屏蔽事件、冒泡、监听器等底层实现。
  • 动作采用基本的 “名值对” 模型,“一个萝卜一个坑”。

引入队列机制,会使整概念模型复杂化。作为系统中的一项基础设施,Action 必须做到简单、易理解、易排错,因为当大量上层代码依赖这个基础设施时,稳定性必定是它的首要目标。

同时,对一个通用型类库来说,不加节制地增强功能,并不会令它更加通用;相反,膨胀的体积和大多数人用不到的功能反而会限制它的适用面。一个单纯的机制,恰恰更容易被按需扩展。

扩展

每个类库都有适用场景,Action 的原生功能适用于中小型网站项目;而对于更复杂的场景,Action 也可以通过各种方式在上层被扩展。

在 C4 前端交流会上,有同学认为,由于 .add() 没有队列机制(不可以对同一动作追加多个动作函数),无法满足以下需求:

如果我有一组类似的按钮需要执行一些共同的动作、同时也有自己特有的动作,如何实现?

实际上,我们完全可以在动作函数这个层面来完成业务逻辑的抽象。为便于讨论,我们先约定场景和术语:“Btn A” 和 “Btn B” 都需要执行自己的特有行为 fnAfnB,同时它们也需要执行共同行为 fnC。那么我们可以采用以下一些的扩展方案:

  • 使用 AOP 等方式来给 fnAfnB 分别追加 fnC,得到 fnACfnBC,再把它们作为真正的动作函数传给 .add() 方法。

  • .add() 只管接收动作列表(用对象组织的名值对),至于这个动作列表是怎么来的,它是不关心的。我们完全可以用一套更高级的机制来构造出自己需要的动作列表。假设我们根据自己的业务需要实现了一套 ActionComposer,可以按需构造动作函数:

    var myActions = {
        'action-a': ActionComposer.combine('fnA', 'fnC'),
        'action-b': ActionComposer.combine('fnB', 'fnC')
    }
    action.add(myActions)

    只要 myActions 是合法的数据结构,.add() 就能正常使用。

  • 顺着上面的思路进一步拓展,我们甚至可以不用操心动作列表的产生。我们可以预先定一些原子操作(比如这里的 fnAfnBfnC),然后在 HTML 层面通过一定的命名约定来书写动作名。比如:

    <button data-action="a+c">Btn A</button>
    <button data-action="b+c">Btn B</button>

    然后使用一个构建工具来扫描 HTML,自动生成以下结构并传递给 action.add()

    {
        'a+c': ActionComposer.combine('fnA', 'fnC'),
        'b+c': ActionComposer.combine('fnB', 'fnC')
    }

    这样连定义动作这一步都可以交给程序来自动完成了。

总之,我们的思路就是把 Action 当作一种底层的基础设施来用——上层的业务逻辑可以很复杂,但底层可以很简单!

@cssmagic
Copy link
Owner Author

为避免可能存在的歧义,从 v0.4 开始,action.add() 更名为 action.define()

action.add() 仍可使用,但已不建议。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant