【blade利刃出鞘】一起進入移動端webapp開發吧

前言

在移動浪潮襲來的時候,小釵有幸進入框架組做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的關系如下:

Toast UML

第二期-通用工具

框架第二期的想法是,完善本身一些通用的東西,比如UI組件或者簡單的flip手勢工具等

這里小釵不是專業的前端,就直接從線上將公司的CSS Down下來了,也用了他的Dom結構 但是

整個組件的擴展非常方便,有興趣的同學看看UI一塊的代碼,UI的繼承關系如下:

Toast UML

第三期-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 Code

我們每個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 });
View Code

這里代碼比較簡單,主要是繼承至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   });
View Code

至于氣泡彈層當然不是居中的,于是氣泡彈層中只需要重寫掉repositon接口罷了

前端經常會提到繼承,經常會提到閉包,但是到底如何使用繼承,或者閉包是什么,還是必須真正使用才能掌握

若是不使用就不會真正明白,那么如何成為優秀的前端,如何成為前端架構只是空想!更何論站在頂端呢?

PS:當然,小釵這里有點扯淡,并不是說小釵是高手,其實我在公司也只是小碼農

結語

好了,扯著扯著,就扯多了,今天暫時到此,其實也沒有什么好介紹的,代碼在那里各位自己看吧,若是有什么問題請給位留言

最后,各位若能點一下推薦,或者在git中為我加一顆心便善莫大焉了

后面點,小釵會對框架進行源碼解讀,希望對入門級的前端朋友有所幫助,也不知道各位是否感興趣

轉載于:https://www.cnblogs.com/yexiaochai/p/3837713.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/376829.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376829.shtml
英文地址,請注明出處:http://en.pswp.cn/news/376829.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

題目理解。。

有時候一道大水題&#xff0c;因為英文描述可能有點復雜或者其它云云&#xff0c; 就比如那道PRO。。 別先被一道題嚇一跳&#xff0c;&#xff0c;&#xff0c; 英文描述看深入一點總歸會能解決的&#xff0c;盡可能多的考慮情況。大膽嘗試。轉載于:https://www.cnblogs.com/c…

C++多線程快速入門(四)shared_mutex以及讀寫鎖應用

在前面的三講中我們使用的mutex都是普通的std::mutex&#xff0c;這里介紹一下shared_mutex&#xff0c;版本為C17 std::shared_mutex的底層實現時操作系統提供的讀寫鎖&#xff0c;在讀多寫少的情況下&#xff0c;該shared_mutex比mutex更加高效。 它提供了常用的四種方法&am…

Tornado/Python 學習筆記(一)

1.源代碼下載及安裝&#xff1a;http://www.tornadoweb.org/en/stable/ 2.python中xmlrpc庫官方文檔&#xff1a;https://docs.python.org/3/library/xmlrpc.html?highlightxmlrpc 3.xml介紹與學習&#xff1a;http://www.w3school.com.cn/xml/xml_intro.asp XML 被設計為傳輸…

spring-aop-annotation

1。假設你已經配好依賴注入那一塊。此時的bean.xml為 <?xml version"1.0" encoding"UTF-8"?><beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xmlns:cont…

c++實現簡單線程池代碼

目錄完整代碼TaskPool.cppTaskPool.hmain.cpp完整代碼 TaskPool.cpp // // Created by LENOVO on 2021/10/25. //#include "TaskPool.h" #include <functional>std::mutex printMutex;TaskPool::TaskPool() : m_bRunning(false) {}TaskPool::~TaskPool() {re…

Android靜態圖片人臉識別的完整demo(附完整源碼)

Demo功能&#xff1a;利用android自帶的人臉識別進行識別&#xff0c;標記出眼睛和人臉位置。點擊按鍵后進行人臉識別&#xff0c;完畢后顯示到imageview上。 第一部分&#xff1a;布局文件activity_main.xml [html] view plaincopyprint?<RelativeLayout xmlns:android&qu…

圖論:最短路徑搜索--Dijkstra算法(c代碼實現)

最近因為辭職&#xff0c;有不少閑功夫&#xff0c;重溫下數據結構&#xff0c;順便練練手。今天說說最短路徑搜索算法中的Dijkstra原理和實現。 一&#xff1a;簡介 這個算法用于解決圖中單源最短路徑問題。所謂單源節點是指給定源節點&#xff0c;求圖中其它節點到此源節點的…

C++多線程快速入門(五)簡單線程池設計

目錄設計思路主線程運行邏輯task以及taskpool設計詳細流程講解完整代碼打印結果往期回顧設計思路 線程池實際上就是一組線程&#xff0c;當我們需要異步執行一些任務時&#xff0c;經常要通過OS頻繁創建和銷毀線程&#xff0c;不如直接創建一組在程序生命周期內不會退出的線程…

C--函數

函數:具有特定功能的代碼段,分為庫函數,自定義函數. 函數定義: 函數返回值類型 函數名(形式參數列表) { 代碼段; return 返回值; } 注意:每個函數返回值最多只有一個.return是一個函數結束的標志. 形式參數(形參):函數定義時使用的虛擬參數名,用以接收函數調用是傳遞過來的實際…

公式系統 - TradeBlazer公式基礎 - Bar數據

Bar數據 在介紹Bar數據之前&#xff0c;首先&#xff0c;我們需要討論一下TradeBlazer公式的計算方法&#xff0c;針對上面介紹的各種公式類型&#xff0c;包含公式應用&#xff0c;在公式進行計算時&#xff0c;都是建立在基本數據源(Bar數據)之上&#xff0c;我們這里所謂的B…

C++網絡編程快速入門(一):TCP網絡通信基本流程以及基礎函數使用

目錄流程概述服務器端代碼實現客戶端代碼實現函數和結構講解sockaddr_in和sockaddrsocket &#xff1a; 創建一個socket連接bind &#xff1a;綁定地址以及端口號問題流程概述 客戶端與服務器之間的網絡通信基本原理如下所示&#xff0c;復雜一點的架構可能會添加消息中間件。…

php 字符串處理

addcslashes — 為字符串里面的部分字符添加反斜線轉義字符addslashes — 用指定的方式對字符串里面的字符進行轉義bin2hex — 將二進制數據轉換成十六進制表示chop — rtrim() 的別名函數chr — 返回一個字符的ASCII碼chunk_split — 按一定的字符長度將字符串分割成小塊conve…

使用前端框架Foundation 4來幫助簡化響應式設計開發

日期&#xff1a;2013-3-12 來源&#xff1a;GBin1.com Foundation是一套使用廣泛的前端開發套件&#xff0c;可以幫助你快速的網站。最近ZURB發布了一個新版本的Foundation 4前端框架&#xff0c;能夠有效的幫助你快速的開發響應式的網站。 和另外一個套知名的前端框架BootSt…

C++網絡編程快速入門(二):Linux下使用select演示簡單服務端程序

目錄select參數解釋select使用規范select使用缺點基本流程實例代碼通信效果演示往期文章select參數解釋 extern int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout)…

Android轉載一:Android文件命名規范

REF&#xff1a;http://blog.csdn.net/gulianchao/article/details/23391651 (一) Layout命名 1&#xff0e;contentview命名&#xff1a;activity_功能模塊.xml 例如&#xff1a;activity_main.xml、activity_more.xml 2&#xff0e;Dialog命名&#xff1a;dialog_描述.xml …

[轉]XBRL應用軟件分類

1) 分類標準編輯軟件(Taxonomy editor)&#xff1a; 分類標準是XBRL技術的應用基礎&#xff0c;每一個采用XBRL技術的國家都必須先按各國的GAAP制訂XBRL分類標準&#xff0c;上市公司才能據以編制實例文件。由于一套XBRL 2.0或2.1版分類標準必須包含至少一份XML Schema文…

C++網絡編程快速入門(三):阻塞與非阻塞式調用網絡通信函數

目錄阻塞與非阻塞定義send與recvconnect一些問題為什么要將監聽socket設置為非阻塞阻塞與非阻塞定義 阻塞模式指的是當前某個函數執行效果未達預期&#xff0c;該函數會阻塞當前的執行線程&#xff0c;程序執行流在超時時間到達或者執行成功后恢復原有流程。非阻塞模式相反&am…

css3實現頭像旋轉360度

css樣式: .div a img{ width: 88px; height: 88px; border-radius: 88px; transition: all 1.2s ease-out 0s;}.div a img:hover{ -webkit-transform:rotate(360deg); -moz-transform:rotate(360deg); -o-transform:rotate(360deg); -ms-transform:rotate(360deg); transform:r…

POJ 2488 深搜

POJ 2488 深搜 要求字典序的順序。 1 #include <iostream>2 #include <stdio.h>3 #include <string.h>4 using namespace std;5 int n,m,cnt;6 bool success;7 bool sign[30][30];8 int step[30][2];9 int dir[8][2]{ 10 -2,-1,-2,1, 11 …

socket 端口和地址復用

https://blog.csdn.net/weibo1230123/article/details/79978745 https://blog.csdn.net/weixin_42157432/article/details/115560824 在linux socket網絡編程中&#xff0c;大規模并發TCP或UDP連接時&#xff0c;經常會用到端口復用&#xff1a; int opt 1; if (setsockopt…