前言
在移動浪潮襲來的時候,小釵有幸進入框架組做webapp框架開發,過程中遇到了移動端的各種坑,也產生了各種激情,就我們公司的發展歷程來說
第一階段:使用傳統方式開發移動站點,少量引入HTML5元素
第二階段:框架化,使用jquery mobile框架,發現慢,組件不好管理,不好維護給搞掉了
第三階段:jquery+Backbone的組合,最后為了瘦身將jquery換成了zepto
第四階段:框架適應Hybrid版本,Hybrid相關頻道與H5站點一套代碼,業務擴展遍地開花
第五階段:框架適應ipad版本/實現前端ABTesting,由于不可控原因,計劃夭折
框架使用過程中需要快速適應業務需求,框架中過多的摻雜了業務代碼,并且隨之發展,框架本身的耦合度、設計不合理的地方也體現了出來
小釵雖然知道哪里有問題,但框架的代碼不是想象那么好改,一個API的改變會導致整個業務線的崩潰,聽之任之又很不爽
開發一套干凈webapp框架的想法油然而生,于是該框架便出現了
誠然,此框架比不上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或者低版本兼容,小釵便需要看后續精力與工作強度
框架發展
第一期-MVC
該框架第一期的目標是簡單的webapp MVC的實現,現在也基本實現了,app支持hashChange與pushState兩種方式做路由加載view
對此有興趣的同學可以看看helloWord,關于app與頁面級View的關系如下:
第二期-通用工具
框架第二期的想法是,完善本身一些通用的東西,比如UI組件或者簡單的flip手勢工具等
這里小釵不是專業的前端,就直接從線上將公司的CSS Down下來了,也用了他的Dom結構 但是
整個組件的擴展非常方便,有興趣的同學看看UI一塊的代碼,UI的繼承關系如下:
第三期-ABTesting
框架第三期目標是實現前端ABTesting方案
第四期-ipad適配
框架第四期的目標是一套業務代碼,可以同時用于mobile與ipad
第五期-Hybrid
框架第五期目標是實現Hybrid交互適配,由于小釵本身不懂native開發所以此方案要靠后
隨機期-疑難雜癥
框架還會單開一個頻道做一些疑難雜癥處理,比如: ① fixed問題 ② 區域滾動問題 ③ app喚醒 ④ History路徑問題等
目錄簡介
有些項目文件或者不相關的文件我們不予關注,整個框架代碼在blade文件目錄中主要
demo在demo中......
dest為框架打包后的文件
doc用于存放一些文檔,暫時還未補齊,我的想法是博客作為文檔,因為后期會源碼解析,并分析設計思維
grunt存放著打包文件
helloworld為最簡單的入門代碼,教我們如何進入webapp開發
res存放資源樣式文件,這里是直接用的別人的
test用于存放測試用例,當然現在很多用例未加上......
Helloworld
要進入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
是否引入第三方組件?
demo中會列舉出框架中的一些UI組件與一些通用功能的API,并做簡單示例,一個框架是否好用的一個標識便是其API是否豐富好用
blade這里還需要很多努力,這里還望各位多多提點
稍微成熟點的公司一般不會使用什么第三方組件,什么創業團隊就最喜歡使用第三方的插件,或者通用功能,我原來就見過一個公司:
① 需要一個彈出層組件便引用國外的,1000-2000行代碼,功能非常全面
② 需要一個flip手勢工具也引用一個第三方的,代碼500-2000行,功能非常全面
但是一來程序員抓不住其中的關鍵,一旦遇到不能適應項目的功能便完蛋了,這個時候對程序員的水準要求便高了可能需要源碼修改
往往完全讀懂后卻發現自己只是需要其中的一點點功能而已,這個時候很有可能會重寫,或者尋找另外的庫,所以只有2B公司才會完全依賴什么第三方庫
一般放出來的庫文件,都是十分全面,會滿足很多場景,但是你只會用到一個,或者你基本適用,但是總有一個兩個地方不適用,那么怎么辦呢?再從新引入一個第三方庫么???
這樣的話產品前端代碼便會越發臃腫難以維護,并且BUG無數,又爛又慢。
demo簡介
算了,這里扯遠了,回到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中為我加一顆心便善莫大焉了
后面點,小釵會對框架進行源碼解讀,希望對入門級的前端朋友有所幫助,也不知道各位是否感興趣