以真实项目作为背景,先说一下前端的技术选型:Bootstrap 3.3 + JQuery,标准的企业后台开发框架。当前产品对表单操作提出了优化需求,现在需要一个二级联动的下拉框组件,通过一个大类去动态筛选目标项,数据从服务端接口获取。在下图的例子中:通过选择领域再选择开发语言。我选择引入bootstrap-select这个下拉框组件来进行二次封装。(展示代码以伪代码为主)
先做个原型
1 <div> 2 <div> 3 <select id="domains" multiple></select> 4 </div> 5 <div> 6 <select id="languages" multiple></select> 7 </div> 8 </div>
<script> var domains = null; //元数据 var languages = null; //元数据 $(function () { $('#domains').selectpicker(); //初始化组件 $('#languages').selectpicker(); //初始化组件 $('#domains').on('changed.bs.select', function (e, clickedIndex, isSelected, previousValue) { //筛选联动的languages //刷新render languages控件的数据 $('#languages').selectpicker('refresh'); });//绑定domains更新事件 $.ajax({ //从服务端获取domain数据 success: (function (data) { domains = data; //创建options $('#domains').selectpicker('refresh');//刷新控件 }) }) $.ajax({ //从服务端获取language数据 success: (function (data) { languages = data; //创建options $('#languages').selectpicker('refresh');//刷新控件 }) }) }); /** * * 获取已选择的开发语言 * * */ var getSelectedLanguages = function () { var selectOptions = $('#languages').selectpicker().val(); return selectOptions.map(function (val) { //通过options获取对应language }); } </script>
以上的代码,其实已经实现了业务上的需求。即便后端用了Thymeleaf作为模板引擎,将以上两段代码作为fragment引入需要的页面中,它已经可以成为业务“组件”。但它真的算是组件级的重用吗?
1.domains、languages、getSelectedLanguages其实已经在污染了命名空间。
2.它没有一个生命周期的概念。
3.它的重用方式只是代码片段的重用。
封装一下?
<script> var LanguageSelector = { domains: null, languages: null, init: function () { $('#domains').selectpicker(); //初始化组件 $('#languages').selectpicker(); //初始化组件 $('#domains').on('changed.bs.select', function (e, clickedIndex, isSelected, previousValue) { //筛选联动的languages //刷新render languages控件的数据 $('#languages').selectpicker('refresh'); }); //绑定domains更新事件 }, render: function () { $.ajax({ //从服务端获取domain数据 success: (function (data) { domains = data; LanguageSelector.renderDomains(); }) }) $.ajax({ //从服务端获取language数据 success: (function (data) { languages = data; LanguageSelector.renderLanguages(); }) }) }, reset: function () { //重置组件 }, renderDomains: function () { //通过元数据向控件提供数据 }, renderLanguages: function () { //通过元数据向控件提供数据 }, getSelectedLanguages: function () { var selectOptions = $('#languages').selectpicker().val(); return selectOptions.map(function (val) { //通过options获取对应language }); } }; </script>
LanguageSelector.init();
LanguageSelector.render();
LanguageSelector.reset();
LanguageSelector.getSelectedLanguages();
简单地进行了封装,就有组件LanguageSelector。我们能对这个业务组件进行生命周期管理并且将生命周期交给使用者管理。将这个组件以一个“黑盒”的形式发布出去并不是单纯的代码片段复用。
Object or Instance?
使用LanguageSelector object 还是 LanguageSelector's instance?这是实际业务跟开发节奏来考量。在页面只需有单一控件的情况下,使用object无疑对组件开发以及使用复杂度来说是最优解。但对于需要多个相同控件的情况下,我们只能选择使用instance的方式,进而拥有进一步的封装——HTML与JS解耦,更灵活的参数化配置。
总结
前端业务组件的开发,主要是对真实业务的共性的理解和细节与领域的隔离,然后再从现有的技术基础上开发,再根据不同的业务场景进行功能迭代。
因为我之前一直在做游戏服务端开发,对现代前端的框架没有做进一步了解和使用,使用的也是当前工作的技术栈。根据工作的点滴,把真实业务剥离后写下本文以抛砖引玉。