近日来我有幸主导了一个典型的web app开发。该项目从产品层次来说是个典型的CRUD应用,故而我毫不犹豫地采用了grunt + boilerplate + angularjs + bootstrap + D3 + requirejs 的架构来实现它。angularjs早在去年6月份我就有所接触,将它应用在实验室项目的个别页面中,11月份在新浪的时候也将其推荐给了所在云事业部项目组。项目组老大程辉等人都是很有技术敏感性的人,大胆地采纳了我的建议,将之应用于原本使用dojo开发的项目前端模块上。然而,由于前端模块很大,并且已经用dojo开发了很长时间了,故而angularjs一开始也是应用在一些页面和子模块上,至我离开新浪之前还完成全部的重构。虽然之后我也做过一些angularjs的插件之类的事,但终究没有用angularjs完整地搞一个大型项目的经验。故而这次实验室交予我一整个项目,且不管所得利益几何,对于一个对前端有爱的人而言,这是一次从头开始完整地进行angularjs项目实战的机会,我怎么可能放过。期间种种我都想用博客记录下来,以备后用。

开发大型javascript应用和做几个demo页面玩一玩最大的区别在于,要对一开始的文件组织和模块划分要有更清晰的认识。这项工作前期花了我较多的时间去处理。故而第一篇文章就写模块划分和目录组织,这方面本人经验不多,不足之处敬请指出。

项目需求和资源评估

没有什么模块划分是必须的和通用的,模块划分都是在对项目需求、手头资源(员工资源、时间资源和物质资源)有充分的评估,同时参考大量范型的基础上作出决定。所以先大致介绍一下我接手的项目需求。这是一个典型的B/S架构的呈报系统软件。要求有用户-服务台-办事人员-系统管理员四级的角色架构,针对不同的角色,显示不同界面,每种界面内含子页面大约20个不等,子页面互相有重复。需求要求系统有足够的稳定性,一般的并发性。前端要求有设计感,足够美观,快速响应,对一些特定统计内容有相应的可视化呈现。不需支持IE6。项目成型后,作为软件安装部署,二次开发可能性较小。人力资源方面,加上我干活的有三个,其中前端熟练工一个(俺),有一定开发经验的一个,编写代码一般可视化程序操作优良踏实肯干的一个。时间资源大约3人*60个工作日。

根据项目需求可知,项目从用户角色可划分为四个大模块:用户、服务台、办事人员、系统管理员。大部分页面逻辑较为单一,易于划分。对于重复子页面可另建立通用模块。考虑到人力方面,2/3是非熟练工,美工设计人员基本没有(本来我是,但是现在的工作量不允许我做这个了),工程量大时间紧,故而要求将设计的工作量降到最低。使用的技术和模块划分要尽量易上手,减少重复劳动,提高并发效率。由于前端人员较多,后台较为简单,故而整体架构上,后台只须提供REST风格的API,将工作重心前移,前台负责主要逻辑模块的实现。

前端初始架构设计

在确认了需求和资源评估之后,本人接下来的工作是在github上遍览使用了angularjs的各种项目,以及google相关文档。其中github上的几个project给我了我较大启发,它们分别是:

根据这些参考内容,我设计了初始的技术路线,如图所示:

简要说明一下:

web server使用的是node.js。同时配以自动化工具grunt做一些端到端测试、代码压缩和合并之类的工作。

framwork当然用的是angularjs做MVVM框架。同时用了一些UI小插件,如angular-ui-bootstrap, angular-ui和我自己写的表格插件angrid(https://github.com/zhangdiwaa/anGrid)。因为表格插件对于本项目来说比较重要,这里采用自己写的表格插件主要是为了便于定制(想当年为了定制dojo的表格插件改得要吐)。在模块划分上,一开始是模仿angular-seed的方式,就用一个myApp作为程序入口点,direcvtives、filters、service作为文件夹,存放对应的js文件。

基础的工具集采用jquery以及bootstrap。bootstrap这个东西的好处就在于可以快速地让不懂设计的人也能快速开发出能看的界面,搞定不挑剔的用户足够了。何况国内还有很多软件系统的界面远不如bootstrap好看。这里bootstrap我们没有使用其开发包而是直接使用它的工程包,并且摒弃了less。这样做因为考虑到需求我们并不需要对bootstrap做多少定制化处理。直接使用原始设计,将样式设计的工作量降到最低。less这个东西,只在需要非常频繁变更样式设计的时候方显本事,其他时候还不如直接用CSS。

其他工具集主要是前端数据可视化工具包D3,D3的强大无需说明。另外把boilerplate单独说一下是因为这东西我确实很喜欢,它是制作符合html5+CSS3规范的网页程序的快捷方式

初始架构设计的问题1:angularjs没有诸如AMD或者CMD的机制。

当我企图以现有方案继续的时候,发现在从myApp划分子模块开始就进行不下去了。问题如下:angularjs没有诸如AMD或者CMD的机制。从myApp开始,myApp所有的依赖都必须先行加载。虽然angularjs可以使用路由按需加载模板,但是控制器却要先行加载(如下面的代码所示)。

 
1
$route.when('/view1', {template: 'partials/partial1.html', controller: MyCtrl1});

我的项目里有可分4个角色模块(用户、服务台、办事人员、系统管理员),对于一个入口点压力已经很大了,每个模块还有20多个页面,难道所有的控制器、以来模块都要全部加载吗?这显然不科学。必须按需加载。

于是我在网上搜了半天,首先找到的方法如下:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$routeProvider.
        when('/phones', {
               templateUrl: 'partials/phone-list.html',
               controller: PhoneListCtrl,
               resolve: PhoneListCtrl.resolve})
 
function PhoneListCtrl($scope) {
  //本身不用管,该怎么弄怎么弄
}
 
PhoneListCtrl.resolve = {
  delay: function($q) {
    var delay = $q.defer(),
    load = function(){
        $.getScript('/js/xxxxx.js',function(){
        delay.resolve();
        });
    };
    load();
    return delay.promise;
  }
}

这个办法相当于使用angularjs内置的ajax方法,延迟加载了js文件。然而此法并不好用。在每个控制函数后面都写个延迟加载属性方法显得很笨,当然,你可以把它写到prototype去或者改angularjs源码,但那样做很有可能是给另外两个非熟练开发人员埋地雷。总之延迟加载controller问题多多,尤其是在显示的时候,具体你试试就知道了。

于是我想到了require.js这个AMD工具。之所以用require.js而不是国产的seajs并非我歧视国产(事实上我上一个项目就用的是seajs),而是因为require.js文档丰富,在github上有很多例程,适合教其他两个开发人员使用。除此以外require.js的AMD模式跟dojo的AMD按需加载方式如出一辙,因此使用过dojo开发的我对require.js更有好感。于是我最终还是使用了require.js做AMD方式的按需加载。

更进一步地,github上还有人基于RequireJS写了在angular路由时动态加载templete,controller,和directives的插件。在模块较少较清晰的情况下,这样已经够用了。 https://github.com/matys84pl/angularjs-requirejs-lazy-controllers

初始架构设计的问题2:我们的程序真的只需要一个app做入口点吗?

在angularjs的教程中,总是使用一个入口模块(通常是叫myApp)来组织整个程序。不论是angular-seed,phonecat这样的示例程序还是像NJBlog这样的比较大的应用都是一个入口点。但是,我们的项目跟这些都不同,是一个比他们都大的多、复杂的多的项目。由于本项目中的角色之间功能是严格分离的,所以产生了四大角色模块用户、服务台、办事人员、系统管理员,它们之间是不会串门的。所以这4个模块完全可以用4个app做入口点来组织程序。最后再加上一个登陆模块,做判定和跳转即可。使用requirejs最怕的问题就是依赖关系太多从而产生混乱,而这样划分5个入口的方式决定了每个app的依赖都不是太多,整个程序的功能逻辑一目了然。

虽然事实上,采用一个入口点,然后通过内部机制判断,从而在各个模块之间跳转也是可行的,但是那样程序复杂度就要高的多,还要给另外两个搭档解释都要解释半天,所以还是算了吧。

初始架构设计的问题3:按模块组织文件

一开始的时候,我的文件目录完全是照书抄书仿照angular-seed进行组织的。考虑到文件较多较复杂,于是就设置了几个direcvtives、filters、service作为文件夹,存放对应的js文件。于是文件目录看起来就像下面这个样子:

  • controllers/

    • LoginController.js
    • RegistrationController.js
    • ProductDetailController.js
    • SearchResultsController.js
  • directives.js
  • filters.js
  • models/
    • CartModel.js
    • ProductModel.js
    • SearchResultsModel.js
    • UserModel.js
  • services/
    • CartService.js
    • UserService.js
    • ProductService.js

但是这样真的好用吗?

看起来文件排列的是很整齐,但是叫我的搭档来看,他依然不清楚这些对象的依赖关系。尤其当要重用某些模块的时候,他必须从各个文件夹中搜集相关文件,而且常常会遗漏某些文件夹中的对象。

事实上,在快速开发中确实很少会在新项目中重用很多代码(重复代码倒有很多),但很可能需要重用登陆系统这样的整个模块。

所以,不如按照功能模块去组织文件夹。最终,我的目录是这么排列的:

  • build/(工程目录)

    • css/
    • img/
    • js/
      • appAdmin/  (独立模块,以app名字开头,各app模块内容近似)

        • controller/  (相关的子模板的controller.js存放在这里)
        • directives/  (相关的directive.js存放在这里)
        • admin_app.js  (app模型定义和路由配置文件)
        • admin_main.js  (requirejs的入口和配置文件)
        • admin_services.js  (app的相关服务配置文件)
      • appCustomer/
      • appHelpdesk/
      • appRepairer/
      • appLogin/
      • common/  (通用模块库)
        • angular_filter/  (一些通用的过滤器)
        • common_plugin.js  (一些非基于angular的但比较重要组件,例如console plugin)
      • utils/  (其他组件)
    • lib/  (包含所有第三方类库)
    • templete/  (子模板文件夹,其内容按模块类型分类)
      • common/
      • tplAdmin/
      • tplCustomer/
      • tplHelpdesk/
      • tplRepairer/
    • 404.html
    • admin.html  (admin模块的入口html)
    • customer.html  (customer模块的入口html)
    • helpdesk.html  (helpdesk模块的入口html)
    • login.html  (login模块的入口html)
    • index.html  (程序的总入口点,用以根据配置跳转到各个模块入口)
    • repairer.html  (repairer模块的入口html)
  • node_modules/  (grunt)
  • src/  (未经grunt处理的源文件)
  • test/  (端到端测试)
  • gruntfile.js  (grunt)
  • human.txt
  • package.json  (grunt)
  • README.md

显然,这里我将js和templete里面的文件都按模块划分为一个个子文件夹,在build的根目录下留下了数个模块入口点。这样已经足够清晰了。

还有更为激进的做法,就是将属于同一个模块的templete、css、js全放在一个模块目录下,我上一个项目就是这么做的。但是上一个项目未能清晰地划分通用模块和功能模块,造成了一定混乱。我暂时不好比较这两种划分的优劣。至少以目前的划分,已经足够可用了。

angularJs项目实战!01:模块划分和目录组织的更多相关文章

  1. angularJs项目实战!02:前端的页面分解与组装

    自从上一篇文章到现在已经有将近一个月的时间,我将精力放在了前端页面分解与组装,和angularjs如何与jquery.bootstrap.D3等一系列其他类库结合使用的经验总结上.由于公司新招了一些员 ...

  2. angularJs项目实战!03:angularjs与其他类库的协作(转)

    angularjs,在我看来是个中等重量级的框架.即不像backbone那么简单,也不像dojo和Yui那么包罗万象.很多时候,妄图包罗万象,往往会出现很多子模块的质量高不成低不就,并且修改起来较为困 ...

  3. angularJs项目实战!03:angularjs与其他类库的协作

    引言:angularjs是一个中等重量级的前端开发框架 HTML是一门很好的为静态文本设计的语言,但要构建动态的web应用它就显的乏力了.通常,我们使用以下技术来解决静态网页技术在构建动态应用上的不足 ...

  4. Vue2+VueRouter2+webpack 构建项目实战(二):目录以及文件结构

    通过上一篇博文<Vue2+VueRouter2+webpack 构建项目实战(一):准备工作>,我们已经新建好了一个基于vue+webpack的项目.本篇文章详细介绍下项目的结构. 项目目 ...

  5. net core体系-web应用程序-4asp.net core2.0 项目实战(1)-1目录

    Asp.Net Core 2.0 项目实战(1) NCMVC开源下载了 Asp.Net Core 2.0 项目实战(2)NCMVC一个基于Net Core2.0搭建的角色权限管理开发框架 Asp.Ne ...

  6. Ionic2实战——按模块划分app 创建多module

    http://www.jianshu.com/p/d94324b722af 背景 用ionic2开发过一两个小功能的朋友都会发现,每新建一个页面都需要在\src\app\app.module.ts中添 ...

  7. angularJs项目实战!04:angularjs的性能问题

    上一篇文章中我花了很多口舌去介绍angularjs是一个中型框架,面对大型应用时少不了第三方类库的配合.而我的核心议题是:如何以angularjs的思路使用其他类库,这里jquery是最好的例子了,谁 ...

  8. 【SSH网上商城项目实战01】整合Struts2、Hibernate4.3和Spring4.2

    转自:https://blog.csdn.net/eson_15/article/details/51277324 今天开始做一个网上商城的项目,首先从搭建环境开始,一步步整合S2SH.这篇博文主要总 ...

  9. JAVAEE——SSH项目实战01:SVN介绍、安装和使用方法

    1 学习目标 1.掌握svn服务端.svn客户端.svn eclipse插件安装方法 2.掌握svn的基本使用方法 2 svn介绍 2.1 项目管理中的版本控制问题 通常软件开发由多人协作开发,如果对 ...

随机推荐

  1. Stack的三种含义(数据超过栈的大小,就发生stack overflow)

    非常典型的基础知识,转自http://www.ruanyifeng.com/blog/2013/11/stack.html 学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈& ...

  2. USBSpirit(USB精灵)更新到1.2.300.105

    USBSpirit(USB精灵)是CopyU!的内核引擎,CopyU!的主要功能均由该引擎提供,此次更新主要内容如下:(版本号:1.2.300.105) 1.[修复]:修复了几处引擎的资源泄露问题,提 ...

  3. 【转】ubuntu连接android设备(附最简单方法)

    原文网址:http://blog.csdn.net/maosidiaoxian/article/details/22661725 在ubuntu下连接android设备,虽然不用像windows那样安 ...

  4. 警惕P2B模式

    大家都知道P2P是什么,估计也有很多人了解P2B的意思,这里也不多做解释,但是为什么要警惕P2B,这里我要做详细说明,希望能给大家一个参考.      首先我们要把P2B分成两种,一种是针对大型企业, ...

  5. 黑马程序员_JavaIO流(一)

    IO(Input Output)流 概述: IO流(数据流)用来处理设备之间的数据传输. Java对数据的操作是通过流的方式. Java用于操作流的对象都在IO包中. 流按操作数据分为两种:字节流与字 ...

  6. UNION ALL vs UNION

    一直没意识到它们之间的区别,只知道UNION ALL在性能上优于UNION,忽略一个很重要的区别:UNION会去掉重复的行,而UNION ALL是包括所有行.

  7. http soap关系

    http:是一个客户端和服务器端请求和应答的标准(TCP).http协议其目的是为了提供一种发布和接收htttp页面的方法 一http协议的客户端与服务器的交互:由HTTP客户端发起一个请求,建立一个 ...

  8. ERP售前顾问形象寓意

    几个做销售的朋友一起去X山旅游,一群爱侃的人凑在一起,一顿云山雾罩的神吹是少不了的.突然看到一栋平房前面伸出一个大大的幌子:铁嘴铜牙.下面一行小字:不灵不要钱(管理专家:提供专业解决方案). 销售都是 ...

  9. scroll pagination.js数据重复加载、分页问题

    scroll pagination.js数据重复加载.分页问题 解决办法 参考资料: http://blog.csdn.net/dyw442500150/article/details/1753242 ...

  10. libsvm java 调用说明

    libsvm是著名的SVM开源组件,目前有JAVA.C/C++,.NET 等多个版本,本人使用的是2.9libsvm命名空间下主要使用类:svm_model 为模型类,通过训练或加载训练好的模型文件获 ...