快捷搜索:
来自 新京葡娱乐场网址 2019-10-14 11:23 的文章
当前位置: 67677新澳门手机版 > 新京葡娱乐场网址 > 正文

前端进阶篇之如何编写可维护可升级的代码

前端进级篇之怎么着编写可体贴可晋级的代码

2015/10/16 · JavaScript · 1 评论 · 代码

原来的文章出处: 叶小钗(@欲苍穹)   

前言

本人还在游侠客的做职业的时候,每一个周围不难的运动页面背后往往会掩饰5个以上的数量哀告,此中最过复杂的当属机票与酒店的订单填写作业代码

此地先看看比较“容易”的机票代码:

图片 1

接下来看看稍微复杂的酒店专门的职业逻辑:

图片 2

机票二个页面包车型客车代码量到达了四千行代码,而旅馆的代码竟然超越了七千行,这里还不包蕴模板(html)文件!!!

然后初略看了机票的代码,就该页面也许发生的接口诉求有18个之多!!!而酒馆的的并行DOM事件基本多到了天怒人怨的境地:

图片 3

当然,机票团队的互动DOM事件早就多到了自己台式机无法截图了:

JavaScript

events: { 'click .js_check_invoice_type': 'checkInvoiceType', //切换小票类型 'click .flight-hxtipshd': 'huiXuanDesc', //惠选表达'click .js_ListReload': 'hideNetError', 'click #js_return': 'backAction', //再次回到列表页 'click div[data-rbtType]': 'showRebate', //插烂返现表明 'click #paybtn .j_btn': 'beforePayAction', //提交订单 //flightDetailsStore, passengerQueryStore, mdStore, postAddressStorage, userStore, flightDeliveryStore 'click .flight-loginbtn2': 'bookLogin', //登陆 'input #linkTel': 'setContact', //保存客户输入的交流人 'click #addPassenger .flight-labq': 'readmeAction',//姓名援助 'click .jsDelivery': 'selDelivery', //选取配送情势 'click #jsViewCoupons': 'viewCoupons', //查看费用券使用表明 //flightDetailsStore // 'click .j_refundPolicy': 'fanBoxAction', //查看返现音信 //'click .flight-bkinfo-tgq .f-r': 'tgBoxAction', //查看退改签 'click .js_del_tab': 'showDelListUI', //配送格局 // 'click .js_del_cost .flight-psf i': 'selectPaymentType', // 选择快递开支情势 'click #js_addrList': 'AddrListAction', //选取地址 'click #date-picker': 'calendarAction', //定票日期 //airportDeliveryStore 'click #done-address': 'zqinairselect', //领票柜台 'click #selectCity': 'selectCityAction', //选择城市 'click #date-zqtime': 'showZqTimeUI', //买票时间 //airportDeliveryStore 'click #jsinsure': 'viewInsure', //保证表达 'click #js_invoice_title': 'inTitleChangeWrp', //发票抬头更动 // userStore, flightOrderInfoInviceStore, flightOrderStore //don't move outside 'click #js_invoice_title_div': 'inTitleChangeWrp', 'click .flight-icon-arrrht': 'showinTitleList', //‘ ’号,跳转载票抬头列表 //userStore, invoiceULX570LStore 'focusin #linkTel': 'telInput', 'focusout #linkTel': 'telInputFinish', 'touchstart input': 'touchStartAction', // 处理Android手机上点击不灵敏难点 'click #package .flight-arrrht': 'packageSelect', 'focusin input': 'hideErrorTips', 'click #dist_text_div': 'hideErrorTips', 'click .j_PackageNotice': 'toggletips', 'click .j_AnnouncementNotice': 'toggleNotice', 'click #travalPackageDesc': 'forwardToTravalPackage', //don't move into child modules 'click #airInsureDesc': 'showAirInsureDesc', 'click #paybtn': 'orderDetailAction',//价格明细 'click .J_retriveVerifyCodeBtn': 'getVerifyCode', 'click .J_toPay': 'toPayAction', 'click .J_closeVerifyCode': 'closeVerifyCodePopup', 'keyup .J_verifyCodePopup input': 'setToPayBtnStatus', 'click .js_flight_seat': 'selectRecommendCabin', // 采用推荐仓位 'click .j_changeFlight': 'changeFlightAction', // 推荐航班弹层中退换航班 'focusin input:not([type=tel])': 'adjustInputPosition', // iphone5/5s ios8搜狗输入法遮住input 'click .js_addr,#js_addr_div': 'editDeliverAddress',//报废凭据,详细地址编辑 'click .js_showUserInfo': 'showUserInfo', // add by hkhu v2.5.9 'click #logout': 'logout', // add by hkhu v2.5.9 'click #gotoMyOrder': 'gotoMyOrder', // add by hkhu v2.5.9 'touchstart #logout': function (e) { $(e.currentTarget).addClass('current'); }, 'touchstart #gotoMyOrder': function (e) { $(e.currentTarget).addClass('current'); }, 'click .js_buddypayConfirm': 'buddypayConfirmed', 'click .js_pickupTicket': 'viewPickUp', //261接送机券表达 'click .flt-bking-logintips': 'closelogintips'//关闭接送机券提醒 },

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
events: {
'click .js_check_invoice_type': 'checkInvoiceType', //切换发票类型
'click .flight-hxtipshd': 'huiXuanDesc', //惠选说明
'click .js_ListReload': 'hideNetError',
'click #js_return': 'backAction', //返回列表页
'click div[data-rbtType]': 'showRebate', //插烂返现说明
'click #paybtn .j_btn': 'beforePayAction', //提交订单                        //flightDetailsStore, passengerQueryStore, mdStore, postAddressStorage, userStore, flightDeliveryStore
'click .flight-loginbtn2': 'bookLogin', //登录
'input #linkTel': 'setContact', //保存用户输入的联系人
'click #addPassenger .flight-labq': 'readmeAction',//姓名帮助
'click .jsDelivery': 'selDelivery', //选择配送方式
'click #jsViewCoupons': 'viewCoupons', //查看消费券使用说明                                                  //flightDetailsStore
// 'click .j_refundPolicy': 'fanBoxAction', //查看返现信息
//'click .flight-bkinfo-tgq .f-r': 'tgBoxAction', //查看退改签
'click .js_del_tab': 'showDelListUI', //配送方式
//            'click .js_del_cost .flight-psf i': 'selectPaymentType', // 选择快递费用方式
'click #js_addrList': 'AddrListAction', //选择地址
'click #date-picker': 'calendarAction', //取票日期                                                                    //airportDeliveryStore
'click #done-address': 'zqinairselect', //取票柜台
'click #selectCity': 'selectCityAction', //选择城市
'click #date-zqtime': 'showZqTimeUI', //取票时间                                                                        //airportDeliveryStore
'click #jsinsure': 'viewInsure', //保险说明
'click #js_invoice_title': 'inTitleChangeWrp', //发票抬头更改                // userStore, flightOrderInfoInviceStore, flightOrderStore    //don't move outside
'click #js_invoice_title_div': 'inTitleChangeWrp',
'click .flight-icon-arrrht': 'showinTitleList', //‘ ’号,跳转发票抬头列表                 //userStore, invoiceURLStore
'focusin #linkTel': 'telInput',
'focusout #linkTel': 'telInputFinish',
'touchstart input': 'touchStartAction', // 处理Android手机上点击不灵敏问题
'click #package .flight-arrrht': 'packageSelect',
'focusin input': 'hideErrorTips',
'click #dist_text_div': 'hideErrorTips',
'click .j_PackageNotice': 'toggletips',
'click .j_AnnouncementNotice': 'toggleNotice',
'click #travalPackageDesc': 'forwardToTravalPackage',       //don't move into child modules
'click #airInsureDesc': 'showAirInsureDesc',
'click #paybtn': 'orderDetailAction',//价格明细
'click .J_retriveVerifyCodeBtn': 'getVerifyCode',
'click .J_toPay': 'toPayAction',
'click .J_closeVerifyCode': 'closeVerifyCodePopup',
'keyup .J_verifyCodePopup input': 'setToPayBtnStatus',
'click .js_flight_seat': 'selectRecommendCabin', // 选择推荐仓位
'click .j_changeFlight': 'changeFlightAction', // 推荐航班弹层中更改航班
'focusin input:not([type=tel])': 'adjustInputPosition', // iphone5/5s ios8搜狗输入法遮住input
'click .js_addr,#js_addr_div': 'editDeliverAddress',//报销凭证,详细地址编辑
'click .js_showUserInfo': 'showUserInfo', // add by hkhu v2.5.9
'click #logout': 'logout', // add by hkhu v2.5.9
'click #gotoMyOrder': 'gotoMyOrder', // add by hkhu v2.5.9
'touchstart #logout': function (e) { $(e.currentTarget).addClass('current'); },
'touchstart #gotoMyOrder': function (e) { $(e.currentTarget).addClass('current'); },
'click .js_buddypayConfirm': 'buddypayConfirmed',
'click .js_pickupTicket': 'viewPickUp', //261接送机券说明
'click .flt-bking-logintips': 'closelogintips'//关闭接送机券提示
},

就这种体积的页面,假使须求迭代必要、打BUG补丁的话,笔者敢分明的说,一个BUG的修复很轻巧引起其余BUG,而地点还只有是里面叁个作业页面,前面还应该有强盛而复杂的前端框架呢!如此复杂的前端代码维护职业可不是开玩笑的!

PS:说道此处,不得不为途牛的前端水平点个赞,行业内部稀少的单页应用,一套代码H5&Hybrid同期运维不说,还解决了SEO难点,嗯,十分赞。

怎么珍贵这种页面,怎么着设计这种页面是大家昨日商量的第一,而上述是乐途合併后的代码,他们五个组织的宏图思路不便在那地打开。

前天,作者这里提供一个思路,认真读书此文只怕在偏下方面前遇到你富有利于:

JavaScript

① 怎么着将贰个错综相连的页面拆分为四个个独立的页面组件模块 ② 怎么样将分拆后的事务组件模块重新合为三个一体化的页面 ③ 从重构角度看组件化开辟推动的实惠 ④ 从前端优化的角度对待组件化开垦

1
2
3
4
① 如何将一个复杂的页面拆分为一个个独立的页面组件模块
② 如何将分拆后的业务组件模块重新合为一个完整的页面
③ 从重构角度看组件化开发带来的好处
④ 从前端优化的角度看待组件化开发

文中是自身个人的一些框架&业务支出经历,希望对各位有用,也期待各位万般扶助探究,提出文中不足以至提议您的一对建议

鉴于该品种事关到了等级次序拆分与统一,基本属于七个整机的前端工程化案例了,所以将之放到了github上:https://github.com/yexiaochai/mvc

内部工程化一块的代码,后续会由另一个人小同伙持续更新,要是该文对各位有所支持的话请各位给品种点个赞、加颗星:)

自身信赖只借使中间水平的前端,认真读书此文一定会对你有某个拉拉扯扯滴。

八个实在的景色

亲自过问地址

代码仓促,大概会有BUG哦:)

代码地址:

页面基本构成

因为订单填写页经常有密度,小编那边挑选相对复杂而又尚未密度的成品列表页来做表明,个中框架以至业务代码已经做过抽离,不会满含敏感新闻,一些优化后续会联合到开源blade框架中去。

我们这里列表页的首屏页面如下:

图片 4

粗略来讲组成如下:

① 框架等级UI组件UIHeader,头部组件

② 点击日期会出框架等级UI,日历组件UICalendar

③ 点击出发时段、出发小车站、达到汽车站,皆会出框架等级UI

④ header上面包车型客车日期工具栏须求充任独立的职业模块

⑤ 列表区域能够用作独立的事务模块,可是与主业务靠太近,不太相符

⑥ 出发时段、出发小车站、达到小车站皆已单身的作业模块

二个页面被我们拆分成了几个小模块,我们只须要关爱模块内部的并行完成,而囊括业务模块的通讯,业务模块的样式,业务模块的录取,一时有以下约定:

JavaScript

① 单个页面包车型地铁样式全体写在三个文本中,比如list里面装有模块对应的是list.css ② 模块之间利用观看者情势观看数据实体变化,以多少为媒介通讯 ③ 平日的话事业模块不可重用,假如有重用的模块,供给分离到common目录中,因为咱们今日不思索common重用,那块一时不予理睬

1
2
3
① 单个页面的样式全部写在一个文件中,比如list里面所有模块对应的是list.css
② 模块之间采用观察者模式观察数据实体变化,以数据为媒介通信
③ 一般来说业务模块不可重用,如果有重用的模块,需要分离到common目录中,因为我们今天不考虑common重用,这块暂时不予理睬

此间有个别朋友恐怕以为单个模块的CSS以至image也应该加入单独,小编这里不太同意,业务页面样式粒度太细的话会给规划带来相当的大的费劲,这里再以通俗的话来讲:尼玛,笔者CSS功底平时,拆分的太细,对自己的话难度太高……

不佳的做法

倒霉的那些事情莫过于是相对的,因为不好的做法日常是比较简单的做法,对于二次性项目依然业务比较轻松的页面来讲反而是好的做法,比如这里的作业逻辑能够那样写:

JavaScript

define(['AbstractView', 'list.layout.html', 'list.html', 'BusModel', 'BusStore', 'UICalendarBox', 'UILayerList', 'cUser', 'UIToast'], function (AbstractView, layoutHtml, listTpl, BusModel, BusStore, UICalendarBox, UILayerList, cUser, UIToast) { return _.inherit(AbstractView, { propertys: function ($super) { $super(); //一批基础属性定义 //...... //交互业务逻辑 this.events = { 'click .js_pre_day': 'preAction', //点击前一天触发 'click .js_next_day': 'nextAction', //点击后一天触发 'click .js_bus_list li': 'toBooking', //点击列表项目触发 'click .js_show_calendar': 'showCalendar', //点击日期项出日历组件 'click .js_show_setoutdate': 'showSetoutDate', //筛选出发时段 'click .js_show_setstation': 'showStation', //筛选出发站 'click .js_show_arrivalstation': 'showArrivalStation', //筛选达到站 //迭代必要,扩展别的频道入口 'click .js-list-tip': function () {} }; }, //开首化尾部标题栏 initHeader: function (t) { }, //第一回dom渲染后,发轫化后续会用到的有所dom成分,以防再度获取 initElement: function () {}, showSetoutDate: function () {}, showStation: function () {}, showArrivalStation: function () {}, showCalendar: function () {}, preAction: function (e) {}, nextAction: function () {}, toBooking: function (e) {}, listInit: function () {}, bindScroll伊芙nt: function () {}, unbindScroll伊夫nt: function () { }, add伊夫nt: function () { this.on('onShow', function () { //当页面渲染甘休,须要做的最初化操作,举个例子渲染页面 this.listInit(); //...... }); this.on('onHide', function () { this.unbindScroll伊芙nt(); }); } }); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
define(['AbstractView', 'list.layout.html', 'list.html', 'BusModel', 'BusStore', 'UICalendarBox', 'UILayerList', 'cUser', 'UIToast'],
function (AbstractView, layoutHtml, listTpl, BusModel, BusStore, UICalendarBox, UILayerList, cUser, UIToast) {
    return _.inherit(AbstractView, {
        propertys: function ($super) {
            $super();
            //一堆基础属性定义
            //......
            //交互业务逻辑
            this.events = {
                'click .js_pre_day': 'preAction', //点击前一天触发
                'click .js_next_day': 'nextAction', //点击后一天触发
                'click .js_bus_list li': 'toBooking', //点击列表项目触发
                'click .js_show_calendar': 'showCalendar', //点击日期项出日历组件
                'click .js_show_setoutdate': 'showSetoutDate', //筛选出发时段
                'click .js_show_setstation': 'showStation', //筛选出发站
                'click .js_show_arrivalstation': 'showArrivalStation', //筛选到达站
                //迭代需求,增加其它频道入口
                'click .js-list-tip': function () {}
            };
        },
        //初始化头部标题栏
        initHeader: function (t) { },
        //首次dom渲染后,初始化后续会用到的所有dom元素,以免重复获取
        initElement: function () {},
        showSetoutDate: function () {},
        showStation: function () {},
        showArrivalStation: function () {},
        showCalendar: function () {},
        preAction: function (e) {},
        nextAction: function () {},
        toBooking: function (e) {},
        listInit: function () {},
        bindScrollEvent: function () {},
        unbindScrollEvent: function () { },
        addEvent: function () {
            this.on('onShow', function () {
                //当页面渲染结束,需要做的初始化操作,比如渲染页面
                this.listInit();
                //......
            });
            this.on('onHide', function () {
                this.unbindScrollEvent();
            });
        }
    });
});

依附从前的阅历,若是单纯包括这一个工作逻辑,那样写代码难题不是可怜大,代码量揣度在800行左右,不过为了成功总体的作业逻辑,大家那边立即发生了新的要求。

须求迭代

因为本身这里的车的班次列表,最早是尚未U奥迪Q5L参数,所以根本不能够产出车的班次列表,页面上全体组件模块都以摆放,于是这里新扩充贰个急需:

JavaScript

当url没有出发-达到相关参数音信时,私下认可弹出出发城市到达城市选拔框

1
当url没有出发-到达相关参数信息时,默认弹出出发城市到达城市选择框

于是,我们这里会骤增三个简单的弹出层:

图片 5

本条看似轻松的弹出层,背后却潜藏了贰个宏伟的圈套,因为点击出发恐怕达到时会出城市列表,而城市列表本身便是一个比较复杂的事体:

图片 6

于是乎页面包车型地铁整合产生了更动:

① 本人业务逻辑约800行代码

② 新扩展出发达到筛选弹出层

③ 出发城市页面,推断300行代码

而弹出层的增加生产数量对作业本身变成了深切的熏陶,本来url是不含有业务参数的,不过点击了弹出层的显著开关,要求转移U奥迪Q5L参数,而且刷新自个儿页面的数码,于是简单的贰个弹出层新增加间接将页面包车型客车复杂程度提高了一倍。

于是乎该页面代码轻轻易松破千了,后续供给迭代js代码量破3000不过是时间难点,到时候维护便复杂了,页面复杂无规律的DOM操作将会令你焦头烂额,这一年组件化开采的优势便足以显示了,于是上面步向组件化开拓的布置。

有备无患工作

完整框架结构

此次的代码正视于blade骨架,包罗:

① MVC模块,完毕经过url获取科学的page控制器,进而通过view.js达成渲染页面包车型客车机能

② 数据央浼模块,达成接口央求

全站重视于javascript的承接功效,详细情况见:【二回面试】再谈javascript中的承袭,如若不太理解面向对象编制程序,文中代码也许会有一些困难,也请各位多多通晓。

完全工作架构如图:

图片 7

框架架构图:

图片 8.

上面分别介绍下各类模块,扶助各位在下文中能越来越好的精晓代码,首先是基本MVC的牵线,这里请参考作者那篇文章:简单的MVC介绍

全局调整器

实际调控器可谓是变化万千的二个指标,对于服务器端来讲,调节器实现的效益是将本次恳求分发到具体的代码模块,由代码模块管理后重返字符串给前端;

对于乞求已经降临浏览器的前端来讲,依照此次乞求UCRUISERL(只怕其余衡量模范),决断该次乞求应该由哪位前端js调节器施行,那是前面三个调控器干的作业;

真正的本次拍卖逻辑踏入二个实际的page后,那几个page事实上也得以看成三个调控器存在……

笔者们这里的调节器,首要造成依据当下呼吁实例化View的机能,並且会提供部分view品级希望单例使用的接口:

JavaScript

define([ 'UIHeader', 'UIToast', 'UILoading', 'UIPageView', 'UIAlert' ], function (UIHeader, UIToast, UILoading, UIPageView, UIAlert) { return _.inherit({ propertys: function () { //view寻找目录 this.viewRootPath = 'views/'; //默许view this.defaultView = 'index'; //当前视图路线 this.viewId; this.viewUrl; //视图集 this.views = {}; //是或不是张开单页应用 // this.isOpenWebapp = _.getHybridInfo().platform == 'baidubox' ? true : false; this.isOpenWebapp = false; this.viewMapping = {}; //UIHeader须要释放出来 this.UIHeader = UIHeader; this.interface = [ 'forward', 'back', 'jump', 'showPageView', 'hidePageView', 'showLoading', 'hideLoading', 'showToast', 'hideToast', 'showMessage', 'hideMessage', 'showConfirm', 'hideConfirm', 'openWebapp', 'closeWebapp' ]; }, initialize: function (options) { this.propertys(); this.setOption(options); this.initViewPort(); this.initAppMapping(); //开启fastclick $.bindFastClick && $.bindFastClick(); }, setOption: function (options) { _.extend(this, options); }, //创建dom结构 initViewPort: function () { this.d_header = $('#headerview'); this.d_state = $('#js_page_state'); this.d_viewport = $('#main'); //实例化全局使用的header,这里就好像有个别语无伦次 this.header = new this.UIHeader({ wrapper: this.d_header }); //非分享能源,这里应该引进app概念了 this.pageviews = {}; this.toast = new UIToast(); this.loading = new UILoading(); this.alert = new UIAlert(); this.confirm = new UIAlert(); }, openWebapp: function () { this.isOpenWebapp = true; }, closeWebapp: function () { this.isOpenWebapp = false; }, showPageView: function (name, _viewdata_, id) { var view = null, k, scope = this.curViewIns || this; if (!id) id = name; if (!_.isString(name)) return; // for (k in _viewdata_) { // if (_.isFunction(_viewdata_[k])) _viewdata_[k] = $.proxy(_viewdata_[k], scope); // } view = this.pageviews[id]; var arr = name.split('/'); var getViewPath = window.getViewPath || window.GetViewPath; if (!view) { view = new UIPageView({ // bug fixed by zzx viewId: arr[arr.length - 1] || name, viewPath: getViewPath ? getViewPath(name) : name, _viewdata_: _viewdata_, onHide: function () { scope.initHeader(); } }); this.pageviews[id] = view; } else { view.setViewData(_viewdata_); } view.show(); }, hidePageView: function (name) { if (name) { if (this.pageviews[name]) this.pageviews[name].hide(); } else { for (var k in this.pageviews) this.pageviews[k].hide(); } }, showLoading: function () { this.loading.show(); }, hideLoading: function () { this.loading.hide(); }, showToast: function (msg, callback) { this.toast.resetDefaultProperty(); this.toast.content = msg; if (callback) this.toast.hideAction = callback; this.toast.refresh(); this.toast.show(); }, hideToast: function () { this.toast.hide(); }, showMessage: function (param) { if (_.isString(param)) { param = { content: param }; } this.alert.resetDefaultProperty(); this.alert.setOption(param); this.alert.refresh(); this.alert.show(); }, hideMessage: function () { this.alert.hide(); }, showConfirm: function (params) { if (!params) params = {}; if (typeof params == 'string') { params = { content: params }; } this.confirm.resetDefaultProperty(); //与showMessage差别的地点 this.confirm.btns = [ { name: '取消', className: 'cm-btns-cancel js_cancel' }, { name: '确定', className: 'cm-btns-ok js_ok' } ]; this.confirm.setOption(params); this.confirm.refresh(); this.confirm.show(); }, hideConfirm: function () { this.confirm.hide(); }, //起初化app initApp: function () { //第一次加载不要求行动由决定 this.loadViewByUrl(); //前边的加载全部要透过路由拍卖 if (this.isOpenWebapp === true) $(window).on('popstate.app', $.proxy(this.loadViewByUrl, this)); }, loadViewByUrl: function (e) { this.hidePageView(); var url = decodeUKoleosIComponent(location.href).toLowerCase(); var viewId = this.getViewIdRule(url); viewId = viewId || this.defaultView; this.viewId = viewId; this.viewUrl = url; this.switchView(this.viewId); }, //@override getViewIdRule: function (url) { var viewId = '', hash = ''; var reg = /webapp/. /(. ).html/; var match = url.match(reg); if (match && match[1]) viewId = match[1]; return viewId; }, //@override setUrlRule: function (viewId, param, replace, project) { var reg = /(webapp/. /)(. ).html/; var url = window.location.href; var match = url.match(reg); var proj = project ? 'webapp/' project : match[1]; var preUrl = '', str = '', i = 0, _k, _v; //这里那样做稍微过分业务了 *bug* var keepParam = [ 'us' ], p; if (!viewId) return; if (!match || !match[1]) { preUrl = url '/webapp/bus/' viewId '.html'; } else { preUrl = url.substr(0, url.indexOf(match[1])) proj viewId '.html'; ; } //特定的参数将会平昔带上去,门路、来源等标识 for (i = 0; i < keepParam.length; i ) { p = keepParam[i]; if (_.getUrlParam()[p]) { if (!param) param = {}; param[p] = _.getUrlParam()[p]; } } i = 0; for (k in param) { _k = encodeURIComponent(_.removeAllSpace(k)); _v = encodeURIComponent(_.removeAllSpace(param[k])); if (i === 0) { str = '?' _k '=' _v; i ; } else { str = '&' _k '=' _v; } } url = preUrl str; if (this.isOpenWebapp === false) { window.location = url; return; } if (replace) { history.replaceState('', {}, url); } else { history.pushState('', {}, url); } }, switchView: function (id) { var curView = this.views[id]; //切换前的眼下view,立时会遮蔽 var tmpView = this.curView; if (tmpView && tmpView != curView) { this.lastView = tmpView; } //加载view样式,权宜之计 // this.loadViewStyle(id); //借使当前view存在,则实施请onload事件 if (curView) { //假若当前要跳转的view正是当前view的话便不予处理//这里具体管理逻辑要改************************************* if (curView == this.curView) { return; } this.curView = curView; this.curView.show(); this.lastView && this.lastView.hide(); } else { // this.showLoading(); this.loadView(id, function (View) { //每一次加载截至将状态栏遮掩,这些代码要改 // this.hideLoading(); this.curView = new View({ viewId: id, refer: this.lastView ? this.lastView.viewId : null, 应用软件: this, wrapper: this.d_viewport }); //设置网页上的view标识 this.curView.$el.attr('page-url', id); //保存至队列 this.views[id] = this.curView; this.curView.show(); this.lastView && this.lastView.hide(); }); } }, //加载view loadView: function (path, callback) { var self = this; requirejs([this.buildUrl(path)], function (View) { callback && callback.call(self, View); }); }, //override //配置只怕会有些路径扩充,为Hybrid与各样渠道做适配 initAppMapping: function () { // console.log('该方法必需被重写'); }, //@override buildUrl: function (path) { var mappingPath = this.viewMapping[path]; return mappingPath ? mappingPath : this.viewRootPath '/' path '/'

  • path; }, //此处须要三个翻新逻辑,比方在index view再点击到index view不会有感应,下一次改************************** forward: function (viewId, param, replace) { if (!viewId) return; viewId = viewId.toLowerCase(); this.setUrlRule(viewId, param, replace); this.loadViewByUrl(); }, jump: function (path, param, replace) { var viewId; var project; if (!path) { return; } path = path.toLowerCase().split('/'); if (path.length <= 0) { return; } viewId = path.pop(); project = path.length === 1 ? path.join('') '/' : path.join(''); this.setUrlRule(viewId, param, replace, project); this.loadViewByUrl(); }, back: function (viewId, param, replace) { if (viewId) { this.forward(viewId, param, replace) } else { if (window.history.length == 1) { this.forward(this.defaultView, param, replace) } else { history.back(); } } } }); }); abstract.app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
define([
  'UIHeader',
  'UIToast',
  'UILoading',
  'UIPageView',
  'UIAlert'
], function (UIHeader, UIToast, UILoading, UIPageView, UIAlert) {
 
    return _.inherit({
        propertys: function () {
            //view搜索目录
            this.viewRootPath = 'views/';
 
            //默认view
            this.defaultView = 'index';
 
            //当前视图路径
            this.viewId;
            this.viewUrl;
 
            //视图集
            this.views = {};
 
            //是否开启单页应用
            //      this.isOpenWebapp = _.getHybridInfo().platform == 'baidubox' ? true : false;
            this.isOpenWebapp = false;
 
            this.viewMapping = {};
 
            //UIHeader需要释放出来
            this.UIHeader = UIHeader;
 
            this.interface = [
                'forward',
                'back',
                'jump',
                'showPageView',
                'hidePageView',
                'showLoading',
                'hideLoading',
                'showToast',
                'hideToast',
                'showMessage',
                'hideMessage',
                'showConfirm',
                'hideConfirm',
                'openWebapp',
                'closeWebapp'
            ];
 
        },
 
        initialize: function (options) {
            this.propertys();
            this.setOption(options);
            this.initViewPort();
            this.initAppMapping();
 
            //开启fastclick
            $.bindFastClick && $.bindFastClick();
 
        },
 
        setOption: function (options) {
            _.extend(this, options);
        },
 
        //创建dom结构
        initViewPort: function () {
 
            this.d_header = $('#headerview');
            this.d_state = $('#js_page_state');
            this.d_viewport = $('#main');
 
            //实例化全局使用的header,这里好像有点不对
            this.header = new this.UIHeader({
                wrapper: this.d_header
            });
 
            //非共享资源,这里应该引入app概念了
            this.pageviews = {};
            this.toast = new UIToast();
            this.loading = new UILoading();
            this.alert = new UIAlert();
            this.confirm = new UIAlert();
        },
 
        openWebapp: function () {
            this.isOpenWebapp = true;
        },
 
        closeWebapp: function () {
            this.isOpenWebapp = false;
        },
 
        showPageView: function (name, _viewdata_, id) {
            var view = null, k, scope = this.curViewIns || this;
            if (!id) id = name;
            if (!_.isString(name)) return;
            //    for (k in _viewdata_) {
            //      if (_.isFunction(_viewdata_[k])) _viewdata_[k] = $.proxy(_viewdata_[k], scope);
            //    }
            view = this.pageviews[id];
            var arr = name.split('/');
            var getViewPath = window.getViewPath || window.GetViewPath;
            if (!view) {
                view = new UIPageView({
                    // bug fixed by zzx
                    viewId: arr[arr.length - 1] || name,
                    viewPath: getViewPath ? getViewPath(name) : name,
                    _viewdata_: _viewdata_,
                    onHide: function () {
                        scope.initHeader();
                    }
                });
                this.pageviews[id] = view;
            } else {
                view.setViewData(_viewdata_);
            }
            view.show();
 
        },
 
        hidePageView: function (name) {
            if (name) {
                if (this.pageviews[name]) this.pageviews[name].hide();
            } else {
                for (var k in this.pageviews) this.pageviews[k].hide();
            }
        },
 
        showLoading: function () {
            this.loading.show();
        },
 
        hideLoading: function () {
            this.loading.hide();
        },
 
        showToast: function (msg, callback) {
            this.toast.resetDefaultProperty();
            this.toast.content = msg;
            if (callback) this.toast.hideAction = callback;
            this.toast.refresh();
            this.toast.show();
        },
 
        hideToast: function () {
            this.toast.hide();
        },
 
        showMessage: function (param) {
            if (_.isString(param)) {
                param = { content: param };
            }
 
            this.alert.resetDefaultProperty();
            this.alert.setOption(param);
            this.alert.refresh();
            this.alert.show();
        },
 
        hideMessage: function () {
            this.alert.hide();
        },
 
        showConfirm: function (params) {
            if (!params) params = {};
            if (typeof params == 'string') {
                params = {
                    content: params
                };
            }
 
            this.confirm.resetDefaultProperty();
 
            //与showMessage不一样的地方
            this.confirm.btns = [
              { name: '取消', className: 'cm-btns-cancel js_cancel' },
              { name: '确定', className: 'cm-btns-ok js_ok' }
            ];
            this.confirm.setOption(params);
            this.confirm.refresh();
            this.confirm.show();
        },
 
        hideConfirm: function () {
            this.confirm.hide();
        },
 
        //初始化app
        initApp: function () {
 
            //首次加载不需要走路由控制
            this.loadViewByUrl();
 
            //后面的加载全部要经过路由处理
            if (this.isOpenWebapp === true)
                $(window).on('popstate.app', $.proxy(this.loadViewByUrl, this));
 
        },
 
        loadViewByUrl: function (e) {
            this.hidePageView();
 
            var url = decodeURIComponent(location.href).toLowerCase();
            var viewId = this.getViewIdRule(url);
 
            viewId = viewId || this.defaultView;
            this.viewId = viewId;
            this.viewUrl = url;
            this.switchView(this.viewId);
 
        },
 
        //@override
        getViewIdRule: function (url) {
            var viewId = '', hash = '';
            var reg = /webapp/. /(. ).html/;
 
            var match = url.match(reg);
            if (match && match[1]) viewId = match[1];
 
            return viewId;
        },
 
        //@override
        setUrlRule: function (viewId, param, replace, project) {
            var reg = /(webapp/. /)(. ).html/;
            var url = window.location.href;
            var match = url.match(reg);
            var proj = project ? 'webapp/' project : match[1];
            var preUrl = '', str = '', i = 0, _k, _v;
            //这里这样做有点过于业务了 *bug*
            var keepParam = [
              'us'
            ], p;
            if (!viewId) return;
            if (!match || !match[1]) {
                preUrl = url '/webapp/bus/' viewId '.html';
            } else {
                preUrl = url.substr(0, url.indexOf(match[1])) proj viewId '.html'; ;
            }
 
            //特定的参数将会一直带上去,渠道、来源等标志
            for (i = 0; i < keepParam.length; i ) {
                p = keepParam[i];
                if (_.getUrlParam()[p]) {
                    if (!param) param = {};
                    param[p] = _.getUrlParam()[p];
                }
            }
 
            i = 0;
 
            for (k in param) {
                _k = encodeURIComponent(_.removeAllSpace(k));
                _v = encodeURIComponent(_.removeAllSpace(param[k]));
                if (i === 0) {
                    str = '?' _k '=' _v;
                    i ;
                } else {
                    str = '&' _k '=' _v;
                }
            }
 
            url = preUrl str;
 
            if (this.isOpenWebapp === false) {
                window.location = url;
                return;
            }
 
            if (replace) {
                history.replaceState('', {}, url);
            } else {
                history.pushState('', {}, url);
            }
 
        },
 
        switchView: function (id) {
 
            var curView = this.views[id];
 
            //切换前的当前view,马上会隐藏
            var tmpView = this.curView;
 
            if (tmpView && tmpView != curView) {
                this.lastView = tmpView;
            }
 
            //加载view样式,权宜之计
            //      this.loadViewStyle(id);
 
            //如果当前view存在,则执行请onload事件
            if (curView) {
 
                //如果当前要跳转的view就是当前view的话便不予处理
                //这里具体处理逻辑要改*************************************
                if (curView == this.curView) {
                    return;
                }
 
                this.curView = curView;
                this.curView.show();
                this.lastView && this.lastView.hide();
            } else {
 
                //        this.showLoading();
                this.loadView(id, function (View) {
                    //每次加载结束将状态栏隐藏,这个代码要改
                    //          this.hideLoading();
 
                    this.curView = new View({
                        viewId: id,
                        refer: this.lastView ? this.lastView.viewId : null,
                        APP: this,
                        wrapper: this.d_viewport
                    });
 
                    //设置网页上的view标志
                    this.curView.$el.attr('page-url', id);
 
                    //保存至队列
                    this.views[id] = this.curView;
 
                    this.curView.show();
                    this.lastView && this.lastView.hide();
 
                });
            }
        },
 
        //加载view
        loadView: function (path, callback) {
            var self = this;
            requirejs([this.buildUrl(path)], function (View) {
                callback && callback.call(self, View);
            });
        },
 
        //override
        //配置可能会有的路径扩展,为Hybrid与各个渠道做适配
        initAppMapping: function () {
            //            console.log('该方法必须被重写');
        },
 
        //@override
        buildUrl: function (path) {
            var mappingPath = this.viewMapping[path];
            return mappingPath ? mappingPath : this.viewRootPath '/' path '/' path;
        },
 
        //此处需要一个更新逻辑,比如在index view再点击到index view不会有反应,下次改**************************
        forward: function (viewId, param, replace) {
            if (!viewId) return;
            viewId = viewId.toLowerCase();
 
            this.setUrlRule(viewId, param, replace);
            this.loadViewByUrl();
        },
        jump: function (path, param, replace) {
            var viewId;
            var project;
            if (!path) {
                return;
            }
            path = path.toLowerCase().split('/');
            if (path.length <= 0) {
                return;
            }
            viewId = path.pop();
            project = path.length === 1 ? path.join('') '/' : path.join('');
            this.setUrlRule(viewId, param, replace, project);
            this.loadViewByUrl();
        },
        back: function (viewId, param, replace) {
            if (viewId) {
                this.forward(viewId, param, replace)
            } else {
                if (window.history.length == 1) {
                    this.forward(this.defaultView, param, replace)
                } else {
                    history.back();
                }
            }
        }
 
    });
 
});
 
abstract.app

此地属于框架调节器层面包车型大巴代码,与前几天的主旨不是可怜相关,风野趣的爱侣能够详细读读。

页面基类

此处的中坚是页面品级的管理,这里会做相当多的介绍,首先大家为持有的业务级View提供了二个后续的View:

JavaScript

define([], function () { 'use strict'; return _.inherit({ _propertys: function () { this.APP = this.APP || window.APP; var i = 0, len = 0, k; if (this.APP && this.APP.interface) { for (i = 0, len = this.APP.interface.length; i < len; i ) { k = this.APP.interface[i]; if (k == 'showPageView') continue; if (_.isFunction(this.APP[k])) { this[k] = $.proxy(this.APP[k], this.APP); } else this[k] = this.APP[k]; } } this.header = this.APP.header; }, showPageView: function (name, _viewdata, id) { this.APP.curViewIns = this; this.APP.showPageView(name, _viewdata, id) }, propertys: function () { //这里设置UI的根节点所处包裹层 this.wrapper = $('#main'); this.id = _.uniqueId('page-view-'); this.classname = ''; this.viewId = null; this.refer = null; //模板字符串,种种零部件分化,未来加盟预编写翻译机制 this.template = ''; //事件机制 this.events = {}; //自定义事件 //此处要求注意mask 绑定事件前后难题,思念scroll.radio插件类型的mask应用,考虑组件通讯this.eventArr = {}; //初阶状态为实例化 this.status = 'init'; this._propertys(); }, getViewModel: function () { //若是有datamodel的话,便直接再次来到,不然便重写,这里基本为了同盟 if (_.isObject(this.datamodel)) return this.datamodel; return {}; }, //子类事件绑定若想保留父级的,应该运用该方法 addEvents: function (events) { if (_.isObject(events)) _.extend(this.events, events); }, on: function (type, fn, insert) { if (!this.eventArr[type]) this.eventArr[type] = []; //底部插入 if (insert) { this.eventArr[type].splice(0, 0, fn); } else { this.eventArr[type].push(fn); } }, off: function (type, fn) { if (!this.eventArr[type]) return; if (fn) { this.eventArr[type] = _.without(this.eventArr[type], fn); } else { this.eventArr[type] = []; } }, trigger: function (type) { var _slice = Array.prototype.slice; var args = _slice.call(arguments, 1); var events = this.eventArr; var results = [], i, l; if (events[type]) { for (i = 0, l = events[type].length; i < l; i ) { results[results.length] = events[type][i].apply(this, args); } } return results; }, createRoot: function (html) { //假设存在style节点,况且style节点不设不常须要处理 if (this.style && !$('#page_' this.viewId)[0]) { $('head').append($('<style id="page_' this.viewId '" class="page-style">' this.style '</style>')) } //若是具备fake节点,须求移除 $('#fake-page').remove(); //UI的根节点 this.$el = $('<div class="cm-view page-' this.viewId ' ' this.classname '" style="display: none; " id="' this.id '">' html '</div>'); if (this.wrapper.find('.cm-view')[0]) { this.wrapper.append(this.$el); } else { this.wrapper.html('').append(this.$el); } }, _isAdd伊芙nt: function (key) { if (key == 'onCreate' || key == 'onPreShow' || key == 'onShow' || key == 'onRefresh' || key == 'onHide') return true; return false; }, setOption: function (options) { //这里能够写成switch,起先未有想到有如此多分支 for (var k in options) { if (k == 'events') { _.extend(this[k], options[k]); continue; } else if (this._isAddEvent(k)) { this.on(k, options[k]) continue; } this[k] = options[k]; } // _.extend(this, options); }, initialize: function (opts) { //这种暗许属性 this.propertys(); //依照参数复位属性 this.setOption(opts); //检查实验不客观属性,订正为科学数据 this.resetPropery(); this.add伊芙nt(); this.create(); this.initElement(); window.sss = this; }, $: function (selector) { return this.$el.find(selector); }, //提供属性重新初始化效率,对品质做检讨 resetPropery: function () { }, //各事件注册点,用于被三番四回override add伊夫nt: function () { }, create: function () { this.trigger('onPreCreate'); //若无传来模板,表达html结构早就存在 this.createRoot(this.render()); this.status = 'create'; this.trigger('onCreate'); }, //实例化须求用到到dom成分 initElement: function () { }, render: function (callback) { var data = this.getViewModel() || {}; var html = this.template; if (!this.template) return ''; //引进预编译机制 if (_.isFunction(this.template)) { html = this.template(data); } else { html = _.template(this.template)(data); } typeof callback == 'function' && callback.call(this); return html; }, refresh: function (needRecreate) { this.resetPropery(); if (needRecreate) { this.create(); } else { this.$el.html(this.render()); } this.initElement(); if (this.status != 'hide') this.show(); this.trigger('onRefresh'); }, /** * @description 组件展现情势,第三遍显示会将ui对象实际由内部存款和储蓄器插入包裹层 * @method initialize * @param {Object} opts */ show: function () { this.trigger('onPreShow'); // //假如包括就不用乱搞了 // if (!$.contains(this.wrapper[0], this.$el[0])) { // //即使须要清空容器的话便清空 // if (this.needEmptyWrapper) this.wrapper.html(''); // this.wrapper.append(this.$el); // } this.$el.show(); this.status = 'show'; this.bind伊夫nts(); this.initHeader(); this.trigger('onShow'); }, initHeader: function () { }, hide: function () { if (!this.$el || this.status !== 'show') return; this.trigger('onPreHide'); this.$el.hide(); this.status = 'hide'; this.unBindEvents(); this.trigger('onHide'); }, destroy: function () { this.status = 'destroy'; this.unBindEvents(); this.$root.remove(); this.trigger('onDestroy'); delete this; }, bindEvents: function () { var events = this.events; if (!(events || (events = _.result(this, 'events')))) return this; this.unBind伊芙nts(); // 深入分析event参数的正则 var delegateEventSplitter = /^(S )s*(.*)$/; var key, method, match, eventName, selector; // 做轻巧的字符串数据分析 for (key in events) { method = events[key]; if (!_.isFunction(method)) method = this[events[key]]; if (!method) continue; match = key.match(delegateEventSplitter); eventName = match[1], selector = match[2]; method = _.bind(method, this); eventName = '.delegateUIEvents' this.id; if (selector === '') { this.$el.on(eventName, method); } else { this.$el.on(eventName, selector, method); } } return this; }, unBindEvents: function () { this.$el.off('.delegateUIEvents' this.id); return this; }, getParam: function (key) { return _.getUrlParam(window.location.href, key) }, renderTpl: function (tpl, data) { if (!_.isFunction(tpl)) tpl = _.template(tpl); return tpl(data); } }); }); abstract.view

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
define([], function () {
    'use strict';
 
    return _.inherit({
 
        _propertys: function () {
            this.APP = this.APP || window.APP;
            var i = 0, len = 0, k;
            if (this.APP && this.APP.interface) {
                for (i = 0, len = this.APP.interface.length; i < len; i ) {
                    k = this.APP.interface[i];
                    if (k == 'showPageView') continue;
 
                    if (_.isFunction(this.APP[k])) {
                        this[k] = $.proxy(this.APP[k], this.APP);
                    }
                    else this[k] = this.APP[k];
                }
            }
 
            this.header = this.APP.header;
        },
 
        showPageView: function (name, _viewdata, id) {
            this.APP.curViewIns = this;
            this.APP.showPageView(name, _viewdata, id)
        },
        propertys: function () {
            //这里设置UI的根节点所处包裹层
            this.wrapper = $('#main');
            this.id = _.uniqueId('page-view-');
            this.classname = '';
 
            this.viewId = null;
            this.refer = null;
 
            //模板字符串,各个组件不同,现在加入预编译机制
            this.template = '';
            //事件机制
            this.events = {};
 
            //自定义事件
            //此处需要注意mask 绑定事件前后问题,考虑scroll.radio插件类型的mask应用,考虑组件通信
            this.eventArr = {};
 
            //初始状态为实例化
            this.status = 'init';
 
            this._propertys();
        },
 
        getViewModel: function () {
            //假如有datamodel的话,便直接返回,不然便重写,这里基本为了兼容
            if (_.isObject(this.datamodel)) return this.datamodel;
            return {};
        },
 
        //子类事件绑定若想保留父级的,应该使用该方法
        addEvents: function (events) {
            if (_.isObject(events)) _.extend(this.events, events);
        },
 
        on: function (type, fn, insert) {
            if (!this.eventArr[type]) this.eventArr[type] = [];
 
            //头部插入
            if (insert) {
                this.eventArr[type].splice(0, 0, fn);
            } else {
                this.eventArr[type].push(fn);
            }
        },
 
        off: function (type, fn) {
            if (!this.eventArr[type]) return;
            if (fn) {
                this.eventArr[type] = _.without(this.eventArr[type], fn);
            } else {
                this.eventArr[type] = [];
            }
        },
 
        trigger: function (type) {
            var _slice = Array.prototype.slice;
            var args = _slice.call(arguments, 1);
            var events = this.eventArr;
            var results = [], i, l;
 
            if (events[type]) {
                for (i = 0, l = events[type].length; i < l; i ) {
                    results[results.length] = events[type][i].apply(this, args);
                }
            }
            return results;
        },
 
        createRoot: function (html) {
 
            //如果存在style节点,并且style节点不存在的时候需要处理
            if (this.style && !$('#page_' this.viewId)[0]) {
                $('head').append($('<style id="page_' this.viewId '" class="page-style">' this.style '</style>'))
            }
 
            //如果具有fake节点,需要移除
            $('#fake-page').remove();
 
            //UI的根节点
            this.$el = $('<div class="cm-view page-' this.viewId ' ' this.classname '" style="display: none; " id="' this.id '">' html '</div>');
            if (this.wrapper.find('.cm-view')[0]) {
                this.wrapper.append(this.$el);
            } else {
                this.wrapper.html('').append(this.$el);
            }
 
        },
 
        _isAddEvent: function (key) {
            if (key == 'onCreate' || key == 'onPreShow' || key == 'onShow' || key == 'onRefresh' || key == 'onHide')
                return true;
            return false;
        },
 
        setOption: function (options) {
            //这里可以写成switch,开始没有想到有这么多分支
            for (var k in options) {
                if (k == 'events') {
                    _.extend(this[k], options[k]);
                    continue;
                } else if (this._isAddEvent(k)) {
                    this.on(k, options[k])
                    continue;
                }
                this[k] = options[k];
            }
            //      _.extend(this, options);
        },
 
        initialize: function (opts) {
            //这种默认属性
            this.propertys();
            //根据参数重置属性
            this.setOption(opts);
            //检测不合理属性,修正为正确数据
            this.resetPropery();
 
            this.addEvent();
            this.create();
 
            this.initElement();
 
            window.sss = this;
 
        },
 
        $: function (selector) {
            return this.$el.find(selector);
        },
 
        //提供属性重置功能,对属性做检查
        resetPropery: function () { },
 
        //各事件注册点,用于被继承override
        addEvent: function () {
        },
 
        create: function () {
            this.trigger('onPreCreate');
            //如果没有传入模板,说明html结构已经存在
            this.createRoot(this.render());
 
            this.status = 'create';
            this.trigger('onCreate');
        },
 
        //实例化需要用到到dom元素
        initElement: function () { },
 
        render: function (callback) {
            var data = this.getViewModel() || {};
            var html = this.template;
            if (!this.template) return '';
            //引入预编译机制
            if (_.isFunction(this.template)) {
                html = this.template(data);
            } else {
                html = _.template(this.template)(data);
            }
            typeof callback == 'function' && callback.call(this);
            return html;
        },
 
        refresh: function (needRecreate) {
            this.resetPropery();
            if (needRecreate) {
                this.create();
            } else {
                this.$el.html(this.render());
            }
            this.initElement();
            if (this.status != 'hide') this.show();
            this.trigger('onRefresh');
        },
 
        /**
        * @description 组件显示方法,首次显示会将ui对象实际由内存插入包裹层
        * @method initialize
        * @param {Object} opts
        */
        show: function () {
            this.trigger('onPreShow');
            //      //如果包含就不要乱搞了
            //      if (!$.contains(this.wrapper[0], this.$el[0])) {
            //        //如果需要清空容器的话便清空
            //        if (this.needEmptyWrapper) this.wrapper.html('');
            //        this.wrapper.append(this.$el);
            //      }
 
            this.$el.show();
            this.status = 'show';
 
            this.bindEvents();
 
            this.initHeader();
            this.trigger('onShow');
        },
 
        initHeader: function () { },
 
        hide: function () {
            if (!this.$el || this.status !== 'show') return;
 
            this.trigger('onPreHide');
            this.$el.hide();
 
            this.status = 'hide';
            this.unBindEvents();
            this.trigger('onHide');
        },
 
        destroy: function () {
            this.status = 'destroy';
            this.unBindEvents();
            this.$root.remove();
            this.trigger('onDestroy');
            delete this;
        },
 
        bindEvents: function () {
            var events = this.events;
 
            if (!(events || (events = _.result(this, 'events')))) return this;
            this.unBindEvents();
 
            // 解析event参数的正则
            var delegateEventSplitter = /^(S )s*(.*)$/;
            var key, method, match, eventName, selector;
 
            // 做简单的字符串数据解析
            for (key in events) {
                method = events[key];
                if (!_.isFunction(method)) method = this[events[key]];
                if (!method) continue;
 
                match = key.match(delegateEventSplitter);
                eventName = match[1], selector = match[2];
                method = _.bind(method, this);
                eventName = '.delegateUIEvents' this.id;
 
                if (selector === '') {
                    this.$el.on(eventName, method);
                } else {
                    this.$el.on(eventName, selector, method);
                }
            }
 
            return this;
        },
 
        unBindEvents: function () {
            this.$el.off('.delegateUIEvents' this.id);
            return this;
        },
 
        getParam: function (key) {
            return _.getUrlParam(window.location.href, key)
        },
 
        renderTpl: function (tpl, data) {
            if (!_.isFunction(tpl)) tpl = _.template(tpl);
            return tpl(data);
        }
 
    });
 
});
 
abstract.view

叁个Page级其余View会有以下多少个主要属性&方法:

① template,html字符串,不满含呼吁的根基模块,会结合页面的html骨架层

② events,全数的DOM事件定义处,以事件代理的办法定义,所以不用忧虑施行各类

③ add伊夫nt,用于页面等第各种阶段的监督检查事件注册点,常常的话顾客只需求关切少之甚少多少个事件,比如:

JavaScript

//写法 addEvent: function () { //页面渲染结束,并体现时候接触的风云this.on('onShow', function () { }); //离开页面,页面遮盖时候接触的事件 this.on('onHide', function () { }); }

1
2
3
4
5
6
7
8
9
//写法
addEvent: function () {
   //页面渲染结束,并显示时候触发的事件
    this.on('onShow', function () {
    });
    //离开页面,页面隐藏时候触发的事件
    this.on('onHide', function () {
    });
}

一个页面包车型客车中央写法:

JavaScript

define(['AbstractView'], function (AbstractView) { return _.inherit(AbstractView, { propertys: function ($super) { $super(); //一群基础属性定义 //...... //交互业务逻辑 this.events = { 'click .js_pre_day': 'preAction' }; }, preAction: function (e) { }, addEvent: function () { this.on('onShow', function () { //当页面渲染截止,必要做的带头化操作,比方渲染页面 //...... }); this.on('onHide', function () { }); } }); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
define(['AbstractView'], function (AbstractView) {
    return _.inherit(AbstractView, {
        propertys: function ($super) {
            $super();
            //一堆基础属性定义
            //......
            //交互业务逻辑
            this.events = {
                'click .js_pre_day': 'preAction'
            };
        },
        preAction: function (e) { },
        addEvent: function () {
            this.on('onShow', function () {
                //当页面渲染结束,需要做的初始化操作,比如渲染页面
                //......
            });
            this.on('onHide', function () {
            });
        }
    });
});

倘诺依照这种准则写,便能呈现页面,况且有着DOM交互事件。

页面模块类

所谓页面模块类,就是用来拆分多个页面为单个组件模块所用类,这里有那几个约定:

JavaScript

① 三个模块类实例一定会依赖一个Page的基类实例 ② 模块类实例通过this.view能够访问到信任类的满贯能源 ③ 模块类实例与模块之间通过数量entity做通讯

1
2
3
① 一个模块类实例一定会依赖一个Page的基类实例
② 模块类实例通过this.view可以访问到依赖类的一切资源
③ 模块类实例与模块之间通过数据entity做通信

此间代码能够再优化,但不是我们那边境海关心的首要性:

JavaScript

define([], function () { 'use strict'; return _.inherit({ propertys: function () { //这里设置UI的根节点所处包裹层,必得设置 this.$el = null; //用于固定dom的选项器 this.selector = ''; //各类moduleView必得有一个父view,页面级容器 this.view = null; //模板字符串,各样零部件不一致,未来参与预编写翻译机制 this.template = ''; //事件机制 this.events = {}; //实体model,跨模块通信的桥梁 this.entity = null; }, setOption: function (options) { //这里能够写成switch,先导未有想到有这般多分支 for (var k in options) { if (k == 'events') { _.extend(this[k], options[k]); continue; } this[k] = options[k]; } // _.extend(this, options); }, //@override initData: function () { }, //假若传入了dom便 initWrapper: function (el) { if (el && el[0]) { this.$el = el; return; } this.$el = this.view.$(this.selector); }, initialize: function (opts) { //这种私下认可属性 this.propertys(); //遵照参数重新载入参数属性 this.setOption(opts); this.initData(); this.initWithoutRender(); }, //管理dom已经存在,没有须求渲染的气象 initWithoutRender: function () { if (this.template) return; var scope = this; this.view.on('onShow', function () { scope.initWrapper(); if (!scope.$el[0]) return; //若无父view则不可能延续 if (!scope.view) return; scope.initElement(); scope.bindEvents(); }); }, $: function (selector) { return this.$el.find(selector); }, //实例化供给用到到dom成分 initElement: function () { }, //@override //采摘来自各个地区的实业组成view渲染供给的多寡,供给重写 getViewModel: function () { throw '必需重写'; }, _render: function (callback) { var data = this.getViewModel() || {}; var html = this.template; if (!this.template) return ''; //引入预编写翻译机制 if (_.isFunction(this.template)) { html = this.template(data); } else { html = _.template(this.template)(data); } typeof callback == 'function' && callback.call(this); return html; }, //渲染时必需传入dom映射 render: function () { this.initWrapper(); if (!this.$el[0]) return; //若无父view则不能继续 if (!this.view) return; var html = this._render(); this.$el.html(html); this.initElement(); this.bindEvents(); }, bindEvents: function () { var events = this.events; if (!(events || (events = _.result(this, 'events')))) return this; this.unBind伊夫nts(); // 深入分析event参数的正则 var delegateEventSplitter = /^(S )s*(.*)$/; var key, method, match, eventName, selector; // 做轻巧的字符串数据深入分析 for (key in events) { method = events[key]; if (!_.isFunction(method)) method = this[events[key]]; if (!method) continue; match = key.match(delegateEventSplitter); eventName = match[1], selector = match[2]; method = _.bind(method, this); eventName = '.delegateUIEvents' this.id; if (selector === '') { this.$el.on(eventName, method); } else { this.$el.on(eventName, selector, method); } } return this; }, unBindEvents: function () { this.$el.off('.delegateUIEvents' this.id); return this; } }); }); module.view

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
define([], function () {
    'use strict';
 
    return _.inherit({
 
        propertys: function () {
            //这里设置UI的根节点所处包裹层,必须设置
            this.$el = null;
 
            //用于定位dom的选择器
            this.selector = '';
 
            //每个moduleView必须有一个父view,页面级容器
            this.view = null;
 
            //模板字符串,各个组件不同,现在加入预编译机制
            this.template = '';
 
            //事件机制
            this.events = {};
 
            //实体model,跨模块通信的桥梁
            this.entity = null;
        },
 
        setOption: function (options) {
            //这里可以写成switch,开始没有想到有这么多分支
            for (var k in options) {
                if (k == 'events') {
                    _.extend(this[k], options[k]);
                    continue;
                }
                this[k] = options[k];
            }
            //      _.extend(this, options);
        },
 
        //@override
        initData: function () {
        },
 
        //如果传入了dom便
        initWrapper: function (el) {
            if (el && el[0]) {
                this.$el = el;
                return;
            }
            this.$el = this.view.$(this.selector);
        },
 
        initialize: function (opts) {
 
            //这种默认属性
            this.propertys();
            //根据参数重置属性
            this.setOption(opts);
            this.initData();
 
            this.initWithoutRender();
 
        },
 
        //处理dom已经存在,不需要渲染的情况
        initWithoutRender: function () {
            if (this.template) return;
            var scope = this;
            this.view.on('onShow', function () {
                scope.initWrapper();
                if (!scope.$el[0]) return;
                //如果没有父view则不能继续
                if (!scope.view) return;
                scope.initElement();
                scope.bindEvents();
            });
        },
 
        $: function (selector) {
            return this.$el.find(selector);
        },
 
        //实例化需要用到到dom元素
        initElement: function () { },
 
        //@override
        //收集来自各方的实体组成view渲染需要的数据,需要重写
        getViewModel: function () {
            throw '必须重写';
        },
 
        _render: function (callback) {
            var data = this.getViewModel() || {};
            var html = this.template;
            if (!this.template) return '';
            //引入预编译机制
            if (_.isFunction(this.template)) {
                html = this.template(data);
            } else {
                html = _.template(this.template)(data);
            }
            typeof callback == 'function' && callback.call(this);
            return html;
        },
 
        //渲染时必须传入dom映射
        render: function () {
            this.initWrapper();
            if (!this.$el[0]) return;
 
            //如果没有父view则不能继续
            if (!this.view) return;
 
            var html = this._render();
            this.$el.html(html);
            this.initElement();
            this.bindEvents();
 
        },
 
        bindEvents: function () {
            var events = this.events;
 
            if (!(events || (events = _.result(this, 'events')))) return this;
            this.unBindEvents();
 
            // 解析event参数的正则
            var delegateEventSplitter = /^(S )s*(.*)$/;
            var key, method, match, eventName, selector;
 
            // 做简单的字符串数据解析
            for (key in events) {
                method = events[key];
                if (!_.isFunction(method)) method = this[events[key]];
                if (!method) continue;
 
                match = key.match(delegateEventSplitter);
                eventName = match[1], selector = match[2];
                method = _.bind(method, this);
                eventName = '.delegateUIEvents' this.id;
 
                if (selector === '') {
                    this.$el.on(eventName, method);
                } else {
                    this.$el.on(eventName, selector, method);
                }
            }
 
            return this;
        },
 
        unBindEvents: function () {
            this.$el.off('.delegateUIEvents' this.id);
            return this;
        }
    });
 
});
 
module.view

多少实体类

此间的多少实体对应着,MVC中的Model,因为事先曾经使用model用作了数量央浼相关的命名,这里便利用Entity做该工作:

JavaScript

define([], function () { /* 一些规范: init方法时,不可引起别的字段update */ var Entity = _.inherit({ initialize: function (opts) { this.propertys(); this.setOption(opts); }, propertys: function () { //只取页面展现供给多少 this.data = {}; //局地数据变动对应的响应程序,暂定为三个主意 //能够是叁个类的实例,若是是实例必得有render方法 this.controllers = {}; this.scope = null; }, subscribe: function (namespace, callback, scope) { if (typeof namespace === 'function') { scope = callback; callback = namespace; namespace = 'update'; } if (!namespace || !callback) return; if (scope) callback = $.proxy(callback, scope); if (!this.controllers[namespace]) this.controllers[namespace] = []; this.controllers[namespace].push(callback); }, unsubscribe: function (namespace) { if (!namespace) this.controllers = {}; if (this.controllers[namespace]) this.controllers[namespace] = []; }, publish: function (namespace, data) { if (!namespace) return; if (!this.controllers[namespace]) return; var arr = this.controllers[namespace]; var i, len = arr.length; for (i = 0; i < len; i ) { arr[i](data); } }, setOption: function (opts) { for (var k in opts) { this[k] = opts[k]; } }, //第二回起头化时,要求勘误数据,比如做服务器适配 //@override handleData: function () { }, //平日用于第叁次依照服务器数据源填充数据 initData: function (data) { var k; if (!data) return; //假诺暗许数据尚未被覆盖或许有误 for (k in this.data) { if (data[k]) this.data[k] = data[k]; } this.handleData(); this.publish('init', this.get()); }, //验证data的管用,要是不行的话,不应有举办以下逻辑,何况应该报警//@override validateData: function () { return true; }, //获取数据前,能够进行格式化 //@override formatData: function (data) { return data; }, //获取数据 get: function () { if (!this.validateData()) { //必要log return {}; } return this.formatData(this.data); }, //数据跟新后须求做的动作,施行相应的controller改动dom //@override update: function (key) { key = key || 'update'; var data = this.get(); this.publish(key, data); } }); return Entity; }); abstract.entity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
define([], function () {
    /*
    一些原则:
    init方法时,不可引起其它字段update
    */
    var Entity = _.inherit({
        initialize: function (opts) {
            this.propertys();
            this.setOption(opts);
        },
 
        propertys: function () {
            //只取页面展示需要数据
            this.data = {};
 
            //局部数据改变对应的响应程序,暂定为一个方法
            //可以是一个类的实例,如果是实例必须有render方法
            this.controllers = {};
 
            this.scope = null;
 
        },
 
        subscribe: function (namespace, callback, scope) {
            if (typeof namespace === 'function') {
                scope = callback;
                callback = namespace;
                namespace = 'update';
            }
            if (!namespace || !callback) return;
            if (scope) callback = $.proxy(callback, scope);
            if (!this.controllers[namespace]) this.controllers[namespace] = [];
            this.controllers[namespace].push(callback);
        },
 
        unsubscribe: function (namespace) {
            if (!namespace) this.controllers = {};
            if (this.controllers[namespace]) this.controllers[namespace] = [];
        },
 
        publish: function (namespace, data) {
            if (!namespace) return;
            if (!this.controllers[namespace]) return;
            var arr = this.controllers[namespace];
            var i, len = arr.length;
            for (i = 0; i < len; i ) {
                arr[i](data);
            }
        },
 
        setOption: function (opts) {
            for (var k in opts) {
                this[k] = opts[k];
            }
        },
 
        //首次初始化时,需要矫正数据,比如做服务器适配
        //@override
        handleData: function () { },
 
        //一般用于首次根据服务器数据源填充数据
        initData: function (data) {
            var k;
            if (!data) return;
 
            //如果默认数据没有被覆盖可能有误
            for (k in this.data) {
                if (data[k]) this.data[k] = data[k];
            }
 
            this.handleData();
            this.publish('init', this.get());
        },
 
        //验证data的有效性,如果无效的话,不应该进行以下逻辑,并且应该报警
        //@override
        validateData: function () {
            return true;
        },
 
        //获取数据前,可以进行格式化
        //@override
        formatData: function (data) {
            return data;
        },
 
        //获取数据
        get: function () {
            if (!this.validateData()) {
                //需要log
                return {};
            }
            return this.formatData(this.data);
        },
 
        //数据跟新后需要做的动作,执行对应的controller改变dom
        //@override
        update: function (key) {
            key = key || 'update';
            var data = this.get();
            this.publish(key, data);
        }
 
    });
 
    return Entity;
});
 
abstract.entity

此地的数量实体会以实例的方法注入给模块类实例,他的劳作是起一个心脏左右,达成模块之间的通讯,反正非常关键就是了

其它

数据央求统一使用abstract.model,数据前端缓存使用abstract.store,这里因为目的是做页面拆分,乞求模块不是生死攸关,各位可以把这段代码看层贰个简短的ajax就能够:

JavaScript

this.model.setParam({}); this.model.execute(function (data) { });

1
2
3
this.model.setParam({});
this.model.execute(function (data) {
});

事务入口

最终简短说下职业入口文件:

JavaScript

(function () { var project = './'; var viewRoot = 'pages'; require.config({ paths: { //BUS相关模板根目录 IndexPath: project 'pages/index', ListPath: project 'pages/list', BusStore: project 'model/bus.store', BusModel: project 'model/bus.model' } }); require(['AbstractApp', 'UIHeader'], function (APP, UIHeader) { window.APP = new APP({ UIHeader: UIHeader, viewRootPath: viewRoot }); window.APP.initApp(); }); })();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(function () {
    var project = './';
    var viewRoot = 'pages';
    require.config({
        paths: {
            //BUS相关模板根目录
            IndexPath: project 'pages/index',
            ListPath: project 'pages/list',
 
            BusStore: project 'model/bus.store',
            BusModel: project 'model/bus.model'
        }
    });
    require(['AbstractApp', 'UIHeader'], function (APP, UIHeader) {
        window.APP = new APP({
            UIHeader: UIHeader,
            viewRootPath: viewRoot
        });
        window.APP.initApp();
    });
})();

很简短的代码,钦命了下require的path配置,最终我们看看入口页面包车型大巴调用:

<!doctype html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui" /> <meta content="yes" name="apple-mobile-web-app-capable" /> <meta content="black" name="apple-mobile-web-app-status-bar-style" /> <meta name="format-detection" content="telephone=no" /> <link href="../static/css/global.css" rel="stylesheet" type="text/css" /> <title>车次列表</title> </head> <body> <div id="headerview"> <div class="cm-header"> <h1 class="cm-page-title js_title"> 正在加载... </h1> </div> </div> <div class="cm-page-wrap"> <div class="cm-state" id="js_page_state"> </div> <article class="cm-page" id="main"> </article> </div> <script type="text/javascript" src="../blade/libs/zepto.js"></script> <script src="../blade/libs/fastclick.js" type="text/javascript"></script> <script type="text/javascript" src="../blade/libs/underscore.js"></script> <script src="../blade/libs/underscore.extend.js" type="text/javascript"></script> <script type="text/javascript" src="../blade/libs/require.js"></script> <script type="text/javascript" src="../blade/common.js"></script> <script type="text/javascript" src="main.js"></script> </body> </html> list.html list.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui" />
  <meta content="yes" name="apple-mobile-web-app-capable" />
  <meta content="black" name="apple-mobile-web-app-status-bar-style" />
  <meta name="format-detection" content="telephone=no" />
  <link href="../static/css/global.css" rel="stylesheet" type="text/css" />
  <title>班次列表</title>
</head>
<body>
  <div id="headerview">
    <div class="cm-header">
      <h1 class="cm-page-title js_title">
        正在加载...
      </h1>
    </div>
  </div>
  <div class="cm-page-wrap">
    <div class="cm-state" id="js_page_state">
    </div>
    <article class="cm-page" id="main">
    </article>
  </div>
  <script type="text/javascript" src="../blade/libs/zepto.js"></script>
  <script src="../blade/libs/fastclick.js" type="text/javascript"></script>
  <script type="text/javascript" src="../blade/libs/underscore.js"></script>
  <script src="../blade/libs/underscore.extend.js" type="text/javascript"></script>
  <script type="text/javascript" src="../blade/libs/require.js"></script>
  <script type="text/javascript" src="../blade/common.js"></script>
  <script type="text/javascript" src="main.js"></script>
</body>
</html>
 
list.html
 
list.html

webapp ├─blade //框架目录 │ ├─data │ ├─libs │ ├─mvc │ └─ui ├─bus │ ├─model //数据央求模块,完全可以动用zepto ajax替换 │ └─pages │ ├─booking │ ├─index │ └─list //demo代码模块 └─static

1
2
3
4
5
6
7
8
9
10
11
12
13
webapp
├─blade //框架目录
│  ├─data
│  ├─libs
│  ├─mvc
│  └─ui
├─bus
│  ├─model //数据请求模块,完全可以使用zepto ajax替换
│  └─pages
│      ├─booking
│      ├─index
│      └─list //demo代码模块
└─static

接下去,让大家真实的开端拆分页面吗。

组件式编制程序

龙骨设计

首先,大家进行最简易的骨子设计,这里依次是其js代码与模板代码:

JavaScript

define(['AbstractView', 'text!ListPath/list.css', 'text!ListPath/tpl.layout.html'], function (AbstractView, style, layoutHtml) { return _.inherit(AbstractView, { propertys: function ($super) { $super(); this.style = style; this.template = layoutHtml; }, initHeader: function (name) { var title = '车次列表'; this.header.set({ view: this, title: title }); }, addEvent: function () { this.on('onShow', function () { console.log('页面渲染停止'); }); } }); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
define(['AbstractView', 'text!ListPath/list.css', 'text!ListPath/tpl.layout.html'], function (AbstractView, style, layoutHtml) {
    return _.inherit(AbstractView, {
        propertys: function ($super) {
            $super();
            this.style = style;
            this.template = layoutHtml;
        },
 
        initHeader: function (name) {
            var title = '班次列表';
            this.header.set({
                view: this,
                title: title
            });
        },
 
        addEvent: function () {
            this.on('onShow', function () {
                console.log('页面渲染结束');
            });
        }
    });
});

<div class="calendar-bar-wrapper js_calendar_wrapper"> 日历工具条模块 </div> <div class="none-data js_none_data" style="display: none;"> 当前暂无车的班次可订购</div> <div class="js_list_wrapper"> 列表模块 </div> <div class="js_list_loading" style="display: none; text-align: center; padding: 10px 0;"> 正在加载...</div> <ul class="bus-tabs list-filter"> <li class="tabs-item js_show_setoutdate"> <div class="line"> <i class="icon-time"></i>出发时段<i class="icon-sec"></i></div> <div class="line js_day_sec"> 全天</div> </li> <li class="tabs-item js_show_setstation"> <div class="line"> <i class="icon-circle icon-setout "></i>出发小车站<i class="icon-sec"></i></div> <div class="line js_start_sec"> 全部车站</div> </li> <li class="tabs-item js_show_arrivalstation"> <div class="line"> <i class="icon-circle icon-arrival "></i>到达小车站<i class="icon-sec"></i></div> <div class="line js_arrival_sec"> 全部车站</div> </li> </ul> tpl.layout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<div class="calendar-bar-wrapper js_calendar_wrapper">
    日历工具条模块
</div>
<div class="none-data js_none_data" style="display: none;">
    当前暂无班次可预订</div>
<div class="js_list_wrapper">
    列表模块
</div>
<div class="js_list_loading" style="display: none; text-align: center; padding: 10px 0;">
    正在加载...</div>
<ul class="bus-tabs list-filter">
    <li class="tabs-item js_show_setoutdate">
        <div class="line">
            <i class="icon-time"></i>出发时段<i class="icon-sec"></i></div>
        <div class="line js_day_sec">
            全天</div>
    </li>
    <li class="tabs-item js_show_setstation">
        <div class="line">
            <i class="icon-circle icon-setout "></i>出发汽车站<i class="icon-sec"></i></div>
        <div class="line js_start_sec">
            全部车站</div>
    </li>
    <li class="tabs-item js_show_arrivalstation">
        <div class="line">
            <i class="icon-circle icon-arrival "></i>到达汽车站<i class="icon-sec"></i></div>
        <div class="line js_arrival_sec">
            全部车站</div>
    </li>
</ul>
 
tpl.layout

页面突显如图:

图片 9

日历工具栏的兑现

这里要做的率先步是将日历工具栏模块完结,以数据为先的思辨,大家先落成了一个与日历业务有关的多少实体:

JavaScript

define(['AbstractEntity'], function (AbstractEntity) { var Entity = _.inherit(AbstractEntity, { propertys: function ($super) { $super(); var n = new Date(); var curTime = new Date(n.getFullYear(), n.getMonth(), n.getDate()).getTime(); this.data = { date: curTime, title: '当前天子' }; }, set: function (date) { if (!date) return; if (_.isDate(date)) date = date.getTime(); if (typeof date === 'string') date = parseInt(date); this.data.date = date; this.update(); }, getDateStr: function () { var date = new Date(); date.setTime(this.data.date); var dateDetail = _.dateUtil.getDetail(date); var name = dateDetail.year '-' dateDetail.month '-' dateDetail.day ' ' dateDetail.weekday (dateDetail.day1 ? '(' dateDetail.day1 ')' : ''); return name; }, nextDay: function () { this.set(this.getDate() 86600000); return true; }, getDate: function () { return parseInt(this.data.date); }, //是不是能够再往前一天 canPreDay: function () { var n = new Date(); var curTime = new Date(n.getFullYear(), n.getMonth(), n.getDate()).getTime(); //假若当后日期已然是第一天,则不得预约 if (curTime <= this.getDate() - 86600000) { return true; } return false; }, preDay: function () { if (!this.canPreDay()) return false; this.set(this.getDate() - 86400000); return true; } }); return Entity; }); en.date

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
define(['AbstractEntity'], function (AbstractEntity) {
 
    var Entity = _.inherit(AbstractEntity, {
        propertys: function ($super) {
            $super();
            var n = new Date();
            var curTime = new Date(n.getFullYear(), n.getMonth(), n.getDate()).getTime();
            this.data = {
                date: curTime,
                title: '当前日期'
            };
        },
 
        set: function (date) {
            if (!date) return;
            if (_.isDate(date)) date = date.getTime();
            if (typeof date === 'string') date = parseInt(date);
            this.data.date = date;
            this.update();
        },
 
        getDateStr: function () {
            var date = new Date();
            date.setTime(this.data.date);
            var dateDetail = _.dateUtil.getDetail(date);
            var name = dateDetail.year '-' dateDetail.month '-' dateDetail.day ' ' dateDetail.weekday (dateDetail.day1 ? '(' dateDetail.day1 ')' : '');
            return name;
        },
 
        nextDay: function () {
            this.set(this.getDate() 86400000);
            return true;
        },
 
        getDate: function () {
            return parseInt(this.data.date);
        },
 
        //是否能够再往前一天
        canPreDay: function () {
            var n = new Date();
            var curTime = new Date(n.getFullYear(), n.getMonth(), n.getDate()).getTime();
 
            //如果当前日期已经是第一天,则不可预订
            if (curTime <= this.getDate() - 86400000) {
                return true;
            }
            return false;
        },
 
        preDay: function () {
            if (!this.canPreDay()) return false;
            this.set(this.getDate() - 86400000);
            return true;
        }
 
    });
 
    return Entity;
});
 
en.date

其间完结日期工具栏全数相关数据操作,并且不带有实际的事情逻辑。

接下来这里开端安插日期工具栏的模块View:

JavaScript

define(['ModuleView', 'UICalendarBox', 'text!ListPath/tpl.calendar.bar.html'], function (ModuleView, UICalendarBox, tpl) { return _.inherit(ModuleView, { //此处若是要使用model,处实例化时候势须要保障entity的留存,即便一纸空文正是职业BUG initData: function () { this.template = tpl; this.events = { 'click .js_pre_day': 'preAction', 'click .js_next_day': 'nextAction', 'click .js_show_calendar': 'showCalendar' }; //初步化时候需要实践的回调 this.dateEntity.subscribe('init', this.render, this); this.dateEntity.subscribe(this.render, this); }, initDate: function () { var t = new Date().getTime(); //默许意况下获得当明日期,也会有过了18.00就设置为第二天日期 //当时一旦url上有startdatetime参数的话,便必要采纳之 if (_.getUrlParam().startdatetime) t = _.getUrlParam().startdatetime; this.dateEntity.initData({ date: t }); }, getViewModel: function () { var data = this.dateEntity.get(); data.formatStr = this.dateEntity.getDateStr(); data.canPreDay = this.dateEntity.canPreDay(); return data; }, preAction: function () { if (this.dateEntity.preDay()) return; this.view.showToast('前一天不足预约'); }, nextAction: function () { this.dateEntity.nextDay(); }, showCalendar: function () { var scope = this, endDate = new Date(); var secDate = new Date(); secDate.set提姆e(this.dateEntity.getDate()); endDate.setTime(new Date().getTime() 2593000000); if (!this.calendar) { this.calendar = new UICalendarBox({ endTime: endDate, selectDate: secDate, onItemClick: function (date, el, e) { scope.dateEntity.set(date); this.hide(); } }); } else { this.calendar.calendar.selectDate = secDate; this.calendar.calendar.refresh(); } this.calendar.show(); } }); }); mod.date

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
define(['ModuleView', 'UICalendarBox', 'text!ListPath/tpl.calendar.bar.html'], function (ModuleView, UICalendarBox, tpl) {
    return _.inherit(ModuleView, {
 
        //此处若是要使用model,处实例化时候一定要保证entity的存在,如果不存在便是业务BUG
        initData: function () {
 
            this.template = tpl;
            this.events = {
                'click .js_pre_day': 'preAction',
                'click .js_next_day': 'nextAction',
                'click .js_show_calendar': 'showCalendar'
            };
 
            //初始化时候需要执行的回调
            this.dateEntity.subscribe('init', this.render, this);
            this.dateEntity.subscribe(this.render, this);
 
        },
 
        initDate: function () {
            var t = new Date().getTime();
            //默认情况下获取当前日期,也有过了18.00就设置为第二天日期
            //当时一旦url上有startdatetime参数的话,便需要使用之
            if (_.getUrlParam().startdatetime) t = _.getUrlParam().startdatetime;
            this.dateEntity.initData({
                date: t
            });
        },
 
        getViewModel: function () {
            var data = this.dateEntity.get();
            data.formatStr = this.dateEntity.getDateStr();
            data.canPreDay = this.dateEntity.canPreDay();
            return data;
        },
 
        preAction: function () {
            if (this.dateEntity.preDay()) return;
            this.view.showToast('前一天不可预订');
        },
 
        nextAction: function () {
            this.dateEntity.nextDay();
        },
 
        showCalendar: function () {
            var scope = this, endDate = new Date();
            var secDate = new Date();
            secDate.setTime(this.dateEntity.getDate());
 
            endDate.setTime(new Date().getTime() 2592000000);
 
            if (!this.calendar) {
                this.calendar = new UICalendarBox({
                    endTime: endDate,
                    selectDate: secDate,
                    onItemClick: function (date, el, e) {
                        scope.dateEntity.set(date);
                        this.hide();
                    }
                });
            } else {
                this.calendar.calendar.selectDate = secDate;
                this.calendar.calendar.refresh();
            }
            this.calendar.show();
        }
 
    });
 
});
 
mod.date

本条组件模块干了几个工作:

① 首先,dateEntity实体要求由list.js那一个主view注入

② 这里为dateEntity注册了四个数据响应事件:

JavaScript

this.dateEntity.subscribe('init', this.render, this); this.dateEntity.subscribe(this.render, this);

1
2
this.dateEntity.subscribe('init', this.render, this);
this.dateEntity.subscribe(this.render, this);

render方法传承至基类,使用template与数据生成html,在那之中多少产生必需重写父类多少个方法:

JavaScript

getViewModel: function () { var data = this.dateEntity.get(); data.formatStr = this.dateEntity.getDateStr(); data.canPreDay = this.dateEntity.canPreDay(); return data; },

1
2
3
4
5
6
getViewModel: function () {
    var data = this.dateEntity.get();
    data.formatStr = this.dateEntity.getDateStr();
    data.canPreDay = this.dateEntity.canPreDay();
    return data;
},

因为此处的日历数量,暗中同意取当前时光,但是url参数恐怕传递日期参数,所以定义了贰个数量开首化方法:

JavaScript

initDate: function () { var t = new Date().getTime(); //暗许情状下获得当今日子,也可能有过了18.00就安装为第二天日期 //那时候一旦url上有startdatetime参数的话,便需求利用之 if (_.getUrlParam().startdatetime) t = _.getUrlParam().startdatetime; this.dateEntity.initData({ date: t }); },

1
2
3
4
5
6
7
8
9
initDate: function () {
    var t = new Date().getTime();
    //默认情况下获取当前日期,也有过了18.00就设置为第二天日期
    //当时一旦url上有startdatetime参数的话,便需要使用之
    if (_.getUrlParam().startdatetime) t = _.getUrlParam().startdatetime;
    this.dateEntity.initData({
        date: t
    });
},

该措施在主页面渲染甘休后会第一时常间调用,那年日历工具栏便渲染出来,个中国和扶桑历组件的接纳便不予理睬了,主要调控制器的代码改变如下:

JavaScript

define([ 'AbstractView', 'text!ListPath/list.css', 'ListPath/en.date', 'ListPath/mod.date', 'text!ListPath/tpl.layout.html' ], function ( AbstractView, style, DateEntity, DateModule, layoutHtml ) { return _.inherit(AbstractView, { _initEntity: function () { this.dateEntity = new DateEntity(); }, _initModule: function () { this.dateModule = new DateModule({ view: this, selector: '.js_calendar_wrapper', dateEntity: this.dateEntity }); }, propertys: function ($super) { $super(); this._initEntity(); this._initModule(); this.style = style; this.template = layoutHtml; }, initHeader: function (name) { var title = '车的班次列表'; this.header.set({ view: this, title: title }); }, add伊夫nt: function () { this.on('onShow', function () { //早先化date数据 this.dateModule.initDate(); }); } }); }); list.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
define([
    'AbstractView',
    'text!ListPath/list.css',
 
    'ListPath/en.date',
 
    'ListPath/mod.date',
 
    'text!ListPath/tpl.layout.html'
], function (
    AbstractView,
    style,
 
    DateEntity,
 
    DateModule,
 
    layoutHtml
) {
    return _.inherit(AbstractView, {
 
        _initEntity: function () {
            this.dateEntity = new DateEntity();
        },
 
        _initModule: function () {
            this.dateModule = new DateModule({
                view: this,
                selector: '.js_calendar_wrapper',
                dateEntity: this.dateEntity
            });
        },
 
        propertys: function ($super) {
            $super();
 
            this._initEntity();
            this._initModule();
 
            this.style = style;
            this.template = layoutHtml;
        },
 
        initHeader: function (name) {
            var title = '班次列表';
            this.header.set({
                view: this,
                title: title
            });
        },
 
        addEvent: function () {
            this.on('onShow', function () {
 
                //初始化date数据
                this.dateModule.initDate();
 
            });
        }
    });
 
});
 
list.js

JavaScript

_initEntity: function () { this.dateEntity = new DateEntity(); }, _initModule: function () { this.dateModule = new DateModule({ view: this, selector: '.js_calendar_wrapper', dateEntity: this.dateEntity }); },

1
2
3
4
5
6
7
8
9
10
11
_initEntity: function () {
    this.dateEntity = new DateEntity();
},
 
_initModule: function () {
    this.dateModule = new DateModule({
        view: this,
        selector: '.js_calendar_wrapper',
        dateEntity: this.dateEntity
    });
},

JavaScript

addEvent: function () { this.on('onShow', function () { //初始化date数据 this.dateModule.initDate(); }); }

1
2
3
4
5
6
7
addEvent: function () {
    this.on('onShow', function () {
        //初始化date数据
        this.dateModule.initDate();
 
    });
}

于是乎,整个分界面形成了这几个样子:

图片 10

此地是呼应的日历工具模板文件tpl.calendar.html:

<ul class="bus-tabs calendar-bar"> <li class="tabs-item js_pre_day <%=!canPreDay ? 'disabled' : '' %>">前一天</li> <li class="tabs-item js_show_calendar" style="-webkit-flex: 2; flex: 2;"><%=formatStr %></li> <li class="tabs-item js_next_day">后一天</li> </ul>

1
2
3
4
5
<ul class="bus-tabs calendar-bar">
    <li class="tabs-item  js_pre_day <%=!canPreDay ? 'disabled' : '' %>">前一天</li>
    <li class="tabs-item js_show_calendar" style="-webkit-flex: 2; flex: 2;"><%=formatStr %></li>
    <li class="tabs-item js_next_day">后一天</li>
</ul>

追寻工具栏的落到实处

咱俩以后的页面,固然不传任何U途锐L参数,已经能渲染出有个别页面了,不过上边出发站小车等业务数据必得等待车的班次列表数据央求结束才具替换数据,可是那一个数量若无出发城市和达到城市是无法倡导呼吁的,所以这里先完毕寻觅工具栏功效:

在动身城市依旧达到城市不真实的话便弹出寻觅工具栏,指引客商采取城市,这里新添弹出层必要在主页面调整器(检查测验主要调控制器)中动用贰个UI组件:

JavaScript

define([ 'AbstractView', 'text!ListPath/list.css', 'ListPath/en.date', 'ListPath/mod.date', 'text!ListPath/tpl.layout.html', 'text!ListPath/tpl.search.box.html', 'UIScrollLayer' ], function ( AbstractView, style, DateEntity, DateModule, layoutHtml, searchBoxHtml, UIScrollLayer ) { return _.inherit(AbstractView, { _initEntity: function () { this.dateEntity = new DateEntity(); }, _initModule: function () { this.dateModule = new DateModule({ view: this, selector: '.js_calendar_wrapper', dateEntity: this.dateEntity }); }, propertys: function ($super) { $super(); this._initEntity(); this._initModule(); this.style = style; this.template = layoutHtml; }, initHeader: function (name) { var title = '车次列表'; this.header.set({ view: this, title: title, back: function () { console.log('回降'); }, right: [ { tagname: 'search-bar', value: '找出', callback: function () { console.log('弹出寻觅框'); this.showSearchBox(); } } ] }); }, //找出工具弹出层 showSearchBox: function () { var scope = this; if (!this.searchBox) { this.searchBox = new UIScrollLayer({ title: '请选取搜索条件', html: searchBoxHtml, events: { 'click .js-start': function () { }, 'click .js-arrive': function () { }, 'click .js_search_list': function () { console.log('查询列表'); } } }); } this.searchBox.show(); }, addEvent: function () { this.on('onShow', function () { //最早化date数据 this.dateModule.initDate(); //这里剖断是不是须求弹出找出弹出层 if (!_.getUrlParam().startcityid || !_.getUrlParam().arrivalcityid) { this.showSearchBox(); return; } }); } }); }); list.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
define([
    'AbstractView',
    'text!ListPath/list.css',
 
    'ListPath/en.date',
 
    'ListPath/mod.date',
 
    'text!ListPath/tpl.layout.html',
    'text!ListPath/tpl.search.box.html',
    'UIScrollLayer'
], function (
    AbstractView,
    style,
 
    DateEntity,
 
    DateModule,
 
    layoutHtml,
    searchBoxHtml,
    UIScrollLayer
) {
    return _.inherit(AbstractView, {
 
        _initEntity: function () {
            this.dateEntity = new DateEntity();
        },
 
        _initModule: function () {
            this.dateModule = new DateModule({
                view: this,
                selector: '.js_calendar_wrapper',
                dateEntity: this.dateEntity
            });
        },
 
        propertys: function ($super) {
            $super();
 
            this._initEntity();
            this._initModule();
 
            this.style = style;
            this.template = layoutHtml;
        },
 
        initHeader: function (name) {
            var title = '班次列表';
            this.header.set({
                view: this,
                title: title,
                back: function () {
                    console.log('回退');
                },
                right: [
                    {
                        tagname: 'search-bar',
                        value: '搜索',
                        callback: function () {
                            console.log('弹出搜索框');
                            this.showSearchBox();
                        }
                    }
                ]
            });
        },
 
        //搜索工具弹出层
        showSearchBox: function () {
            var scope = this;
            if (!this.searchBox) {
                this.searchBox = new UIScrollLayer({
                    title: '请选择搜索条件',
                    html: searchBoxHtml,
                    events: {
                        'click .js-start': function () {
 
                        },
                        'click .js-arrive': function () {
 
                        },
                        'click .js_search_list': function () {
 
                            console.log('查询列表');
                        }
                    }
                });
            }
            this.searchBox.show();
        },
 
        addEvent: function () {
            this.on('onShow', function () {
                //初始化date数据
                this.dateModule.initDate();
 
                //这里判断是否需要弹出搜索弹出层
                if (!_.getUrlParam().startcityid || !_.getUrlParam().arrivalcityid) {
                    this.showSearchBox();
                    return;
                }
 
            });
        }
    });
 
});
 
list.js

对应找出弹出层html模板:

<div class="c-row search-line" data-flag="start"> <div class="c-span3"> 出发</div> <div class="c-span9 js-start search-line-txt"> 请选抽取发地</div> </div> <div class="c-row search-line" data-flag="arrive"> <div class="c-span3"> 达到</div> <div class="c-span9 js-arrive search-line-txt"> 请选择达到地</div> </div> <div class="c-row " data-flag="arrive"> <span class="btn-primary full-width js_search_list">查询</span> </div> tpl.search.box.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="c-row search-line" data-flag="start">
    <div class="c-span3">
        出发</div>
    <div class="c-span9 js-start search-line-txt">
        请选择出发地</div>
</div>
<div class="c-row search-line" data-flag="arrive">
    <div class="c-span3">
        到达</div>
    <div class="c-span9 js-arrive search-line-txt">
        请选择到达地</div>
</div>
<div class="c-row " data-flag="arrive">
    <span class="btn-primary full-width js_search_list">查询</span>
</div>
 
tpl.search.box.html

这里主题代码是:

JavaScript

//搜索工具弹出层 showSearchBox: function () { var scope = this; if (!this.search博克斯) { this.searchBox = new UIScrollLayer({ title: '请选用寻觅条件', html: searchBoxHtml, events: { 'click .js-start': function () { }, 'click .js-arrive': function () { }, 'click .js_search_list': function () { console.log('查询列表'); } } }); } this.searchBox.show(); },

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//搜索工具弹出层
showSearchBox: function () {
    var scope = this;
    if (!this.searchBox) {
        this.searchBox = new UIScrollLayer({
            title: '请选择搜索条件',
            html: searchBoxHtml,
            events: {
                'click .js-start': function () {
 
                },
                'click .js-arrive': function () {
 
                },
                'click .js_search_list': function () {
 
                    console.log('查询列表');
                }
            }
        });
    }
    this.searchBox.show();
},

于是当U智跑L什么参数都尚未的时候,就能够弹出这几个寻找框

图片 11

此地也迎来了二个难关,因为城市列表事实上应该是贰个独门的可访谈的页面,不过此间是想用弹出层的点子调用他,所以本人在APP层完毕了贰个措施可以用弹出层的主意调起三个独立的页面。

JavaScript

只顾: 这里city城市列表未完全使用组件化的格局开垦,风野趣的心上人能够团结尝试着开拓

1
2
注意:
这里city城市列表未完全采用组件化的方式开发,有兴趣的朋友可以自己尝试着开发

此地有一个见仁见智的地方是,因为大家点击查询的时候才会抓好业数据更新,这里是仅仅的做DOM操作了,这里不设置数据实体一个缘由就是:

其一搜索弹出层是贰个页面级DOM之外的一对,数据实体变化平时只应该影响Page级其余DOM,除非真的有五个页面级View会公用一个数量实体。

JavaScript

define([ 'AbstractView', 'text!ListPath/list.css', 'ListPath/en.date', 'ListPath/mod.date', 'text!ListPath/tpl.layout.html', 'text!ListPath/tpl.search.box.html', 'UIScrollLayer' ], function ( AbstractView, style, DateEntity, DateModule, layoutHtml, searchBoxHtml, UIScrollLayer ) { return _.inherit(AbstractView, { _initEntity: function () { this.dateEntity = new DateEntity(); }, _initModule: function () { this.dateModule = new DateModule({ view: this, selector: '.js_calendar_wrapper', dateEntity: this.dateEntity }); }, propertys: function ($super) { $super(); this._initEntity(); this._initModule(); this.style = style; this.template = layoutHtml; //主要调整制器业务特性this.urlData = { start: {}, end: {} }; }, initHeader: function (name) { var title = '车的班次列表'; this.header.set({ view: this, title: title, back: function () { console.log('回落'); }, right: [ { tagname: 'search-bar', value: '找出', callback: function () { console.log('弹出寻找框'); this.showSearchBox(); } } ] }); }, //搜索工具弹出层 showSearchBox: function () { var scope = this; if (!this.searchBox) { this.searchBox = new UIScrollLayer({ title: '请选拔寻觅条件', html: searchBoxHtml, events: { 'click .js-start': function (e) { scope._showCityView('start', $(e.currentTarget)); }, 'click .js-arrive': function (e) { scope._showCityView('end', $(e.currentTarget)); }, 'click .js_search_list': function () { var param = {}; if (!scope.urlData.start.id) { scope.showToast('请先选拔出发城市'); return; } if (!scope.urlData.end.id) { scope.showToast('请先选拔到达城市'); return; } //这里一定会有出发城市与达到城市等数码 param.startcityid = scope.urlData.start.id; param.arrivalcityid = scope.urlData.end.id; param.startdatetime = scope.dateEntity.getDate(); param.startname = scope.urlData.start.name; param.arrivename = scope.urlData.end.name; if (scope.urlData.start.station) { param.startstationid = scope.urlData.start.station } if (scope.urlData.end.station) { param.arrivalstationid = end_station } scope.forward('list', param); this.hide(); } } }); } this.searchBox.show(); }, _showCityView: function (key, el) { var scope = this; if (key == 'end') { //因为到达车站会依附出发车站的数码,所以那边得先做剖断 if (!this.urlData.start.id) { this.showToast('请先选用出发城市'); return; } } this.showPageView('city', { flag: key, startId: this.urlData.start.id, type: this.urlData.start.type, onCityItemClick: function (id, name, station, type) { scope.urlData[key] = {}; scope.urlData[key]['id'] = id; scope.urlData[key]['type'] = type; scope.urlData[key]['name'] = name; if (station) scope.urlData[key]['name'] = station; el.text(name); scope.hidePageView(); }, onBackAction: function () { scope.hidePageView(); } }); }, addEvent: function () { this.on('onShow', function () { //最早化date数据 this.dateModule.initDate(); //这里判别是不是须要弹出找寻弹出层 if (!_.getUrlParam().startcityid || !_.getUrlParam().arrivalcityid) { this.showSearchBox(); return; } }); } }); }); list.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
define([
    'AbstractView',
    'text!ListPath/list.css',
 
    'ListPath/en.date',
 
    'ListPath/mod.date',
 
    'text!ListPath/tpl.layout.html',
    'text!ListPath/tpl.search.box.html',
    'UIScrollLayer'
], function (
    AbstractView,
    style,
 
    DateEntity,
 
    DateModule,
 
    layoutHtml,
    searchBoxHtml,
    UIScrollLayer
) {
    return _.inherit(AbstractView, {
 
        _initEntity: function () {
            this.dateEntity = new DateEntity();
 
        },
 
        _initModule: function () {
            this.dateModule = new DateModule({
                view: this,
                selector: '.js_calendar_wrapper',
                dateEntity: this.dateEntity
            });
 
        },
 
        propertys: function ($super) {
            $super();
 
            this._initEntity();
            this._initModule();
 
            this.style = style;
            this.template = layoutHtml;
 
            //主控制器业务属性
            this.urlData = {
                start: {},
                end: {}
            };
 
        },
 
        initHeader: function (name) {
            var title = '班次列表';
            this.header.set({
                view: this,
                title: title,
                back: function () {
                    console.log('回退');
                },
                right: [
                    {
                        tagname: 'search-bar',
                        value: '搜索',
                        callback: function () {
                            console.log('弹出搜索框');
                            this.showSearchBox();
                        }
                    }
                ]
            });
        },
 
        //搜索工具弹出层
        showSearchBox: function () {
            var scope = this;
            if (!this.searchBox) {
                this.searchBox = new UIScrollLayer({
                    title: '请选择搜索条件',
                    html: searchBoxHtml,
                    events: {
                        'click .js-start': function (e) {
                            scope._showCityView('start', $(e.currentTarget));
                        },
                        'click .js-arrive': function (e) {
                            scope._showCityView('end', $(e.currentTarget));
                        },
                        'click .js_search_list': function () {
                            var param = {};
 
                            if (!scope.urlData.start.id) {
                                scope.showToast('请先选择出发城市');
                                return;
                            }
 
                            if (!scope.urlData.end.id) {
                                scope.showToast('请先选择到达城市');
                                return;
                            }
 
                            //这里一定会有出发城市与到达城市等数据
                            param.startcityid = scope.urlData.start.id;
                            param.arrivalcityid = scope.urlData.end.id;
                            param.startdatetime = scope.dateEntity.getDate();
                            param.startname = scope.urlData.start.name;
                            param.arrivename = scope.urlData.end.name;
 
                            if (scope.urlData.start.station) {
                                param.startstationid = scope.urlData.start.station
                            }
 
                            if (scope.urlData.end.station) {
                                param.arrivalstationid = end_station
                            }
 
                            scope.forward('list', param);
                            this.hide();
                        }
                    }
                });
            }
            this.searchBox.show();
        },
 
        _showCityView: function (key, el) {
            var scope = this;
 
            if (key == 'end') {
                //因为到达车站会依赖出发车站的数据,所以这里得先做判断
                if (!this.urlData.start.id) {
                    this.showToast('请先选择出发城市');
                    return;
                }
            }
 
            this.showPageView('city', {
                flag: key,
                startId: this.urlData.start.id,
                type: this.urlData.start.type,
                onCityItemClick: function (id, name, station, type) {
                    scope.urlData[key] = {};
                    scope.urlData[key]['id'] = id;
                    scope.urlData[key]['type'] = type;
                    scope.urlData[key]['name'] = name;
                    if (station) scope.urlData[key]['name'] = station;
                    el.text(name);
                    scope.hidePageView();
                },
                onBackAction: function () {
                    scope.hidePageView();
                }
            });
        },
 
        addEvent: function () {
            this.on('onShow', function () {
                //初始化date数据
                this.dateModule.initDate();
 
                //这里判断是否需要弹出搜索弹出层
                if (!_.getUrlParam().startcityid || !_.getUrlParam().arrivalcityid) {
                    this.showSearchBox();
                    return;
                }
 
            });
        }
    });
 
});
 
list.js

追寻功能完成后,我们这里便得以进来真正的数额伏乞功效渲染列表了。

任何模块

在促成数据央求从前,我根据日期模块的不二秘诀将下边八个模块的效果也一并成功了,这里独一差别的是,这一个模块的DOM已经存在,大家没有要求渲染了,实现后的代码大约是那样的:

JavaScript

define([ 'AbstractView', 'text!ListPath/list.css', 'ListPath/en.station', 'ListPath/en.date', 'ListPath/en.time', 'ListPath/mod.date', 'ListPath/mod.time', 'ListPath/mod.setout', 'ListPath/mod.arrive', 'text!ListPath/tpl.layout.html', 'text!ListPath/tpl.search.box.html', 'UIScrollLayer' ], function ( AbstractView, style, StationEntity, DateEntity, TimeEntity, DateModule, TimeModule, SetoutModule, ArriveModule, layoutHtml, searchBoxHtml, UIScrollLayer ) { return _.inherit(AbstractView, { _initEntity: function () { this.dateEntity = new DateEntity(); this.timeEntity = new TimeEntity(); this.timeEntity.subscribe('init', this.renderTime, this); this.timeEntity.subscribe(this.renderTime, this); this.setoutEntity = new StationEntity(); this.setoutEntity.subscribe('init', this.renderSetout, this); this.setoutEntity.subscribe(this.renderSetout, this); this.arriveEntity = new StationEntity(); this.arriveEntity.subscribe('init', this.renderArrive, this); this.arriveEntity.subscribe(this.renderArrive, this); }, _initModule: function () { this.dateModule = new DateModule({ view: this, selector: '.js_calendar_wrapper', dateEntity: this.dateEntity }); this.timeModule = new TimeModule({ view: this, selector: '.js_show_setoutdate', timeEntity: this.timeEntity }); this.setOutModule = new SetoutModule({ view: this, selector: '.js_show_setstation', setoutEntity: this.setoutEntity }); this.arriveModule = new ArriveModule({ view: this, selector: '.js_show_arrivalstation', arriveEntity: this.arriveEntity }); }, propertys: function ($super) { $super(); this._initEntity(); this._initModule(); this.style = style; this.template = layoutHtml; //主要调控制器业务特性 this.urlData = { start: {}, end: {} }; }, initHeader: function (name) { var title = '车的班次列表'; this.header.set({ view: this, title: title, back: function () { console.log('回落'); }, right: [ { tagname: 'search-bar', value: '寻找', callback: function () { console.log('弹出寻觅框'); this.showSearchBox(); } } ] }); }, initElement: function () { this.d_list_wrapper = this.$('.js_list_wrapper'); this.d_none_data = this.$('.js_none_data'); this.d_js_show_setoutdate = this.$('.js_show_setoutdate'); this.d_js_show_setstation = this.$('.js_show_setstation'); this.d_js_show_arrivalstation = this.$('.js_show_arrivalstation'); this.d_js_list_loading = this.$('.js_list_loading'); this.d_js_tabs = this.$('.js_tabs'); this.d_js_day_sec = this.$('.js_day_sec'); this.d_js_start_sec = this.$('.js_start_sec'); this.d_js_arrival_sec = this.$('.js_arrival_sec'); }, //搜索工具弹出层 showSearchBox: function () { var scope = this; if (!this.searchBox) { this.searchBox = new UIScrollLayer({ title: '请选取寻觅条件', html: search博克斯Html, events: { 'click .js-start': function (e) { scope._showCityView('start', $(e.currentTarget)); }, 'click .js-arrive': function (e) { scope._showCityView('end', $(e.currentTarget)); }, 'click .js_search_list': function () { var param = {}; if (!scope.urlData.start.id) { scope.showToast('请先选抽出发城市'); return; } if (!scope.urlData.end.id) { scope.showToast('请先采用到达城市'); return; } //这里一定会有出发城市与达到城市等数码 param.startcityid = scope.urlData.start.id; param.arrivalcityid = scope.urlData.end.id; param.startdatetime = scope.dateEntity.getDate(); param.startname = scope.urlData.start.name; param.arrivename = scope.urlData.end.name; if (scope.urlData.start.station) { param.startstationid = scope.urlData.start.station } if (scope.urlData.end.station) { param.arrivalstationid = end_station } scope.forward('list', param); this.hide(); } } }); } this.searchBox.show(); }, _showCityView: function (key, el) { var scope = this; if (key == 'end') { //因为达到车站会依靠出发车站的数码,所以这里得先做剖断 if (!this.urlData.start.id) { this.showToast('请先选拔出发城市'); return; } } this.showPageView('city', { flag: key, startId: this.urlData.start.id, type: this.urlData.start.type, onCityItemClick: function (id, name, station, type) { scope.urlData[key] = {}; scope.urlData[key]['id'] = id; scope.urlData[key]['type'] = type; scope.urlData[key]['name'] = name; if (station) scope.urlData[key]['name'] = station; el.text(name); scope.hidePageView(); }, onBackAction: function () { scope.hidePageView(); } }); }, //初叶化出发车站,该数量会趁着数据加载结束而转换//借使url具备出发站名称以至id,必要新鲜管理 initSetoutEntity: function () { var data = {}; if (_.getUrlParam().startstationid) { //出发车站只怕并不曾传,包容老代码 data.name = _.getUrlParam().startname || '全体车站'; data.id = _.getUrlParam().startstationid; } this.setoutEntity.initData(data, data.id); }, //伊始化达到站 initArriveEntity: function () { var data = {}; if (_.getUrlParam().arrivalstationid) { //出发车站恐怕并不曾传,包容老代码 data.name = _.getUrlParam().arrivename || '全体车站'; data.id = _.getUrlParam().arrivalstationid; } this.arriveEntity.initData(data, data.id); }, //时段独有变化时候才有所显示状态 renderTime: function () { var name = this.timeEntity.getName(); this.d_js_day_sec.html(name); }, renderSetout: function () { var name = this.setoutEntity.getName(); this.d_js_start_sec.html(name); }, renderArrive: function () { var name = this.arriveEntity.getName(); this.d_js_arrival_sec.html(name); }, addEvent: function () { this.on('onShow', function () { //领头化date数据 this.dateModule.initDate(); //这里推断是还是不是要求弹出搜索弹出层 if (!_.getUrlParam().startcityid || !_.getUrlParam().arrivalcityid) { this.showSearchBox(); return; } //带头化时段选用 this.timeEntity.initData(); this.initSetoutEntity(); this.initArriveEntity(); }); } }); }); list.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
define([
    'AbstractView',
    'text!ListPath/list.css',
 
    'ListPath/en.station',
    'ListPath/en.date',
    'ListPath/en.time',
 
    'ListPath/mod.date',
    'ListPath/mod.time',
    'ListPath/mod.setout',
    'ListPath/mod.arrive',
 
    'text!ListPath/tpl.layout.html',
    'text!ListPath/tpl.search.box.html',
    'UIScrollLayer'
], function (
    AbstractView,
    style,
 
    StationEntity,
    DateEntity,
    TimeEntity,
 
    DateModule,
    TimeModule,
    SetoutModule,
    ArriveModule,
 
    layoutHtml,
    searchBoxHtml,
    UIScrollLayer
) {
    return _.inherit(AbstractView, {
 
        _initEntity: function () {
            this.dateEntity = new DateEntity();
 
            this.timeEntity = new TimeEntity();
            this.timeEntity.subscribe('init', this.renderTime, this);
            this.timeEntity.subscribe(this.renderTime, this);
 
            this.setoutEntity = new StationEntity();
            this.setoutEntity.subscribe('init', this.renderSetout, this);
            this.setoutEntity.subscribe(this.renderSetout, this);
 
            this.arriveEntity = new StationEntity();
            this.arriveEntity.subscribe('init', this.renderArrive, this);
            this.arriveEntity.subscribe(this.renderArrive, this);
 
        },
 
        _initModule: function () {
            this.dateModule = new DateModule({
                view: this,
                selector: '.js_calendar_wrapper',
                dateEntity: this.dateEntity
            });
 
            this.timeModule = new TimeModule({
                view: this,
                selector: '.js_show_setoutdate',
                timeEntity: this.timeEntity
            });
 
            this.setOutModule = new SetoutModule({
                view: this,
                selector: '.js_show_setstation',
                setoutEntity: this.setoutEntity
            });
 
            this.arriveModule = new ArriveModule({
                view: this,
                selector: '.js_show_arrivalstation',
                arriveEntity: this.arriveEntity
            });
 
        },
 
        propertys: function ($super) {
            $super();
 
            this._initEntity();
            this._initModule();
 
            this.style = style;
            this.template = layoutHtml;
 
            //主控制器业务属性
            this.urlData = {
                start: {},
                end: {}
            };
 
        },
 
        initHeader: function (name) {
            var title = '班次列表';
            this.header.set({
                view: this,
                title: title,
                back: function () {
                    console.log('回退');
                },
                right: [
                    {
                        tagname: 'search-bar',
                        value: '搜索',
                        callback: function () {
                            console.log('弹出搜索框');
                            this.showSearchBox();
                        }
                    }
                ]
            });
        },
 
        initElement: function () {
            this.d_list_wrapper = this.$('.js_list_wrapper');
            this.d_none_data = this.$('.js_none_data');
 
            this.d_js_show_setoutdate = this.$('.js_show_setoutdate');
            this.d_js_show_setstation = this.$('.js_show_setstation');
            this.d_js_show_arrivalstation = this.$('.js_show_arrivalstation');
            this.d_js_list_loading = this.$('.js_list_loading');
            this.d_js_tabs = this.$('.js_tabs');
 
            this.d_js_day_sec = this.$('.js_day_sec');
            this.d_js_start_sec = this.$('.js_start_sec');
            this.d_js_arrival_sec = this.$('.js_arrival_sec');
        },
 
        //搜索工具弹出层
        showSearchBox: function () {
            var scope = this;
            if (!this.searchBox) {
                this.searchBox = new UIScrollLayer({
                    title: '请选择搜索条件',
                    html: searchBoxHtml,
                    events: {
                        'click .js-start': function (e) {
                            scope._showCityView('start', $(e.currentTarget));
                        },
                        'click .js-arrive': function (e) {
                            scope._showCityView('end', $(e.currentTarget));
                        },
                        'click .js_search_list': function () {
                            var param = {};
 
                            if (!scope.urlData.start.id) {
                                scope.showToast('请先选择出发城市');
                                return;
                            }
 
                            if (!scope.urlData.end.id) {
                                scope.showToast('请先选择到达城市');
                                return;
                            }
 
                            //这里一定会有出发城市与到达城市等数据
                            param.startcityid = scope.urlData.start.id;
                            param.arrivalcityid = scope.urlData.end.id;
                            param.startdatetime = scope.dateEntity.getDate();
                            param.startname = scope.urlData.start.name;
                            param.arrivename = scope.urlData.end.name;
 
                            if (scope.urlData.start.station) {
                                param.startstationid = scope.urlData.start.station
                            }
 
                            if (scope.urlData.end.station) {
                                param.arrivalstationid = end_station
                            }
 
                            scope.forward('list', param);
                            this.hide();
                        }
                    }
                });
            }
            this.searchBox.show();
        },
 
        _showCityView: function (key, el) {
            var scope = this;
 
            if (key == 'end') {
                //因为到达车站会依赖出发车站的数据,所以这里得先做判断
                if (!this.urlData.start.id) {
                    this.showToast('请先选择出发城市');
                    return;
                }
            }
 
            this.showPageView('city', {
                flag: key,
                startId: this.urlData.start.id,
                type: this.urlData.start.type,
                onCityItemClick: function (id, name, station, type) {
                    scope.urlData[key] = {};
                    scope.urlData[key]['id'] = id;
                    scope.urlData[key]['type'] = type;
                    scope.urlData[key]['name'] = name;
                    if (station) scope.urlData[key]['name'] = station;
                    el.text(name);
                    scope.hidePageView();
                },
                onBackAction: function () {
                    scope.hidePageView();
                }
            });
        },
 
        //初始化出发车站,该数据会随着数据加载结束而变化
        //如果url具有出发站名称以及id,需要特殊处理
        initSetoutEntity: function () {
            var data = {};
            if (_.getUrlParam().startstationid) {
                //出发车站可能并没有传,兼容老代码
                data.name = _.getUrlParam().startname || '全部车站';
                data.id = _.getUrlParam().startstationid;
            }
 
            this.setoutEntity.initData(data, data.id);
        },
 
        //初始化到达站
        initArriveEntity: function () {
 
            var data = {};
            if (_.getUrlParam().arrivalstationid) {
                //出发车站可能并没有传,兼容老代码
                data.name = _.getUrlParam().arrivename || '全部车站';
                data.id = _.getUrlParam().arrivalstationid;
            }
 
            this.arriveEntity.initData(data, data.id);
        },
 
        //时段只有变化时候才具有显示状态
        renderTime: function () {
            var name = this.timeEntity.getName();
            this.d_js_day_sec.html(name);
        },
 
        renderSetout: function () {
            var name = this.setoutEntity.getName();
            this.d_js_start_sec.html(name);
        },
 
        renderArrive: function () {
            var name = this.arriveEntity.getName();
            this.d_js_arrival_sec.html(name);
        },
 
        addEvent: function () {
            this.on('onShow', function () {
                //初始化date数据
                this.dateModule.initDate();
 
                //这里判断是否需要弹出搜索弹出层
                if (!_.getUrlParam().startcityid || !_.getUrlParam().arrivalcityid) {
                    this.showSearchBox();
                    return;
                }
 
                //初始化时段选择
                this.timeEntity.initData();
                this.initSetoutEntity();
                this.initArriveEntity();
 
            });
        }
    });
 
});
 
list.js

其不平日候整个逻辑结构大要上出来了:

图片 12

JavaScript

瞩目: 因为该文耗费时间过长,导致本人现在体力有一点点虚脱,所以那边的代码不必然最优

1
2
注意:
因为该文耗时过长,导致我现在体力有点虚脱,所以这里的代码不一定最优

终极效果:

图片 13

到此,demo结束了,最终变成的目录:

图片 14

三个js便足以拆分成那样多的小组件模块,借使是更加的目眩神摇的页面,这里的文书会数不胜数,举个例子订单填写页的组件模块是此处的三倍。

组件化的利害

组件化带来的多少个亮点十二分显著:

JavaScript

① 组件化拆分,使得主要调节制业务逻辑清晰轻松 ② 各样业务组件模块效能相对独立,可维护性可测量检验性大大进步 ③ 组件之间可以任性组合,有必然可重用性 ④ 增删模块不会怕打断骨头连着筋 ⑤ 三个政工模块所需代码全体在三个目录,相比好操作(有一点点凑数困惑)

1
2
3
4
5
① 组件化拆分,使得主控制业务逻辑清晰简单
② 各个业务组件模块功能相对独立,可维护性可测试性大大提升
③ 组件之间可以任意组合,有一定可重用性
④ 增删模块不会怕打断骨头连着筋
⑤ 一个业务模块所需代码全部在一个目录,比较好操作(有点凑数嫌疑)

缺点

事实上,组件化不会拉动如何不足,对于不打听的相爱的人可能会认为代码复杂度有所增添,其实不这么做代码才真正叫叁个难吗!

诚然的美中不足的要挑四个毛病的话,这种分拆只怕会比单个文件代码量稍大

从品质优化角度看组件化

任凭如何前端优化,最终的瓶颈一定是在伏乞量上做小说:压缩、缓存、仅仅做首屏渲染、将jQuery缓存zepto……

说都会说,不过不菲气象由不得你那样做,项目丰富复杂,而UI又提供给了不一样团体采取以来,有一天前端做了一回UI优化,而怎么着将本次UI优化反应到线上才是考验架构设计的时候,借使是不好的统一计划的话,想将这一次优化推上线,会生出多少个业务:

① 业务团队大改代码

② 框架能源(js&css)膨胀

这种胸口痛的标题是形似人做优化考虑不到的,而事情公司不会因为你的更新而去修改代码,所以日常会以代码膨胀为代价将此次优化强推上线,那往往会让情状更为复杂:

新老代码融入,3个月后您根本不通晓什么代码能够删,哪些代码能够留,一点都不小时候那一个难点会反映在具备公共天性的CSS中 假如你的CSS同期服务于八个集体,而一一公司的框架版本分化样,那么UI进级对你的话大概是一个梦魇! 假诺您想做第三轮车的UI升级,那照旧算了吧……

其实,作者评价叁个前端是或不是丰裕厉害,往往就能从那边思考:

当三个项目丰裕复杂后,你私行做好了优化,但是你的优化代码不可能无缝的让事情公司选用,而需求专门的学业团队做过多改换,你怎么着缓和这种难题

成都百货上千前端做二个优化,正是重复做了三个事物,刚初阶确定比线上的好,但7个月后,这一个代码质量还未必有从前的可以吗,所以我们那边应该消除的是:

哪些筹算三个编写制定,让专门的学问公司以细小的退换,而得以用上新的UI(样式、天性),而不会加多CSS(JS)体积这么些只怕是组件化真正要消除的事体!

大好图景下,一个H5的财富整合情形是如此的:

① 公共大旨CSS文件(200行左右)

② 框架主旨文件(包蕴框架主题和第三方库)

③ UI组件(有好多单身的UI组件组成,每一种UI组件又包括完整的HTML&CSS)

④ 公共事务模块(提供业务等级公共服务,比方登陆、城市列表等事务相关作用)

⑤ 业务频道一个页面,约等于我们那边的list页的代码

因为框架大旨日常的话是非凡转移的,固然改动也是对表现层透明的,UI采纳增量与预加运载飞机制,那样做会对持续样式进级,UI晋级有可观的功利,而专门的工作组件化后本人要做怎么样滚动加载也是简单

好的前端架构划设想计应该满足不停的UI进级须要,而不扩展业务团队下载量

结语

正文就怎么着分解复杂的前端页面提议了部分融洽的主张,并且给予了达成,希望对各位有所扶植。

关于统一

前面贰个代码有分拆就有统一,因为最终二个完完全全的页面须要有所能源工夫运作,但思考到此文已经相当长了,关于统一一块的职业留待下文深入分析吧

有关代码

为了有协助各位精通组件化开辟的牵挂,小编这里写了二个完完全全的demo帮助各位深入分析,由于精力有限,代码难免会有BUG,各位多多原谅:

可能会浏览的代码:

2 赞 9 收藏 1 评论

图片 15

本文由67677新澳门手机版发布于新京葡娱乐场网址,转载请注明出处:前端进阶篇之如何编写可维护可升级的代码

关键词: