by Jingwei Liu (@th507), Meituan.com
异步请求回来直接插入节点
删除、修改元素的时候需要人工确认调用关系
组件化是一个非常好的尝试,它制定了一套简单的规则。
把元素对应的 JS, CSS 放到一起,提供基本的初始化功能。
现在一般会函数的 init 方法里面处理启动时机
或者在页面上/另一个模块的事件回调里面初始化
提供一系列 on[] 型的接口/配置项?
管理器需要实现一系列接口,每个接口对应一个内部队列
被管理的子模块需要知道管理器的接口列表。
手工初始化比较繁复
// 注册初始化函数
Y.mt.ComponentHub.attach('login-section', function (nd, params) {
Y.mt.www.Login.init(params);
});
// AJAX插入后手工初始化,并注册回调函数
Y.mt.ComponentHub.boot(ndDialog, {
'login-section': handleBooted
});
需要写的代码还是不少
注册回调和初始化应该分离
减少需要写的代码
减少组件化的对外接口
处理不同的启动时机
看看浏览器怎么处理的
我们可以接管元素整个生命周期。
提供默认函数(行为)
不再把所有函数都写到 init 里
需要可覆盖特定默认行为
var defaultScope = {
render: function(ndElement) {
var ndContent = ndElement.one("> textarea[rel='lazyrender']");
if (ndContent) ndElement.setHTML(ndContent.get('value'));
ndContent = null;
ndElement.show();
},
// initialization: setup and event delegation
init: function (ndElement, config) {},
// add post/get data before sending AJAX requests
beforeupdate: function (url, config) {
if (!url) return null;
var _config = config || {};
if (!Y.Lang.isObject(_config)) _config = { args: config };
return Y.merge(_config, { act: url});
},
// update DOM with AJAX return values
update: function (ndElement, value, config) {
if (value && ndElement) ndElement.setHTML(value);
},
// when element is about to be destroyed
teardown: function (ndElement) {
if (!ndElement) return;
// remove all event listener
// on ndElement and its descendents
ndElement.purge(true);
}
};
减少代码量,提高开发效率
<div class="J-hub"
data-hubmodule="www-deal-firstscreen"
data-huburl="/deal/dynamicomponenent"
data-hubconfig="{id: 1223, isMobileOnly: false}">...</div>
<div class="J-hub"
data-huburl="deal/toprecommend"
data-hubconfig="3638724"></div>
<div class="J-hub"
data-hubmodule="cate-nav"
data-hubcss="www/css/cate-nav.css"></div>
<div class="J-hub"
data-hubmodule="www-deal-firstscreen"
data-huburl="/deal/dynamicomponenent"
data-hubconfig="{id: 1223, isMobileOnly: false}">...</div>
每个元素可以自行控制生命周期
而且任何时候重启都可以,由模块自己控制
<div class="site-sidebar J-hub"
data-hubmodule="www-search"
data-hubnamespace="search.sidebar"
data-hubconfig="<?php echo htmlspecialchars($keyword); ?>"
data-huburl="search/sidebar">
</div>
有了默认行为,用模版引擎就变得很简单了:
粗放式的加载:每个组件的加载都是独立的。
n 个组件,意味着 n 个 CSS 请求,n 个 JS 请求,可能还有 n 个异步请求
启动方式单一:自定义启动时机比较复杂 自定义启动需要手工启动,或传入配置项+Component 支持
减少需要写的代码
减少组件化的对外接口
性能优化:无论加载多少组件,性能不会有大的起伏
处理不同的启动时机
各个组件之间通讯比较麻烦
n 个组件,1 个 CSS 请求,1 个 JS 请求,1 个异步请求
对外没有函数接口,通过事件通信
内部只维护一个队列,但支持各元素按需启动
任何时刻插入 DOM 节点的组件会自动初始化
// 注册初始化函数
Y.mt.ComponentHub.attach('login-section', function (nd, params) {
Y.mt.www.Login.init(params);
});
// AJAX插入后手工初始化,并注册回调函数
Y.mt.ComponentHub.boot(ndDialog, {
'login-section': handleBooted
});
// 注册初始化函数:不需要
// AJAX插入后手工初始化:不需要
// 注册回调函数
Y.one('.login-section').on("hub:ready", handleBooted);
Introducing Hub
默认行为 | Hub | Pod |
scan | 分发事件,收集、加载所有 JS 和 CSS | 上报元素需要的 JS 和 CSS |
render | 分发 Pod 进行 render | 渲染/展现元素 |
init | 分发 Pod 进行 init | 运行对应模块或默认的 init |
beforeupdate | 分发事件,收集异步请求 | 准备、并上报异步请求数据 |
update | 发送请求,分发结果到对应 Pod | 处理元素的更新,上报完成 |
主要分工 | 分发事件到每个 Pod 收集返回值 |
处理对应元素的相关行为 上报相关的数据(如需要) |
合并请求
同时下载 CSS 和 JS
提高网络利用效率
不会重复下载
合并异步请求
完成后触发 ready 事件
接受回调
上层对下层分发事件
下层完全不知道上层的代码结构
需要通讯的时候在自身上 fire 事件
这些事件会冒泡到上层
页面和 Hub,Hub 和 Pod,Pod 和元素之间只有发送消息这一种通信方式,消息让各层之间解耦。
外部不能直接调用对象的行为,这样就保证内部数据只有被自己修改。
加上 Promise 可保证生命周期的执行顺序。从而支持任意时刻 pause/resume
直接在元素上 fire 即可,Pod 接管后冒泡到 Hub
Hub 在当前周期结束之后 re-run