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

前言

在移動浪潮襲來的時候,小釵有幸進入框架組作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或者低版本兼容,小釵便須要看後續精力與工做強度

框架發展

第一期-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中爲我加一顆心便善莫大焉了

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

相關文章
相關標籤/搜索