在移动浪潮袭来的时候,小钗有幸进入框架组作webapp框架开发,过程当中遇到了移动端的各类坑,也产生了各类激情,就咱们公司的发展历程来讲javascript
第一阶段:使用传统方式开发移动站点,少许引入HTML5元素php
第二阶段:框架化,使用jquery mobile框架,发现慢,组件很差管理,很差维护给搞掉了css
第三阶段:jquery+Backbone的组合,最后为了瘦身将jquery换成了zeptohtml
第四阶段:框架适应Hybrid版本,Hybrid相关频道与H5站点一套代码,业务扩展遍地开花前端
第五阶段:框架适应ipad版本/实现前端ABTesting,因为不可控缘由,计划夭折java
框架使用过程当中须要快速适应业务需求,框架中过多的掺杂了业务代码,而且随之发展,框架自己的耦合度、设计不合理的地方也体现了出来jquery
小钗虽然知道哪里有问题,但框架的代码不是想象那么好改,一个API的改变会致使整个业务线的崩溃,听之任之又很不爽android
开发一套干净webapp框架的想法油然而生,因而该框架便出现了git
诚然,此框架比不上Backbone、比不了anglarJS,毕竟小钗的资历、水平有限,因此框架自己可能都会有一些缺陷,但做为初步接触前端的同窗程序员
或者想在前端看到一些设计思想的同窗,或者想在移动webapp试水的公司。此框架仍是有一些他一些优点,咱们带着看看不吃亏的想法,仍是能够看看嘛!!!
也但愿各位多多支持,代码写的很差也在提升,由于小钗不是专业的重构,其中的CSS及DOM结构所有由在线网站down下来了,这里是抄袭我说清楚了哦。
① 此次首先出一篇大致介绍性文章,简单结束
② 然后有1,2篇博客对其中的全局MVC相关作说明,这里使用了hashChange与pushState两种方案
③ 而后对其中的UI继承关系作梳理,并拿其中比较复杂的几个UI组件说一下设计思路,预计会有3-5篇
④ 最后根据反馈对框架作一些调整,效果好的话,我也试试捐赠,咱们家的左小萌竟然也在搞捐赠,我以为我也该搞个!!!
框架主要用途仍是自我学习交流,以及框架思惟整理,BUG在所不免,如果有BUG请给我留言!
想简单看看demo的朋友请到: http://yexiaochai.github.io/blade/
对源码有兴趣的朋友请入:https://github.com/yexiaochai/blade
如果PC端浏览请使用chrome浏览器,不用谢我叫雷锋,如果只有IE的同窗请装chrome暂时抛弃IE
因为作是我的框架,而且是学习,框架便抛弃了一些系统如今支持状况是
IOS6+
android4+
那些手机不过千的浏览器上渲染可能会有问题,至于wp或者低版本兼容,小钗便须要看后续精力与工做强度
该框架第一期的目标是简单的webapp MVC的实现,如今也基本实现了,app支持hashChange与pushState两种方式作路由加载view
对此有兴趣的同窗能够看看helloWord,关于app与页面级View的关系以下:
框架第二期的想法是,完善自己一些通用的东西,好比UI组件或者简单的flip手势工具等
这里小钗不是专业的前端,就直接从线上将公司的CSS Down下来了,也用了他的Dom结构 可是
整个组件的扩展很是方便,有兴趣的同窗看看UI一块的代码,UI的继承关系以下:
框架第三期目标是实现前端ABTesting方案
框架第四期的目标是一套业务代码,能够同时用于mobile与ipad
框架第五期目标是实现Hybrid交互适配,因为小钗自己不懂native开发因此此方案要靠后
框架还会单开一个频道作一些疑难杂症处理,好比: ① fixed问题 ② 区域滚动问题 ③ app唤醒 ④ History路径问题等
有些项目文件或者不相关的文件咱们不予关注,整个框架代码在blade文件目录中主要
demo在demo中......
dest为框架打包后的文件
doc用于存放一些文档,暂时还未补齐,个人想法是博客做为文档,由于后期会源码解析,并分析设计思惟
grunt存放着打包文件
helloworld为最简单的入门代码,教咱们如何进入webapp开发
res存放资源样式文件,这里是直接用的别人的
test用于存放测试用例,固然如今不少用例未加上......
要进入webapp开发很是简单,只须要将代码下载下来便可,其中helloworld异常简单
blade框架每一个页面为一个页面片,一个页面片对应一个js文件,而且可能会对应一个html模板文件
一个频道的运行只须要这些文件便可(其实有点多的),最外层的index.html是基本入口
1 <!doctype html> 2 <!--[if (gte IE 9)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--> 3 <html class="ie"> 4 <!--<![endif]--> 5 <html> 6 <head> 7 <meta charset="utf-8" /> 8 <title>hello world</title> 9 <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 10 <meta content="telephone=no" name="format-detection" /> 11 <meta name="apple-mobile-web-app-capable" content="yes" /> 12 <link href="../res/style/style.css" rel="stylesheet" type="text/css" /> 13 14 </head> 15 <body onselectstart="return false"> 16 17 <script src="../blade/libs/require.js" type="text/javascript"></script> 18 19 <script src="../blade/libs/zepto.js" type="text/javascript"></script> 20 <script src="../blade/libs/underscore.js" type="text/javascript"></script> 21 <script src="../blade/libs/underscore.extend.js" type="text/javascript"></script> 22 <script src="../blade/libs/fastclick.js" type="text/javascript"></script> 23 24 <script src="../blade/common.js" type="text/javascript"></script> 25 <script type="text/javascript" src="./main.js"></script> 26 </body> 27 </html>
他这里引入了源码文件,我这里未作任何处理,实际使用请参考demo的dest.html
能够看到,js入口为对应的main.js:
1 (function () { 2 3 window.getViewTemplatePath = function (path) { 4 return 'text!helloworld/templates/' + path + '.html'; 5 } 6 7 require.config({ 8 baseUrl: '../', 9 paths: { 10 } 11 }); 12 13 require(['AbstractApp'], function (App) { 14 //实例化App 15 var app = new App({ 16 'defaultView': 'index', 17 'viewRootPath': 'helloworld/views/' 18 }); 19 20 }); 21 })();
那里引入了咱们全局抽象App,并进行了实例化,这个时候整个代码便开始运行了,这里默认加载的是index View
1 define(['AbstractView', getViewTemplatePath('index')], function (View, viewhtml) { 2 return _.inherit(View, { 3 onCreate: function () { 4 this.$el.html(viewhtml); 5 console.log('你们好,我是omCreate事件,我会执行而且只会执行一次'); 6 }, 7 8 events: { 9 'click h2': function (e) { 10 this.forward('list'); 11 }, 12 'click .icon_home': function () { 13 window.location = '../index.html'; 14 } 15 }, 16 17 onPreShow: function () { 18 console.log('你们好,我是onPreShow事件,我每次都会执行,执行最后执行turning方法即可显示view'); 19 this.turning(); 20 }, 21 22 onShow: function () { 23 console.log('你们好,我是onShow事件,我在onPreShow执行turning后会执行'); 24 25 }, 26 27 onHide: function () { 28 console.log('你们好,我是onHide事件,每次切换我将隐藏时候我会触发'); 29 } 30 31 }); 32 });
咱们每一个View皆是继承至MVC中的View,对于业务层的View只须要关注上面四个事件点便可,描述文字写的比较清楚了,我这里便很少说了
这个时候直接运行index.html便会默认加载index.js的逻辑代码,webapp卡片设计由此开始
demo中会列举出框架中的一些UI组件与一些通用功能的API,并作简单示例,一个框架是否好用的一个标识即是其API是否丰富好用
blade这里还须要不少努力,这里还望各位多多提点
稍微成熟点的公司通常不会使用什么第三方组件,什么创业团队就最喜欢使用第三方的插件,或者通用功能,我原来就见过一个公司:
① 须要一个弹出层组件便引用国外的,1000-2000行代码,功能很是全面
② 须要一个flip手势工具也引用一个第三方的,代码500-2000行,功能很是全面
可是一来程序员抓不住其中的关键,一旦遇到不能适应项目的功能便完蛋了,这个时候对程序员的水准要求便高了可能须要源码修改
每每彻底读懂后却发现本身只是须要其中的一点点功能而已,这个时候颇有可能会重写,或者寻找另外的库,因此只有2B公司才会彻底依赖什么第三方库
通常放出来的库文件,都是十分全面,会知足不少场景,可是你只会用到一个,或者你基本适用,可是总有一个两个地方不适用,那么怎么办呢?再重新引入一个第三方库么???
这样的话产品前端代码便会愈加臃肿难以维护,而且BUG无数,又烂又慢。
算了,这里扯远了,回到demo中来,demo中的代码逻辑便稍微复杂一点了,首先他存在着两个入口文件,分别是:
① debug.html 用于调试,其中的全部文件皆是源码形式
② dest.html 这个文件稍有不一样,其使用的框架文件和控制器js文件所有被grunt打包好了,咱们这里不存在Ajax数据请求,因此整个浏览不会发生请求延时,按道理来讲比较快
dest目录即是装的压缩后的文件,咱们其实将全部相关文件所有打包至了其中的dest/main.js,包括模板文件,这个多亏grunt与requireJS之功
整个demo的设计咱们放到下一章节再说,这里简单描述下,其中比较关键的是ex_mvc中的文件,顾名思义,他表明对框架mvc的扩展
扩展的基石是继承,业内前端面试一道必考提即是继承,可是前端真正使用该技术的人感受很少,不少前端都喜欢堆代码,还停留在面向过程。
blade这里便对js的继承作了一个很好的示例,但愿对js的继承、面向对象知识点有所了解的朋友,不妨看看两个MVC中的代码有何不一样
1 define(['AbstractView', 'cHighlight'], function (AbstractView, cHighlight) { 2 3 var hljs = new cHighlight(); 4 5 hljs.registerLanguage("javascript", function(a) { 6 return {aliases: ["js"],k: {keyword: "in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class",literal: "true false null undefined NaN Infinity",built_in: "eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require"},c: [{cN: "pi",b: /^\s*('|")use strict('|")/,r: 10}, a.ASM, a.QSM, a.CLCM, a.CBLCLM, a.CNM, {b: "(" + a.RSR + "|\\b(case|return|throw)\\b)\\s*",k: "return throw case",c: [a.CLCM, a.CBLCLM, a.REGEXP_MODE, {b: /</,e: />;/,r: 0,sL: "xml"}],r: 0}, {cN: "function",bK: "function",e: /\{/,c: [a.inherit(a.TM, {b: /[A-Za-z$_][0-9A-Za-z$_]*/}), {cN: "params",b: /\(/,e: /\)/,c: [a.CLCM, a.CBLCLM],i: /["'\(]/}],i: /\[|%/}, {b: /\$[(.]/}, {b: "\\." + a.IR,r: 0}]} 7 }); 8 hljs.registerLanguage("xml", function(a) { 9 var c = "[A-Za-z0-9\\._:-]+"; 10 var d = {b: /<\?(php)?(?!\w)/,e: /\?>/,sL: "php",subLanguageMode: "continuous"}; 11 var b = {eW: true,i: /</,r: 0,c: [d, {cN: "attribute",b: c,r: 0}, {b: "=",r: 0,c: [{cN: "value",v: [{b: /"/,e: /"/}, {b: /'/,e: /'/}, {b: /[^\s\/>]+/}]}]}]}; 12 return {aliases: ["html"],cI: true,c: [{cN: "doctype",b: "<!DOCTYPE",e: ">",r: 10,c: [{b: "\\[",e: "\\]"}]}, {cN: "comment",b: "<!--",e: "-->",r: 10}, {cN: "cdata",b: "<\\!\\[CDATA\\[",e: "\\]\\]>",r: 10}, {cN: "tag",b: "<style(?=\\s|>|$)",e: ">",k: {title: "style"},c: [b],starts: {e: "</style>",rE: true,sL: "css"}}, {cN: "tag",b: "<script(?=\\s|>|$)",e: ">",k: {title: "script"},c: [b],starts: {e: "<\/script>",rE: true,sL: "javascript"}}, {b: "<%",e: "%>",sL: "vbscript"}, d, {cN: "pi",b: /<\?\w+/,e: /\?>/,r: 10}, {cN: "tag",b: "</?",e: "/?>",c: [{cN: "title",b: "[^ /><]+",r: 0}, b]}]} 13 }); 14 hljs.registerLanguage("markdown", function(a) { 15 return {c: [{cN: "header",v: [{b: "^#{1,6}",e: "$"}, {b: "^.+?\\n[=-]{2,}$"}]}, {b: "<",e: ">",sL: "xml",r: 0}, {cN: "bullet",b: "^([*+-]|(\\d+\\.))\\s+"}, {cN: "strong",b: "[*_]{2}.+?[*_]{2}"}, {cN: "emphasis",v: [{b: "\\*.+?\\*"}, {b: "_.+?_",r: 0}]}, {cN: "blockquote",b: "^>\\s+",e: "$"}, {cN: "code",v: [{b: "`.+?`"}, {b: "^( {4}|\t)",e: "$",r: 0}]}, {cN: "horizontal_rule",b: "^[-\\*]{3,}",e: "$"}, {b: "\\[.+?\\][\\(\\[].+?[\\)\\]]",rB: true,c: [{cN: "link_label",b: "\\[",e: "\\]",eB: true,rE: true,r: 0}, {cN: "link_url",b: "\\]\\(",e: "\\)",eB: true,eE: true}, {cN: "link_reference",b: "\\]\\[",e: "\\]",eB: true,eE: true,}],r: 10}, {b: "^\\[.+\\]:",e: "$",rB: true,c: [{cN: "link_reference",b: "\\[",e: "\\]",eB: true,eE: true}, {cN: "link_url",b: "\\s",e: "$"}]}]} 16 }); 17 hljs.registerLanguage("css", function(a) { 18 var b = "[a-zA-Z-][a-zA-Z0-9_-]*"; 19 var c = {cN: "function",b: b + "\\(",e: "\\)",c: ["self", a.NM, a.ASM, a.QSM]}; 20 return {cI: true,i: "[=/|']",c: [a.CBLCLM, {cN: "id",b: "\\#[A-Za-z0-9_-]+"}, {cN: "class",b: "\\.[A-Za-z0-9_-]+",r: 0}, {cN: "attr_selector",b: "\\[",e: "\\]",i: "$"}, {cN: "pseudo",b: ":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"}, {cN: "at_rule",b: "@(font-face|page)",l: "[a-z-]+",k: "font-face page"}, {cN: "at_rule",b: "@",e: "[{;]",c: [{cN: "keyword",b: /\S+/}, {b: /\s/,eW: true,eE: true,r: 0,c: [c, a.ASM, a.QSM, a.NM]}]}, {cN: "tag",b: b,r: 0}, {cN: "rules",b: "{",e: "}",i: "[^\\s]",r: 0,c: [a.CBLCLM, {cN: "rule",b: "[^\\s]",rB: true,e: ";",eW: true,c: [{cN: "attribute",b: "[A-Z\\_\\.\\-]+",e: ":",eE: true,i: "[^\\s]",starts: {cN: "value",eW: true,eE: true,c: [c, a.NM, a.QSM, a.ASM, a.CBLCLM, {cN: "hexcolor",b: "#[0-9A-Fa-f]+"}, {cN: "important",b: "!important"}]}}]}]}]} 21 }); 22 hljs.registerLanguage("json", function(a) { 23 var e = {literal: "true false null"}; 24 var d = [a.QSM, a.CNM]; 25 var c = {cN: "value",e: ",",eW: true,eE: true,c: d,k: e}; 26 var b = {b: "{",e: "}",c: [{cN: "attribute",b: '\\s*"',e: '"\\s*:\\s*',eB: true,eE: true,c: [a.BE],i: "\\n",starts: c}],i: "\\S"}; 27 var f = {b: "\\[",e: "\\]",c: [a.inherit(c, {cN: null})],i: "\\S"}; 28 d.splice(d.length, 0, b, f); 29 return {c: d,k: e,i: "\\S"} 30 }); 31 32 33 return _.inherit(AbstractView, { 34 35 propertys: function ($super) { 36 $super(); 37 38 }, 39 40 _initHead: function () { 41 42 this.$('header').append($('<i class="returnico i_bef"></i>')); 43 }, 44 45 events: { 46 'click .returnico': function () { 47 this.back('index'); 48 } 49 }, 50 51 initialize: function ($super, app, id) { 52 $super(app, id); 53 54 this._initHead(); 55 }, 56 57 onPreShow: function ($super) { 58 $super(); 59 60 }, 61 62 show: function ($super) { 63 $super(); 64 65 hljs.initHighlighting(this); 66 67 } 68 69 70 }); 71 72 });
这里代码比较简单,主要是继承至AbstractView,而后作了demo项目的一些通用工做
其中有一大段代码是对代码高亮作处理的,这里小钗可耻的引用了一个第三方库。。。。。。
实现继承的代码在underscore.extend中,这段代码须要各位仔细研读,是本项目的基石。
//继承相关逻辑 (function () { // 全局可能用到的变量 var arr = []; var slice = arr.slice; /** * inherit方法,js的继承,默认为两个参数 * * @param {function} origin 可选,要继承的类 * @param {object} methods 被建立类的成员,扩展的方法和属性 * @return {function} 继承以后的子类 */ _.inherit = function (origin, methods) { // 参数检测,该继承方法,只支持一个参数建立类,或者两个参数继承类 if (arguments.length === 0 || arguments.length > 2) throw '参数错误'; var parent = null; // 将参数转换为数组 var properties = slice.call(arguments); // 若是第一个参数为类(function),那么就将之取出 if (typeof properties[0] === 'function') parent = properties.shift(); properties = properties[0]; // 建立新类用于返回 function klass() { if (_.isFunction(this.initialize)) this.initialize.apply(this, arguments); } klass.superclass = parent; // 父类的方法不作保留,直接赋给子类 // parent.subclasses = []; if (parent) { // 中间过渡类,防止parent的构造函数被执行 var subclass = function () { }; subclass.prototype = parent.prototype; klass.prototype = new subclass(); // 父类的方法不作保留,直接赋给子类 // parent.subclasses.push(klass); } var ancestor = klass.superclass && klass.superclass.prototype; for (var k in properties) { var value = properties[k]; //知足条件就重写 if (ancestor && typeof value == 'function') { var argslist = /^\s*function\s*\(([^\(\)]*?)\)\s*?\{/i.exec(value.toString())[1].replace(/\s/i, '').split(','); //只有在第一个参数为$super状况下才须要处理(是否具备重复方法须要用户本身决定) if (argslist[0] === '$super' && ancestor[k]) { value = (function (methodName, fn) { return function () { var scope = this; var args = [ function () { return ancestor[methodName].apply(scope, arguments); } ]; return fn.apply(this, args.concat(slice.call(arguments))); }; })(k, value); } } //此处对对象进行扩展,当前原型链已经存在该对象,便进行扩展 if (_.isObject(klass.prototype[k]) && _.isObject(value) && (typeof klass.prototype[k] != 'function' && typeof value != 'fuction')) { //原型链是共享的,这里处理逻辑要改 var temp = {}; _.extend(temp, klass.prototype[k]); _.extend(temp, value); klass.prototype[k] = temp; } else { klass.prototype[k] = value; } } if (!klass.prototype.initialize) klass.prototype.initialize = function () { }; klass.prototype.constructor = klass; return klass; }; })();
总的来讲,各个view页面卡片的代码存放与views里面,对应的模板文件存放与templates中,具体原理咱们后面点说明
这里入口的js文件为main.js,这个是最为关键一个js,咱们将里面的动画相关逻辑去掉,来看看主干代码:
1 (function () { 2 var project = 'demo/'; 3 4 window.getViewTemplatePath = function (path) { 5 return 'text!' + project + 'templates/' + path + '.html'; 6 } 7 8 require.config({ 9 baseUrl: '../', 10 paths: { 11 'View': project + 'ex_mvc/view' 12 } 13 }); 14 15 var animations = {}; 16 17 require(['AbstractApp'], function (App) { 18 //实例化App 19 var app = new App({ 20 //选择pushState仍是hashChange 21 hasPushState: false, 22 'defaultView': 'index', 23 'viewRootPath': '' + project + 'views/', 24 animations: animations 25 }); 26 27 $.bindFastClick && $.bindFastClick(); 28 29 }); 30 })();
核心代码是17-27行,这里首先作了一些初始化变量定义,而后使用requireJS引入了咱们的全局控制器,这个文件虽然不到400行,倒是整个框架的最大核心!
这里点一下即是,后续咱们会详解之,这里简单介绍全局控制器app应该干的事情
① 提供URL解析机制,以便让控制器能够根据URL得到当前是要加载哪一个view的实例,好比
http://www.baidu.com/index.html#index
http://www.baidu.com/index
如果使用hashChange实现浏览器跳转便直接取出index这个键值;
如果使用pushState方案的话,便须要业务同事给出取出URL键值的方法,最终咱们须要获得index这个键值
② app应该保留各个view的实例,而且维护一个队列关系
以如今博客园为例,咱们可能具备两个view页面片:index->detail
咱们首次即是加载index这个view,点击其中一个项目便加载detail这个view,这个时候app是应该同时保存两个view,而且内部要维系一个访问顺序队列
这个队列最好可与浏览器保存一致,若不能保存一致,后期即可能会出现点击浏览器后退死循环的问题
③ app应该提供view实例化的方法
因此的view实例若无特殊缘由,皆应该由app生成,app应该具备实例化view的能力,view通常使用AMD规范管理,这里涉及异步加载
PS:真实工做环境中,view须要自建一套事件机制,好比实例化时候要触发什么事件,显示时候要触发什么事件,皆须要有,app只会负责
实例化->显示->隐藏
④ app应该提供监控浏览器事件,每次自动加载各个view
如上面所述,app会注册一个hashChange事件或者popState事件以达到改变URL不刷新页面的功能,这个功能主要用于用户点击浏览器原生后退键
blade的构想是高扩展性,好比咱们核心的MVC的代码便可被继承扩展
不管是AbstractView仍是AbstractAPP或者AbstractModel,皆是可继承扩展的
咱们后面会根据须要以AbstractApp继承一个ipadAPP版本出来
咱们也会根据AbstractModel继承至十月localstorage缓存请求数据的版本
AbstractView的继承扩展更是屡见不鲜
以上是MVC的扩展,完了就是UI组件的扩展,简单由日历组件的demo来看
我只须要提供简单的实现,用户几行代码即可以实现假日日历,不少公司会出现价格日历,也仅仅是几行代码的事情
咱们能够继承下来作单独实现,也可在实例化时候传入数据解决,总之这里的观点即是
UI须要寻找共性,作抽象,而不是重复编码
再以弹出层类为例:
咱们的alert、toast、bubble.layer(冒泡层),皆是继承至ui.layer,这样作的好处是什么呢?
简单来讲他们具备如下共性:
① 都须要mask蒙版,也能够不具备
② 点击mask可关闭
③ 居中排列
④ 以绝对定位的形式出现
因而咱们能够轻易实现这样的代码:
1 return _.inherit(UIView, { 2 3 //默认属性 4 propertys: function ($super) { 5 $super(); 6 this.mask = new UIMask(); 7 8 //须要蒙版 9 this.needMask = true; 10 11 //须要点击蒙版删除 12 this.maskToHide = true; 13 14 //须要居中定位 15 this.needReposition = true; 16 17 }, 18 19 initialize: function ($super, opts) { 20 $super(opts); 21 22 this.clearRes(); 23 }, 24 25 //资源清理 26 clearRes: function () { 27 // if (this.needMask == false) this.mask = null; 28 }, 29 30 addEvent: function () { 31 this.on('onCreate', function () { 32 this.$el.addClass('cui-layer'); 33 }); 34 35 this.on('onPreShow', function () { 36 var scope = this; 37 38 if (this.needMask) { 39 this.mask.show(); 40 } 41 42 if (this.needMask && this.maskToHide) { 43 //mask显示以前为mask绑定关闭事件,一次执行便不予理睬了 44 this.mask.$el.on('click.uimask' + this.mask.id, function () { 45 scope.hide(); 46 }); 47 } 48 49 }); 50 51 this.on('onShow', function () { 52 if (this.needReposition) this.reposition(); 53 this.setzIndexTop(); 54 }); 55 56 this.on('onHide', function () { 57 this.mask.$el.off('.uimask' + this.mask.id); 58 this.mask.hide(); 59 60 }); 61 62 }, 63 64 //弹出层类垂直居中使用 65 reposition: function () { 66 this.$el.css({ 67 'margin-left': -($(this.$el).width() / 2) + 'px', 68 'margin-top': -($(this.$el).height() / 2) + 'px' 69 }); 70 } 71 72 });
至于气泡弹层固然不是居中的,因而气泡弹层中只须要重写掉repositon接口罢了
前端常常会提到继承,常常会提到闭包,可是到底如何使用继承,或者闭包是什么,仍是必须真正使用才能掌握
如果不使用就不会真正明白,那么如何成为优秀的前端,如何成为前端架构只是空想!更何论站在顶端呢?
PS:固然,小钗这里有点扯淡,并非说小钗是高手,其实我在公司也只是小码农
好了,扯着扯着,就扯多了,今天暂时到此,其实也没有什么好介绍的,代码在那里各位本身看吧,如果有什么问题请给位留言
最后,各位若能点一下推荐,或者在git中为我加一颗心便善莫大焉了
后面点,小钗会对框架进行源码解读,但愿对入门级的前端朋友有所帮助,也不知道各位是否感兴趣