ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Marionette 公共 API 完全指南:initialize、triggerMethod、事件/请求绑定与 options 机制

Marionette 公共 API 完全指南:initialize、triggerMethod、事件/请求绑定与 options 机制 前端【免费下载链接】backbone.marionetteMarionette v4 for Backbone applications. Maintenance fixes; new development continues in marionettejs/marionette.项目地址https://gitcode.com/gh_mirrors/ba/backbone.marionette点击查看免费下载Marionette 在 Backbone 之上提供了一套贯穿所有类的公共方法与约定包括实例化钩子、事件触发、事件/请求绑定、方法归一化以及 options 读写机制。本文以 docs/common.md 为主体结合仓库源码src/mixins/common.js、src/common/ 下的各实现文件与单元测试系统讲解这些 API 的行为、源码原理与实战用法读完即可在任何 Marionette 类View、MnObject、Behavior、Region 等中熟练运用它们。适用范围哪些类共享这些功能文档开篇即指出Marionette 有若干方法对 所有类 通用。在源码层面这些能力被统一封装在CommonMixin中src/mixins/common.js它依次导入了normalizeMethods、mergeOptions、getOption、bindEvents、unbindEvents、bindRequests、unbindRequests、triggerMethod并通过_.extend(CommonMixin, Backbone.Events)把 Backbone.Events 也混入其中——这正是“所有 Marionette 类都拥有 Backbone.Events API”的源码依据。CommonMixin随后被混入到各具体类上例如 MnObject_.extend(MarionetteObject.prototype, CommonMixin, DestroyMixin, RadioMixin, ...)和 ViewBackbone.View.extend后通过构造器调用this._setOptions(options, ClassOptions)。因此下列所有 API 在 MnObject、View、CollectionView、Behavior、Region、Application 等类上均可用。initialize实例化钩子与 Backbone 类一致initialize是你在任何 Marionette 类上可以定义的方法在类被实例化时自动调用并接收实例化时传入的全部参数。第一个参数通常包含该类附加到实例上的 options。import { MnObject } from backbone.marionette; const MyObject MnObject.extend({ initialize(options, arg2) { console.log(options.foo, this.getOption(foo), arg2); } }); const myObject new MyObject({ foo: bar }, baz); // logs bar bar baz从源码看MnObject的构造器执行顺序是this._setOptions(options, ClassOptions)→ 生成cid→_initRadio()→this.initialize.apply(this, arguments)src/object.js即options 的合并发生在initialize被调用之前所以initialize内部可以立即使用this.options和this.getOption(...)。extend基于类的继承extend继承自 Backbone在所有类定义上可用用于基于类的继承。详细说明见 基于类的继承。在源码中MarionetteObject.extend extendsrc/object.js其实现位于 src/utils/extend.js与 Backbone 的继承语义一致。Events API事件系统Backbone.Events API 对所有类可用。每个 Marionette 类既可以listenTo任何拥有该 API 的对象也可以在自己的实例上触发事件。注意这里的事件 API 不要与视图的events混淆——后者视图 DOM 事件捕获的是 DOM 事件如 click、submit而 Events API 处理的是对象间的自定义事件。源码层面CommonMixin通过_.extend(CommonMixin, Backbone.Events)src/mixins/common.js把on、off、trigger、listenTo、stopListening等全部方法混入类原型这是所有类“开箱即用”事件能力的直接来源。triggerMethod触发事件并调用对应方法triggerMethod触发一个事件并同时调用 一个对应的方法。它与 Backbone 的trigger相同但额外增加了方法处理器。命名规则事件被触发时事件名每一节的首字母会被大写并在前面加上on前缀triggerMethod(foo)触发foo事件并调用onFoo方法triggerMethod(before:foo)触发before:foo事件并调用onBeforeFoo方法参数传递传给triggerMethod的所有参数会同时传给事件和方法唯一的例外是事件名本身不会传给对应方法。例如triggerMethod(foo, bar)会调用onFoo(bar){...}。import { MnObject } from backbone.marionette; const MyObject MnObject.extend({ initialize(){ this.triggerMethod(foo, baz); }, onFoo(bar){ console.log(bar); } }); const myObj new MyObject(); // console.log baz myObj.triggerMethod(foo, qux); // console.log qux源码原理src/common/trigger-method.js实现用正则/(^|:)(\w)/gi把事件名切分并大写再拼出方法名on event.replace(splitter, getEventName)方法名结果缓存在methodCache中避免重复计算。随后通过getOption.call(this, methodName)在当前对象含 options详见下文 getOption 一节上查找方法若存在则以method.apply(this, _.drop(arguments))调用剔除事件名参数最后仍会this.trigger.apply(this, arguments)把原始事件正常广播出去并将方法返回值作为triggerMethod的返回值。关于triggerMethod的更多信息参见 Marionette 事件文档。bindEvents绑定外部实体的事件bindEvents用于绑定任何实现了 Backbone.Events API 的对象包括所有 Backbone 类、Marionette 类以及 Radio 频道。import Radio from backbone.radio; import { View } from backbone.marionette; const MyView View.extend({ fooEvents: { change:foo: doSomething }, initialize(){ this.fooChannel Radio.channel(foo); this.bindEvents(this.fooChannel, this.fooEvents); }, doSomething(){ // the change:foo event was fired from the radio channel // respond to it appropriately, here. } });参数说明第一个参数是entityBackbone.Model、Backbone.Collection 或任何混入了 Backbone.Events 的对象即事件的来源。第二个参数是{ event:name: eventHandler }形式的配置哈希除了字符串处理器名也可以直接提供一个函数。错误若第二个参数不是对象会抛出错误。源码原理src/common/bind-events.js先通过normalizeBindings校验绑定哈希必须是对象否则抛出带common.html#bindevents定位信息的 MarionetteError再用normalizeMethods.call(this, bindings)把字符串处理器名解析成this上下文中的真实函数引用最后执行this.listenTo(entity, bindings)。这里listenTo的语义意味着当当前视图/对象被销毁时Backbone 会自动清理这些监听避免内存泄漏。unbindEvents解除外部实体的事件绑定unbindEvents用于解除任何实现了 Backbone.Events API 的对象的绑定适用范围与bindEvents相同所有 Backbone 类、Marionette 类与 Radio 频道。不传事件哈希调用时会解除该实体上的所有事件绑定。import Radio from backbone.radio; import { View } from backbone.marionette; const MyView View.extend({ fooEvents: { change:foo: onChangeFoo, stop: onStop }, initialize(){ this.fooChannel Radio.channel(foo); this.bindEvents(this.fooChannel, this.fooEvents); }, onChangeFoo(){ // the change:foo event was fired from the radio channel // respond to it appropriately, here. // Doing something this.listenTo(this.fooChannel, adhoc, this.render); }, onStop() { // Removes all fooEvents this.unbindEvents(this.fooChannel, this.fooEvents); // Removes all bound fooChannel events including adhoc this.unbindEvents(this.fooChannel); } });参数说明第一个参数是entity即要解除绑定的对象。第二个参数是{ event:name: eventHandler }配置哈希也可用函数替代字符串处理器名若不提供第二个参数则移除该实体上的全部监听。源码原理src/common/bind-events.js未提供绑定哈希时调用this.stopListening(entity)一次性清除提供了哈希时则this.stopListening(entity, normalizeBindings(this, bindings))只针对指定的实体与事件做精确解绑。bindRequests绑定 Radio 请求bindRequests用于绑定任何实现了 Backbone.Radio Request API 的对象目前主要就是 Radio 频道。import Radio from backbone.radio; import { View } from backbone.marionette; const MyView View.extend({ channelName: myChannelName, radioRequests: { foo:bar: doFooBar }, initialize() { const channel Radio.channel(this.channelName); this.bindRequests(channel, this.radioRequests); }, doFooBar() { console.log(foo:bar); return bar; } }); const myView new MyView(); const channel Radio.channel(myChannelName); channel.request(foo:bar); // Logs foo:bar and returns bar参数说明第一个参数channel是Radio的频道实例。第二个参数是{ request:name: replyHandler }形式的配置哈希同样支持用函数替代字符串处理器名。错误若第二个参数不是对象会抛出错误。源码原理src/common/bind-requests.js同样先校验并归一化绑定哈希然后执行channel.reply(normalizeBindings(this, bindings), this)即以this作为 reply 回调的上下文。注意与bindEvents不同bindRequests的绑定在对象销毁时不会被 Backbone 自动清理需要显式调用unbindRequests见下。unbindRequests解除 Radio 请求绑定unbindRequests用于解除任何实现了 Backbone.Radio Request API 的对象的请求绑定。不传请求哈希调用时会解除该频道上的所有请求处理器。重要提醒为避免内存泄漏应在onBeforeDestroy中或之前调用unbindRequests。import Radio from backbone.radio; import { View } from backbone.marionette; const MyView View.extend({ channelName: myChannelName, radioRequests: { foo:bar: doFooBar }, onAttach() { const channel Radio.channel(this.channelName); this.bindRequests(channel, this.radioRequests); }, onBeforeDetach() { const channel Radio.channel(this.channelName); this.unbindRequests(channel, this.radioRequests); } });参数说明第一个参数channel是Radio的频道实例。第二个参数是{ request:name: replyHandler }配置哈希可用函数替代字符串名不提供时移除该频道上该对象的全部处理器。源码原理src/common/bind-requests.js未提供哈希时执行channel.stopReplying(null, null, this)清除该对象在该频道上的全部回复提供哈希时执行channel.stopReplying(normalizeBindings(this, bindings), this)做精确解绑。补充说明——Marionette 的 Radio 集成如果你在类上直接声明channelName、radioEvents、radioRequests属性Marionette 的 RadioMixin 会在_initRadio()中自动完成bindEvents/bindRequests的绑定并在destroy事件时自动调用_destroyRadio()即this._channel.stopReplying(null, null, this)做清理src/mixins/radio.js。手动使用bindRequests时仍需自行在onBeforeDestroy中解绑。normalizeMethods把方法名哈希归一化为函数引用normalizeMethods接收一个由事件名与函数/函数名组成的哈希返回同一个结构但函数名已被替换为真实函数引用的哈希。import { View } from backbone.marionette; const MyView View.extend({ initialize() { const hash { action:one: handleActionOne, // This will become a reference to this.handleActionOne action:two: this.handleActionTwo }; this.normalizedHash this.normalizeMethods(hash); }, do(action) { this.normalizedHash[action](); }, handleActionOne() { console.log(action:one was fired); }, handleActionTwo() { console.log(action:two was fired); } }); const myView new MyView(); myView.do(action:one); myView.do(action:two);源码原理src/common/normalize-methods.js实现用_.reduce遍历哈希对每个方法名若不是函数则通过this[method]解析为当前对象上的方法引用只有解析成功的项才会被写入结果哈希。这一机制是bindEvents、bindRequests等 API 能同时接受“字符串方法名”和“直接函数”两种写法的底层支撑也是你自定义类时复用同一套绑定约定的基础。getOption读取选项getOption用于读取一个选项值如果该名称未在 options 中定义则回退到实例上同名属性。import { View } from backbone.marionette; const View View.extend({ classVal: class value, initialize(){ this.instanceVal instance value } }); const view new View({ optVal: option value }); view.getOption(instanceVal); // instance value view.getOption(classVal); // class value view.getOption(optVal); // option value const view2 new View({ instanceVal: foo, classVal: bar, optVal: baz }); view.getOption(instanceVal); // foo view.getOption(classVal); // bar view.getOption(optVal); // bazFalsey 值的行为getOption会从 options 中返回除undefined之外的任何 falsey 值。如果 options 中该键的值是undefined它会转而从对象本身读取。import { MnObject } from backbone.marionette; const MyObject MnObject.extend({ foo: bar, initialize() { console.log(this.getOption(foo)); } }); const model1 new MyObject(); // bar const myObj {}; console.log(myObj.foo); // undefined const model2 new MyObject({ foo: myObj.foo }); // bar这个例子中两次都返回bar第一次是回退到类定义上的foo: bar第二次虽然显式传入了foo但其值为undefined因此getOption依然回退到对象属性foo: bar。源码原理src/common/get-option.js实现极为简洁——if (this.options (this.options[optionName] ! undefined))则返回this.options[optionName]否则返回this[optionName]。判断用的是! undefined而不是真值判断这正是“falsey 但非 undefined 的值如false、0、也能从 options 原样返回”的原因。相关行为在 test/unit/common/get-option.spec.js 中有完整覆盖包括无参数返回undefined、仅定义在类上、仅定义在 options 中、options 中为 falsey 值、options 中为undefined、两者同时定义时 options 优先等六种场景。mergeOptions把指定键合并到实例mergeOptions接收两个参数一个options对象以及要从其中提取的keys数组。任何匹配的keys都会被直接合并到类实例上。import { MnObject } from backbone.marionette; const MyObject MnObject.extend({ initialize(options) { this.mergeOptions(options, [model, something]); // this.model and this.something will now be available } }); const myObject new MyObject({ model: new Backbone.Model(), something: test, another: value }); console.log(myObject.model); console.log(myObject.something); console.log(myObject.getOption(another));在这个例子中model和something直接作为MyObject实例的属性可用而another必须通过getOption访问。当你希望某些键在整个类中被高频使用、不想每次都this.options.xxx时mergeOptions非常方便。源码原理src/common/merge-options.js实现用_.each(keys, ...)遍历键仅当option ! undefined时才执行this[key] option即undefined值不会被覆盖到实例上。该方法在 Marionette 内部被大量使用每个类构造时都会用this._setOptions(options, ClassOptions)把类专属的 ClassOptions 合并到实例见 src/mixins/common.js。例如 MnObject 的 ClassOptions 是[channelName, radioEvents, radioRequests]而 View 则包含behaviors、events、modelEvents、regions、template、triggers、ui等一整套选项。TheoptionsProperty类定义中的默认 optionsMarionette 类在类定义中接受一个options属性它会与实例化时传入的options参数合并传入的 options 值覆盖属性中的值。initialize方法中接收的options参数与实例化时传入的完全一致。要在initialize内取到考虑过options属性合并结果的值必须使用getOption。import { MnObject } from backbone.marionette; const MyObject MnObject.extend({ options: { foo: bar, another: thing }, initialize(options) { console.log(options.foo) // undefined console.log(this.getOption(foo)) // bar console.log(this.getOption(another)) // value } }); const myObject new MyObject({ another: value });关键点initialize(options)收到的options就是实例化时传入的那个原始对象其中没有foo而类定义中的默认options属性已经由_setOptions合并进this.options——合并逻辑是_.extend({}, _.result(this, options), options)src/mixins/common.js即默认值在前、实例化参数在后因此实例化参数优先。注意_.result意味着options既可以是对象也可以是返回对象的函数相关合并顺序与“不修改传入 options 参数”的行为在 test/unit/mixins/common.spec.js 中有测试覆盖。因此initialize内读取合并结果必须用this.getOption(foo)。实战组合建议以下三条约定可以让你的 Marionette 类既简洁又稳健默认值放在类定义options中实例化参数覆盖它——例如把channelName、regionClass等配置项写成类默认值使用时再按需传入。用mergeOptions把高频键提到实例上其余低频配置通过this.options.xxx或getOption读取避免实例属性被无关键污染。Radio 请求绑定务必成对清理在onAttach/initialize中bindRequests在onBeforeDestroy或onBeforeDetach中unbindRequests防止频道 handler 悬挂导致内存泄漏。延伸阅读Marionette 类一览了解这些公共 API 作用在哪些具体类上事件系统详解triggerMethod与onEvent绑定的更多细节Backbone.Radio 集成Radio 频道、请求与 Marionette 的集成方式MnObject 源码 与 CommonMixin 源码公共 API 的最终实现入口赞分享前端【免费下载链接】backbone.marionetteMarionette v4 for Backbone applications. Maintenance fixes; new development continues in marionettejs/marionette.项目地址https://gitcode.com/gh_mirrors/ba/backbone.marionette点击查看免费下载相关推荐Marionette 事件系统完全指南triggerMethod、子视图事件冒泡与生命周期事件Marionette 事件系统完全指南triggerMethod、子视图事件冒泡与生命周期事件 Marionette 在 Backbone 的 Events前端Marionette Class Events 完全指南基于 triggerMethod 的视图与应用生命周期事件体系Marionette Class Events 完全指南基于 triggerMethod 的视图与应用生命周期事件体系 导读 backbone.marione前端node-restify Request API 完全指南请求对象、服务端事件与请求日志node restify Request API 完全指南请求对象、服务端事件与请求日志 导读 本指南基于 node restify 官方 API 文档 do后端上一篇Mac Mouse Fix终极指南3种安装方式与核心功能全解析下一篇3步搞定DOL汉化工具新手零基础入门指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表