AngularJS 进阶 (二十五)requirejs + angular + angular-route 浅谈 HTML5 单页面架构
众所周知,现在移动 Webapp 越来越多,例如天猫、京东、国美这些都是很好的例子。而在 Webapp 中,又要数单页面架构体验最好,更像原生 app。简单来说,单页面 App 不需要频繁切换网页,可以局部刷新,整个加载流畅度会好很多。
废话就不多说了,直接到正题吧,浅谈一下我自己理解的几种单页面架构:
1、requirejs+angular+angular-route(+zepto)
最后这个 zepto 可有可无,主要是给团队中实在用不爽 angular 的同学,可以灵活修改一下页面某些内容。当然,严谨的项目不应该出现 zepto。
2、requirejs+backbone+zepto+template
这个方案更灵活,MVC 味道更浓,使用自定义的 template 模版库
3、requirejs+route+template
这个方案最灵活,看破红尘,针对简单的业务用最简单的方式,只需要路由和模版,不用 MVC 框架
4、react
个人感觉,react 更偏向于 view 层的组件,更 native,但实施难度略高
说到项目架构,往往要考虑很多方面:
方便: 例如使用 jquery,必然比没有使用 jquery 方便很多,所以大部分网站都接入类似的库;
性能优化: 包括加载速度、渲染效率;
代码管理:大型项目需要考虑代码的模块化,模块间低耦合高内聚,目的就为了团队合作效率;
可扩展性:这个不用说了。
学习成本:一个框架再好,团队新成员难以掌握,学习难度大,结果很容易造成代码混乱。
而根据实际经验来看,方便是必然首要地位,除此之外,应该是代码管理了。团队合作过程中,各种协作,代码冲突等等,都会给一个优秀框架带来各种奇怪难题。所以,有好的框架还不够,我们还需要根据自身业务和团队的情况,按需裁剪或者修改框架,找到最佳的实施方案。
接下来,将分三个随笔分别介绍一下我心目中前三种架构的较好实施方案,而最后一种,跟前三种有种道不同不相为谋的感觉,加上自己道行不够,还是暂且不提了。
这一篇,先说说第一种:requirejs+angular+angular-route
移动端单页面 Web 相对多页面来说,模块化管理显得非常重要,因为如果没有模块化,页面初始化时就把所有的 js 和所有模版都加载进来,会导致首屏速度极慢。这一点,大家都理解的。
所以,requirejs 或者类似的模块化框架是必不可少的。requirejs 比较流行,配合 grunt 可以做好整套的自动化工具,我们就以这个为例子吧。
首先,来看看 demo 项目的整体架构。
除了类库外,业务代码都以模块划分目录,这样做便于实际开发中,按模块化合并 js 和 html,也利于多人并行开发,各自修改不同的模块,互不影响。
另外,说说三个重点的根目录文件:
index.html
,这个就是单页面唯一一个 html 了,其他都只是片段模版(tpl.html)。一般可以把这个 html 放到动态服务器上,保持零缓存,同时这里可以携带各种 js 版本控制信息和必要的用户数据。
main.js
,这个是由 requirejs 引入的第一个业务 js,主要是配置 requirejs;
router.js
,这个是整个网站/app 的路由配置,在实际部署中,可以把 main.js 和 router.js 合并。
第一步,先看看 index.html 需要做什么变化
相对 angular 的写法,这里由于使用 requirejs 管理全部模块,所以 index.html 中不需要引入 angular 等,只是设置了一个带 ng-view 属性的 div,用于充当整个 App 的视图区域。
data-baseurl 是额外加入的属性,主要好处是可以轻松在 html(0 缓存)中对 js 的 url 进行修改。
data-main 就是 requirejs 的标准写法了,跳过不说。
第二步,main.js,也就是 requirejs 的配置
requirejs 的语法,说来话长,简单在代码中做了注释。有兴趣了解详情的可以参考官网:http://requirejs.org/;
angular 可以参考:AngularJS
这里配置好 requirejs 后,就做第一步工作,引入 angular 和 angular 的路由配置,然后用 angular.bootstrap(document, [‘webapp’]); 手工启动 angular,这里 webapp 是 router.js 中定义的 angular module。
第三步,配置这个 router
上述代码看起来长,实际很短,因为有一堆绿色的注释,嘿嘿。。。
如果大家用过 angular-route,这里的语法就很简单,如果没用过,则建议直接阅读 angular-route 源代码中的注释,非常清晰。
简单而言,就是 when 函数配置一个路由规则,对应一个 template 和一个 controller。otherwise 就是默认路由,也就是遇到一个未定义路径的时候如何跳转。
如果没有使用 requirejs,那么我们需要在路由配置前加载完全部 controller。angular-route 需要做的只是切换 HTML 模版,重新编译,绑定新的 controller。
但是这里用了 requirejs,事情就变化了。我们要按需加载,不可能页面刚加载就全部 controller 都 load 回来,这样得耗费多少流量。。。
所以,这里利用了 angular-route 提供的 resolve 功能,也就是路由更改 html 前先把 resolve 里边该做的事完成。
resolve 的写法比较特殊,接受的是一个 key:value 对象,keyName 将会导入到 controller 中(如果 controller 有注明依赖)。而 value 应该是一个函数,函数的写法类似 controller,angular 会自动根据参数名导入相应依赖的服务,例如route。
上述例子中,module1.js 定义了模块 1 的 controller,后续我们再看代码。
由于路由配置前还不存在这个 controller,所以现在需要动态注册这个 controller。也就是:
$controllerProvider.register('module1Controller', controller);
第四步,
angular 有太多牛逼的功能,但实际上我业务太简单,用不到。所以这里只演示了 3 种最简单的情况。
这里不得不说,由于双向绑定,拉 cgi 和修改 dom 这些操作就变得非常简单了。
貌似一切解决了?这样的模块化似乎已经很好,跳转到某个模块的时候才加载对应的 html 和 controller js。
但是对于追求极致的团队来说,模块的 html 和 js 应该打包在一起,一次请求就拉回来,这样能大大减少 HTTP 请求的时间。而现在按照 angular-route,只能利用 templateUrl 单独拉取一个 html 文件。
那么接下来,我们再动动歪脑筋,修改一下。
第五步,修改 angular-route,实现 HTML 和 js 打包加载。
首先,先修改一下 angular-route 的源代码,这个源代码非常精简,不用太纠结,狠狠的去修改就好了。
另外,想问我为什么知道或者想到在这修改?咳咳咳,我会大摇大摆的说我认识 angular-route 的作者么?。。。。。。。开玩笑,作者叫什么,我都没去找,还说认识作者。其实就是逐步调,稍加变量搜索,发现一些不对劲,就做了这个小刀。
再另外,有专家要拍板了,这样乱修改,肯定带来毛病。是的,我不得不说,我自己都没彻底的检查是否有问题,但按照实际情况来看,暂时没遇到问题。
然后,做一个新的 when 配置:
这里用 module2 做例子,跟 module1 不同,这里初始设置的 template 是空字符串,然后在 resolve 中 require 回来后,动态修改 $route.current.template。
因为我知道,这个修改能赶在 angular-route 修改 HTML 前,也就是小把戏能凑效。
相应,看看 module2 怎么写:
大功告成,这样 html 模版就不由 angular-route 去接管了,而是由 requirejs 加载,我们可以控制的范围和灵活性就变大了。
不过,这里 controller 的函数写法可能会因为压缩混淆时丢失了原来的参数名,所以,我们也可以采用显式注入的方式:
到这里,整个架构基本就成型了,webapp 中每个模块都能非常独立,这样对网站打开速度和协同开发都非常有好处。
但是,路由表的配置还是略复杂,每次大家都要写一大堆代码,这不是我们想要的,那么可以抽取公用代码,再优化一下。
第六步,优化路由表,变成真正的配置化。
routeMap 可以由服务器直出,实现 0 缓存,彻底解耦,更便于团队合作。
最后最后,由于 requirejs 和 angular 都有模块管理,但两个概念又不一致,这里说说我的看法:
requirejs 模块管理,不单单是代码模块化,还提供了模块加载的功能;
angular 模块管理,更在乎的是代码逻辑上的模块化,避免全局变量污染,并不提供 js 文件层面的加载功能;
作为逻辑模块管理,其实用 requirejs 的模块管理就够了,所以我觉得除了 angular 原生的 controller、service 外,我们业务相关的公用库,用 requirejs 吧。
版权声明: 本文为 InfoQ 作者【No Silver Bullet】的原创文章。
原文链接:【http://xie.infoq.cn/article/fd637cabb33b1ab82d5742dee】。文章转载请联系作者。
评论