哪些开始一个模块化可扩大的Web App

则于没有看好是一个前端开发者,但不知不觉中为积累下了部分前端开发的经验。正巧之前遇到一道面课题,于是便顺手梳理了一下祥和关于Web
App的有思路并整理为本文。

对此群简易的网站或者Web应用来说,引入jQuery以及部分插件,在当前页面内写副简单逻辑已经可以满足大部分亟需。但是倘若一旦多人开,应用之复杂程度上升,就见面起过多题材开展露出来:

  1. 数据源一般都跟页面分离,那么App启动一般都要等待数据源读入。
  2. UI交互复杂时,需要将逻辑通过面向对象抽象后才会重新好之复用。
  3. 功效间一般还存在依靠关系,需要引入支持因关系的模块加载器。

那怎样化解这些题目,就因为一个粗略的订餐App为条例,从零开始一个模块化可扩大Web
App。

这个大概的App基于HTML5 Boilerplate、requireJS、jQuery
Mobile、Underscore.js,后端逻辑用jStorage宪章实现。完成后底活在此。所有代码可以在github查看。下文将相继介绍实现之笔触与措施。

从选择一个好模板开始

初步一个Web项目,HTML的开总是要,一个吓的HTML能自自上规避大量私问题,所以Web
App应该全下一个尺码的大质量HTML模板,而休是用富有页面及由开发人员自由发挥。

这边推荐用HTML5
Boilerplate项目当App的默认模板与文件路径规范,无论是网站要富UI的App,都得以应用这模板作为启动。

可利用

git clone git://github.com/h5bp/html5-boilerplate.git

还是直接下载HTML5
Boilerplate项目代码。HTML5
Boilerplate的文本结构如下,

.
├── css
│   ├── main.css
│   └── normalize.css
├── doc
├── img
├── js
│   ├── main.js
│   ├── plugins.js
│   └── vendor
│       ├── jquery.min.js
│       └── modernizr.min.js
├── .htaccess
├── 404.html
├── index.html
├── humans.txt
├── robots.txt
├── crossdomain.xml
├── favicon.ico
└── [apple-touch-icons]

从上向下看

  • css
    用于存放css文件,并坐了Normalize.css作默认CSS重置手段(其实Normalize.css不可知算是CSS
    reset)。
  • doc 存放项目文档
  • img 存放项目图片
  • js 存放javascript文件,其中老三方类库推荐在vendor
  • .htaccess
    内置了众于静态文件于Apache下的优化策略,如果Web服务器不是Apache则足以参见另外Web服务器配置优化。
  • 404.html 默认的404页面,
  • index.html 项目模板
  • humans.txt
    相对于面向机器人之robots.txt,humans.txt更像是多少幽默,这在里好写关于项目/团队之介绍,或者放置有彩蛋给那些喜欢对您的动刨根问底的用户们。
  • robots.txt 用于告诉搜索引擎蜘蛛爬行规则
  • crossdomain.xml
    用于配置Flash的跨域策略
  • favicon.ico apple-touch-icon.png等小图标。

如果是一个主要面向移动装备,还有更拥有针对性的Mobile
Boilerplate可供参考。

制订统一的编码规范

当专业开班编码之前,无论是多大局面之采取,多少人口的团体,一定要产生一个统一的业内,才能够确保后续之开销不见面乱套。

前者规范其实以要分成三有的:HTML、CSS、Javascript应该分别发生好的正儿八经。HTML/CSS主要约定id/class的命名规则、属性之写顺序。JavaScript可能需要细化到缩进、编码风格、面向对象写法等等。

最为简便易行之办法自然要参考已部分规范,比如Google的HTML/CSS风格指南、Google的Javascript编码指南等等。

HTML篇

HTML5
Boilerplate的模版核心组成部分可30实施,但是各个一行都可谓千锤百炼,可以用极小之吃解决有前端的执着问题:

应用口径注释区分IE浏览器

<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->

因而要这么描绘

  1. 好采用class作为全局条件区分低版本的IE浏览器并开展调整,这肯定要优惠使用CSS
    Hack。
  2. 好避IE6条件注释引起的高版本IE文件阻塞问题,原文的化解措施是于眼前加一个空手的原则注释,但是这里肯定拿原无用空白的尺码注释变得有意义了。
  3. 还可通过HTML验证。
  4. 与Modernizr等特性检测类库使用同样之class,更兼具通用性。

no-js标签是用同Modernizr等类库配合使用的,如果你免思量当档次遭到引入Modernizr,需要在Head部分投入一行要no-js标签变为js,代码来Avoiding
the
FOUC:

 <script>(function(H){H.className=H.className.replace(/\bno-js\b/,'js')})(document.documentElement)</script>

通过者的规格注释,就得于CSS中针对不同状况分别处理

.lt-ie7 {} /* IE6等版本时 */
.no-js {} /* JavaScript没有启用时 */

meta标签的修顺序

以吃浏览器识别是的编码,meta charset标签应该先于title标签出现。

meta
X-UA-Compatible标签可以指定IE8以上版本浏览器为最好尖端模式渲染文档,同时如果都设置Google
Chrome
Frame虽直下Chrome
Frame渲染。而指定渲染模式之meta
X-UA-Compatible标签同样需事先出现

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>

安移动装备显示窗口宽度

<meta name="viewport" content="width=device-width">

立马是挪装备专属的价签,具体装需要基于项目实际上状况调整。

动用Modernizr做浏览器差异检测

Modernizr时举行前端的该还不生疏。引入Modernizr后,html标签的no-js拿会让活动替换为js,同时Modernizr会向html标签添加代表本检测结果的class。

对于没有版本浏览器的迈入兼容需要基于项目实际要求处理,Modernizr也格外完美的吃闹之大多数HTML5作用的匹配方法。

CSS篇

CSS重置及提高效能

HTML5
Boilerplate选择Normalize.css重置CSS。如果项目计划引入Twitter
Bootstrap、YUI
3这些前端框架的说话则可以移除,因为这些框架已放了Normalize.css。

而HTML5
Boilerplate又引入了一个main.css,内置了有些核心的排版样式和打印样式。

使用LESS或Sass生成CSS

当千头万绪应用被,如果还亲手写CSS的讲话将凡一律项痛苦之政工,大量的class前缀,复用样式需要来回copy等等。为了重新好之扩展性,这里建议于路面临引入LESS或Sass。这象征正在:

  • 支持变量和简短运算
  • 支持CSS片段复用
  • class/id样式嵌套

齐片段再次像是编程语言的风味。这对于增强开发效率是成效很明确的。

坐LESS为条例,简单介绍一下LESS在Windows下如何使到这类型被:

  1. 下载Nodejs连安装,nodejs会自动将自己在系路径。
  2. 在cmd运行
    npm install -g less
  3. 然后便可以经过lessc指令将less源文件编译为css
    lessc avnpc.less avnpc.css
  4. 比方非采取nodeJs作为后端,最好当写LESS时使用watch模式,每次保存自动编译为css。这里要设置一个帮模块recess:
    npm install -g recess
    然后运行watch
    recess avnpc.less:avnpc.css --watch

Javascript篇

动requireJS按需要加载

模块加载器的定义可能略接触了前端开发的童鞋都不见面生,通过模块加载器可以使得之化解这些题目:

  1. JS文件之仗关系。
  2. 由此异步加载优化script标签引起的堵截问题
  3. 好大概的坐文件也单位用功能模块化并落实复用

主流的JS模块加载器有requireJS,SeaJS顶,加载器之间或会见坐据的专业不同来神秘的歧异,从纯粹用户之角度出发,之所以选requireJS而未是SeaJS主要是盖:

  • 功用实现上两者相差无几,没有明白的性质差异或要害问题。
  • 文档丰富程度及,requireJS远远好为SeaJS,就以最简易的加载jQuery和jQuery插件这回事,虽然双方的落实方式相差无几,但requireJS就生好直接以来用底Demo,SeaJS还要读文档自己逐渐折腾。一些题目之缓解上,requireJS为要词吗再易找到答案。

requireJS 加载jQuery + jQuery插件

唯恐对于一般Web
App来说,引入jQuery及有关插件的票房价值是绝可怜之,requireJS也近的被出了对应的缓解方案及动态加载jQuery及插件的文档及实例代码。

每当风靡的jQuery1.9.X中,jQuery已经以结尾直接将协调注册为一个AMD模块,即是说可一直为requireJS作为模块加载。如果是加载旧本子的jQuery有有限种植艺术:

1. 让jQuery先于requireJS加载

2. 针对性jQuery代码稍做一点处理,在jQuery代码包裹一句子:

define(["jquery"], function($) {
    // $ is guaranteed to be jQuery now */
});

requireJS的演示中,直接以requireJS与jQuery合并为一个文书,如果是采取jQuery作为核心库底口舌推荐这种做法。

同样于jQuery插件来说也有些许种植艺术

1. 于插件外包裹代码

define(["jquery"], function($){
     // Put here the plugin code. 
});

2. 以动用reuqireJS代码加载前注册插件(比如当main.js)中

requirejs.config({
    "shim": {
        "jquery-cookie"  : ["jquery"]
    }
});

requireJS加载第三正在类库

以实例的App中尚因此到了jQuery以外的老三正值类库,如果类库不是一个正规的AMD模块而而未思改变这些类库的代码,同样需超前开展定义:

require.config({
      paths: {
            'underscore': 'vendor/underscore'
      },
      shim: {
          underscore: {
              exports: '_'
          }
      }
});

CSS文件的模块化处理

以requireJS中,模块的概念就限于JS文件,如果要加载图片、JSON等非JS文件,requireJS实现了一样系列加载插件。

而是遗憾之是requireJS官方没有针对性CSS进行模块化处理,而我辈以事实上项目受到却一再能够碰到有的情景,比如一个轮播的图样展示栏,比如高档编辑器等等。几乎拥有的富UI组件都见面出于JS与CSS两片构成,而CSS之间吧在在模块的定义以及凭借关系。

以更好的及requireJS整合,这里以require-css来解决CSS的模块化与因问题。

require-css是一个requireJS插件,下载后以css.jsnormalize.js放于main.js同级即可默认为加载,比如当咱们的种被需要加载jQuery
Mobile的css文件,那么得一直这样调用:

require(['jquery', 'css!../css/jquery.mobile-1.3.0.min.css'], function($) {
});

可是因为是CSS本质上是属jQuery
Mobile模块的同样有的,更好之做法是以之CSS文件之概念在jQuery
Mobile的指关系遇,最终我们的requireJS定义有也:

require.config({
      paths: {
            'jquerymobile': 'vendor/jquery.mobile-1.3.0',
            'jstorage' : 'vendor/jstorage',
            'underscore': 'vendor/underscore'
      },
      shim: {
          jquerymobile : {
            deps: [
                'css!../css/jquery.mobile-1.3.0.min.css'
            ]
          },
          underscore: {
              exports: '_'
          }
      }
});

当应用模块时,只需要:

require(['jquery', 'underscore', 'jquerymobile', 'jstorage'], function($, _) {
});

jQuery
Mobile的CSS文件就会见于电动加载,这样CSS与JS就为做为一个模块了。同理其他发出千丝万缕依赖关系之模块也可以举行类似处理,requireJS会解决因关系的逻辑。

数据源的加载与待

Web
App一般还见面动态加载后端平的数,数据格式一般可以是JSON、JSONP也得一直是一个JS变量。这里以JS变量为例

var restaurants = [
    {
        "name": "KFC"
    },
    {
        "name": "7-11"
    },
    {
        "name": "成都小吃"
    }
]

载入这段数据:

$.getScript('data/restaurants.json', function(e){
    var data = window.restaurants;
    alert(data[0].name); //KFC
});

纯净的数据源确实挺粗略,但是频繁一个使用中会时有发生多独数据源,比如当是实例App中UI就需载入用户信息、餐厅信息、订餐信息三栽多少后才会办事。如果单纯依赖多叠嵌套回调函数的语,可能代码的耦合就坏重了。

为了化解多单数据加载的问题,我习惯的解决措施是布局一个dataReady事件响应机制。

var foodOrder = {

    //数据载入后要执行的函数暂存在这里
    dataReadyFunc : []

    //数据源URL及载入状态
    , dataSource : [
        { url : 'data/restaurants.json', ready : false, data : null },
        { url : 'data/users.json', ready : false, data : null },
        { url : 'data/foods.json', ready : false, data : null }
    ]

    //检查数据源是否全部载入完毕
    , isReady : function(){
        var isReady = true;
        for(var key in this.dataSource){
            if(this.dataSource[key].ready !== true){
                isReady = false;
            }
        }
        return isReady;
    }

    //数据源全部加载完毕,则逐一运行dataReadyFunc中存放的函数
    , callReady : function(){
        if(true === this.isReady()){
            for(var key in this.dataReadyFunc){
                this.dataReadyFunc[key]();
            }
        }
    }

    //供外部调用,会将外部输入的函数暂存在dataReadyFunc中
    , dataReady : function(func){
        if (typeof func !== 'function') {
            return false;
        } 
        this.dataReadyFunc.push(func);
    }

    , init : function(){
        var self = this;
        var _initElement = function(key, url){
            $.getScript(url, function(e){
                //每次载入数据后,将数据存放于dataSource中,将ready状态置为true,并调用callReady
                self.dataSource[key].data = window[key];
                self.dataSource[key].ready = true;
                self.callReady();
            });
        }
        for(var key in this.dataSource){
            _initElement(key, this.dataSource[key].url);
        }
    }
}

用法为

foodOrder.dataReady(function(){
   alert(1);     
});
foodOrder.init();

dataReady内之alert将会当有着数据载入了后初步施行。

即段处理的逻辑并无复杂,将享有设实行之方式通过dataReady暂存起来,等待数总体加载了后又实行,更加扑朔迷离的观是道仍然通用。

使用JS模板引擎

数载入后,最终都见面为某种形式展示在页面及。简单情况,我们恐怕会见如此做:

$('body').append('<div>' + data.name + '</div>');

苟页面逻辑一旦复杂,比如用发出if判断或者多叠循环时,这种连字符串的办法就是相形见绌了,而及时为就是催生出了JS模板引擎。

主流的JS模板引擎起underscore.js,Jade,EJS等等,可以横向对比一下这些JS模板引擎的优缺点。

对于相对简便易行的页面逻辑(只需要支持if和for/each)来说,我重新赞成选用轻巧的underscore.js或者JavaScript
Templates。

以当下例中,使用underscore.js生成列表就非常简单了,页面模板也:

<ul data-role="listview" data-inset="true">
<script id="tmpl-restaurants" type="text/template">
    <% _.each(data, function(restaurant) { %>
        <li>
            <a href="#" data-rel="back" data-value="<%- restaurant.name%>"><%- restaurant.name%></a>
        </li>
    <% }); %>
</script>
</ul>

调用引擎

$("#tmpl-restaurants").replaceWith(
    _.template($("#tmpl-restaurants").html(), {
        data : restaurants
    })
);

面向对象与模块化

通过地方这些家伙的三结合,我们来了模块的概念,有了模版引擎,有多少的加载。最终还是如透过javascript将马上一体组织以一块儿并加入应用所待之逻辑。为了能够最好要命限度的复用代码,用面向对象的艺术去组织内容是比较好之选料。

JavaScript虽然原生并无支持面向对象,但是还得以由此多方式模拟出面向对象的性状。例子中以了自己个人于好的平等种植方式是:

var foodOrder = function(ui, options){
    //构造函数
    this.init(ui, options);
}
foodOrder.prototype = {
   defaultUI :  {
       form : '#form-order'
   }
   , defaultOptions : {
       debug : false
   }
   , init : function(ui, options){
       this.ui = $.extend({}, this.defaultUI, ui);
       this.options = $.extend({}, this.defaultOptions, options);
   }
}
var order = new foodOrder({
    form : '#real-form'
}, {
    debug : true
});

拿页面的UI元素以及安排型抽象出,在实际上组织对象时虽然足以通过输入参数复写,可以分开整个项目的逻辑和UI,使拍卖的法门进一步灵活。

相关文章