一、 原理:
现在webapp已经全面升级为单页系统,单页系统因为是局部刷新页面,在切页时仍会保留前一个页面的部分状态,比如声明的变量,在某些元素上绑定的事件,导致相关内存不能自动回收,存在内存泄露。
- widget组件中,el指向是切片的单位,也就是说,每个widget组件,只作用于该区域:—可以使用事件代理机制,更小范围的寻找dom元素(不用满document选择元素了); 分配模块管理,便于重用和维护。
- widget通过事件代理机制配置的events,充当了controller的角色,在view里触发各式各样的事件时,触发controller的代理方法,在处理方法中,又可以修改view的HTML。
- 由于大规模的拼接HTML会造成各种维护以及效率问题,所以引入了BaiduTemplate。
- widget中并没有单独分离出model层,而是将所有的逻辑交给controller,灵活性很高。
二、 解决方案
- 规范widget实现:提供一个windows.Widget基类,windows.widget基类有一个静态方法extend,所有widget均由extend方法继承自该基类,extend方法返回的是widget的一个子类构造函数,基类中统一进行事件委托,在widget中提供统一的事件绑定方式。
- 在widget中提供对页面加载各个节点的统一接口,都是页面加载的节点。
- 切页面时回收内存: 切页时解除各引用关系,解绑各事件处理函数,使内存能自动回收。
- 对外提供destroy接口,各人实现自己widget时可以重写destroy接口,实现切页时要做的操作。
三、 API
extend: windows.widget的静态方法,接受一个对象作为参数,返回值是window.Widget的子类,extend接受的参数对象说明:
参数对象属性以及属性值说明:
- el:属性值为字符串,包含该组件的容器,可以是selector—组件所有事情都会委托在该元素上,若不写,会委托在body上,但是建议写,一般是写class或者id值或其他selector带上widget名,避免跟同页面其他widget冲突。如策略监控系统的两个大id为widget-policy-find何widget-policy-alarm,分别对应两个文件夹命名为policyFind和policyAlarm。
- init:属性值为函数,初始化操作,会再createWidget()方法内部调用,可接受参数,参数即为createWidget()方法接收的参数。
- events: 属性值为对象,浏览器事件统一绑定,对象的每个属性值是匿名函数或者函数名的字符串,注意:函数名外引号不要去掉,格式如下:
1
2
3
4events: {
"click[data-node='search'": 'handleForm',
"submit[data-action=create_account]":'createAccount'
}
4 . channels:属性值为对象
1 | channels: { |
补充说明: loadingHandler是一个方法名,在此处调用
destroy: 属性值为函数,切页时要做的操作可以在destroy总实现,会在切页时由基类window.Widget自动调用,预留Api,提供了一些方法:
- _bindCustomerEvent: 绑定广播事件:
- param {string} eventName–事件触发的频道,如common.page;
- param {string} selector 自定义事件名称,如get和show等
- param {funciton} method –事件处理函数
- _unbindBrowserEvent:解绑浏览器事件
- _io–封装ajax请求操作,通过Deferred对象的pass和reject两种状态作为处理ajax成功和失败的操作入口。
- _bindCustomerEvent: 绑定广播事件:
- extend方法返回值说明:
- 类型: window.Widget的子类,有静态方法widgetcreateWidget,接受一个任意类型的参数,该参数会作为实参传给extend参数对象的init方法。
- 总结:调用window.Widget.extend得到widget构造函数,调该构建函数的createWidget方法实例化widget。
- 实例:
- 先依赖:
在fengkong的fe_front_qishan中,依赖入口在page下的antispampalt中:所有页面扩展自layout.tpl。{%require name=“id名: 地址”%};
- 实例中:
1
2
3
4
5
6
7module.export = Widget.extend ({
el: "#大的id名";
events:{ "click[data-node=xxx]: "ssss"} ;
channels: { } ;
init:
urls:
})
- 先依赖:
解析:
- 在init中,拿到$el(根据配置中的选择器);
- 解析DOM,将$el下拥有data-node的元素节点保存在me.$dom下,方便以后引用;
- 如果配置中存在init函数,立即调用,传入
pageName( Widget.extend().createWidget({a:'b',c:'dddd'}) )
,pageName实际为tpl配置的数据; - 执行事件绑定:检测是否存在events配置项,用jQuery事件代替进行逐个绑定;
- 执行事件总线绑定:检测是否存在channels存在,用listener进行逐个处理;
- 最后绑定事件总线common.page,如果执行到,执行销毁,也就说:如果想要将一个组件的所有事件和事件总线进行解绑,就listener.trigger(‘ common.page ‘ , ‘ pagexxxx ‘);
四、 结合当前策略监控平台绑定部分(一):
前三条为总结现有代码以及查阅相关资料所整理,本条结合实例:
关于widget_data传递数据的过程:其实就是:tpl是一种后端模板,后端会将其处理转换成对应的前端的html相关。
1 | {%script%} |
其实就是等于
1 | <!-- require('./App.es6')({%json_encode($widget_data)%}); --> |
分解意思,在require引用es6文件时,将其看作一个函数,后边的( )表示函数立即执行,括号内的参数是解析的$widegt_data,后端在解析时(不管$widget_data)在什么地方,只要碰到了,在解析的时候就能转为前端可以处理的函数,使用最多的 data = $widget_data,也是一个意思,后端会解析$widget_data为对应的格式。
在vue中挂载:
1 | new Vue({ |
然后在子组件中调用方法: 可以参考下图的调用方法
说明:尝试后发现此方法不可取,具体原因还会再进一步分析,但发现绑定在window上之后,可以定义为const变量类型,然后在组件中直接访问变量。