22
33在本章中,我们将对前三章中一步步编写出来聊天室重新做一次梳理,进行一些优化,让整个项目更容易理解和扩展。其次我们还会介绍一些前端流行的工具,帮助我们构建项目,便于发布。
44
5- ### 目前的项目结构
5+ ### 项目结构
66
77目前整个项目的架构如下图:
88
99![ TechNode Structure] ( ./examples/chapter04/images/TechNodestructure.png )
1010
1111箭头代表了读取数据的流向,服务端和客户端基本上都分为三层:
1212
13- - 服务端:在mongodb和mongoose之上 ,我们添加了一层模型的controller,这一层直接处理一些业务相关的逻辑;在这之上,我们直接通过http API或者socket.io将所提供的接口暴露出来;这一块的代码我们全部写在了app .js中;
14- - 客户端:针对不同的组件或者页面,我们对应了不同的controller,而这些controller都是通过$http或者socket service直接于服务端通信的 ;各个controller之间共享数据很困难。
13+ - 服务端:在MongoDB和mongoose之上 ,我们添加了一层模型的controller,这一层直接处理一些业务相关的逻辑;在这之上,我们直接通过http API或者socket.io将所提供的接口暴露出来;这一块的代码全部写在了app .js中;
14+ - 客户端:针对不同的组件或者页面,我们对应了不同的controller,而这些controller都是通过$http或者socket 服务直接于服务端通信的 ;各个controller之间共享数据很困难。
1515
16- 总结一下 :
16+ 基于上面的问题,我们做出以下调整 :
1717
18- - 将服务端的service逻辑从app.js分拆到http和socket service中
18+ - 将服务端逻辑从app.js分拆为http和socket两个服务中;
1919- 在客户端提供一个统一的数据接口层,向上为controller提供数据服务,向下和服务端通信,同步数据。
2020
2121新的结构应该像下面这样:
@@ -99,9 +99,9 @@ socket.on('technode', function (data) {
9999为什么需要客户端缓存?有两点原因:
100100
1011011 . 在第三章的实现中,在房间列表和房间切换时,controller都会通过socket从服务端重新获取房间列表或房间;
102- 2 . 在第三章的实现中,我们无法在controller之间共享数据,比如在LoginCtrl中,用户登录后,我们需要更 $rootScope的用户信息,我们采用了scope事件机制来实现 。
102+ 2 . 在第三章的实现中,我们无法在controller之间共享数据,比如在LoginCtrl中,用户登录后,我们需要更新 $rootScope的用户信息,采用了scope事件机制来实现 。
103103
104- 我们需要一个缓存数据和共享数据的组件,这个组件将服务端请求来的数据缓存下来,避免重复的从服务端请求相同的数据,其次是对所有的controller提供接口,让controller可以共享 (读取、修改)同一块数据 。
104+ 我们需要一个缓存数据和共享数据的组件,这个组件将服务端请求来的数据缓存下来,避免重复的从服务端请求相同的数据,其次是对所有的controller提供接口,让controller间可以共享 (读取、修改)同一份数据 。
105105
106106我们把这个组件命名为server,与服务端通信完全通过这个组件,数据缓存到这个组件之中,controller直接与它通信,不必关心真正的服务器是什么样的。
107107
@@ -147,11 +147,11 @@ angular.module('techNodeApp').factory('server', ['$cacheFactory', '$q', '$http',
147147}])
148148```
149149
150- 在server中,我们使用两个Angular.js提供的组件 ,` $q ` 和` $cacheFactory ` 。
150+ 在server中,我们使用了两个Angular提供的组件 ,` $q ` 和` $cacheFactory ` 。
151151
152152#### $q
153153
154- $q是Angular.js对JavaScript异步变成模式Promise的实现 ,参考了https://github.com/kriskowal/q 。在TechNode对它的用法相对比较简单,仅仅是将ajax请求隐藏起来 。以server.validate为例:
154+ $q是Angular对JavaScript异步编程模式Promise的实现 ,参考了https://github.com/kriskowal/q 。在TechNode对它的用法相对比较简单,仅仅是将Ajax请求隐藏起来 。以server.validate为例:
155155
156156
157157```
@@ -214,7 +214,7 @@ cache.get('rooms') && cache.get('rooms').forEach(function(room) {
214214
215215直接调用$cacheFactory,传入cacheId,Angular就为我构造出一块缓存区域,我们就可以通过get、put等等方法来存储或者获取缓存数据了。
216216
217- $cacheFactory提供了一种TechNode中未使用的特性 ,即这块缓存可以是LRU的,即这块缓存是有大小的(避免缓存太大了,影响了性能 ),并且这块缓存使用LRU算法来淘汰长时间未使用的数据。
217+ $cacheFactory还提供了一种TechNode中未使用的特性 ,即这块缓存可以是LRU的,什么是LRU? 即这块缓存是有大小的(避免缓存开销过大,影响网易性能 ),并且这块缓存使用LRU算法来淘汰长时间未使用的数据。
218218
219219### controller与server
220220
@@ -256,8 +256,10 @@ angular.module('techNodeApp').controller('RoomCtrl', ['$scope', '$routeParams',
256256}])
257257```
258258
259- - RoomCtrl不再直接与服务端通信读取当前的房间信息
260- - 无需监听用户进入或者离开,监听新消息
259+ 我们可以发现如下的变化:
260+
261+ - RoomCtrl不再直接与服务端通信读取当前的房间信息;
262+ - 无需监听用户进入、离开或者新消息的事件。
261263
262264RoomCtrl只需调用server.getRoom,传入房间的id即可。那房间信息不是需要到服务端读取么?这是怎么实现的?
263265
@@ -281,7 +283,7 @@ getRoom: function(_roomId) {
281283}
282284```
283285
284- 这里的处理方式与` promise ` 有异曲同工之妙, ` getRoom ` 方法,如果在缓存中没有找到房间的数据,就先放入一个房间对象 ,不过里面的数据都是空的(此时,RoomCtrl渲染出来的是一个空的房间视图),然后通过socket向服务端请求房间数据;如果找到就直接返回从缓存中获取的房间数据,RoomCtrl就可以渲染出来一个正常的房间视图。
286+ 这里的处理方式与` promise ` 有异曲同工之妙。 ` getRoom ` 方法,如果在缓存中没有找到房间的数据,就先新建一个房间对象 ,不过里面的数据都是空的(此时,RoomCtrl渲染出来的是一个空的房间视图),然后通过socket向服务端请求房间数据;如果找到就直接返回从缓存中获取的房间数据,RoomCtrl就可以渲染出来一个正常的房间视图。
285287
286288而在服务端返回房间信息后,
287289
@@ -296,7 +298,7 @@ case 'getRoom':
296298 }
297299```
298300
299- 我们使用服务端的数据扩展空房间即可 ,Angular即根据数据的变化,渲染出新的房间视图。
301+ 我们使用服务端的数据填充到空房间即可 ,Angular即根据数据的变化,渲染出新的房间视图。
300302
301303> 我们必须保证更新的房间对象必须是视图绑定的对象,因此我们一开始就返回一个房间对象,后面只是修改这个对象的属性。
302304
@@ -306,7 +308,7 @@ case 'getRoom':
306308
307309### 使用Grunt打包TechNode
308310
309- 开发时,为了解耦和便于维护,我们把代码拆成单独的文件,JavaScript代码、CSS代码和HTML都是单独的。在生产环境中,为了提高性能,我们需要把这些分开的文件合并到一起。如果你的网站使用CDN的化,我们还需要给每个文件,添加上唯一的标示,便于处理CDN的缓存 。
311+ 开发时,为了解耦和便于维护,我们把代码拆成单独的文件,JavaScript代码、CSS代码和HTML都是单独的。在生产环境中,为了提高性能,我们需要把这些分开的文件合并到一起。如果你的网站使用CDN的化,我们还需要给每个版本的文件,添加上唯一的标识,便于维护CDN的缓存 。
310312
311313Grunt是目前JavaScript最流行的项目自动化构建工具。Grunt官方提供了很多插件,也有大量的第三方插件。我们可以轻松地使用Grunt检查、压缩合并代码,甚至发布应用程序。我们将基于grunt-usemin等几个流行的Grunt插件来构建TechNode项目。
312314
@@ -316,7 +318,7 @@ Grunt是目前JavaScript最流行的项目自动化构建工具。Grunt官方提
316318npm install -g grunt-cli && npm install grunt --save-dev && touch Gruntfile.js
317319```
318320
319- 为了使用grunt-usemin来压缩我们的代码,我们需要在index.html添加一些特殊的注释来来帮助grunt-usemin找到要合并的文件 :
321+ 为了使用grunt-usemin来压缩我们的代码,我们需要在index.html添加一些特殊的注释来来帮助grunt-usemin找到需要合并的文件 :
320322
321323```
322324<!-- build:css /css/technode.css -->
@@ -349,9 +351,9 @@ npm install -g grunt-cli && npm install grunt --save-dev && touch Gruntfile.js
349351<!-- endbuild -->
350352```
351353
352- 我们分别在css和javascript的引用周围加上了注释,` <!-- build:css /css/technode.css --> ` 标示我们需要把下面这些css都合并到technode .css这个文件中。同理javascript全都合并到technode .js中。
354+ 我们分别在css和javascript的引用周围加上了注释,` <!-- build:css /css/technode.css --> ` 标明我们需要把下面这些css都合并到technode .css这个文件中,javascript全都合并到technode .js中。
353355
354- > 注意,socket.io.js这个文件并没有包含进来,因为它是socket.io自己输入的 ,并没有在我们的自己的源码中。当然,我们甚至可以把这个文件保存到源码中,自己引用也是可以的。
356+ > 注意,socket.io.js这个文件并没有包含进来,因为它是socket.io自己输出的 ,并没有在我们的自己的源码中。当然,我们甚至可以把这个文件保存到源码中,自己引用也是可以的。
355357
356358首先使用grunt-contrib-copy将不需要打包压缩的文件拷贝到build目录中,修改Gruntfile.js
357359
@@ -376,7 +378,7 @@ module.exports = function (grunt) {
376378}
377379```
378380
379- grunt-usemin为我们提供了一个useminPrepare的task,这个task就是基于我们在index.html文件中的配置自动生成合并和压缩代码的配置 :
381+ grunt-usemin为我们提供了一个useminPrepare的task,这个task就是基于我们在index.html文件中的配置,自动生成合并和压缩代码的配置 :
380382
381383```
382384module.exports = function (grunt) {
@@ -459,7 +461,7 @@ Configuration is now:
459461 src: [ '.tmp/concat/css/technode.css' ] } ] } }
460462```
461463
462- 它为我们生成好了,本来需要手动编写的其他grunt task的配置 ,接下来,安装其他几个需要的grunt task,继续修改Gruntfile.js:
464+ 它为我们生成了本来需要手动编写的其他task的配置 ,接下来,安装其他几个需要的grunt task,继续修改Gruntfile.js:
463465
464466```
465467module.exports = function (grunt) {
@@ -496,9 +498,9 @@ module.exports = function (grunt) {
496498}
497499```
498500
499- 安装好新的依赖,再运行grunt试试看。首先concat根据useminPrepare生成的配置,将css和js分别合并成了 .tmp/concat/css/technode.css和.tmp/concat/script/technode.js ;然后uglify和cssmin分别将这两个文件压缩成了build/css/technode.css和build/script/technode.js,我们的css文件和js文件就打包压缩好了。
501+ 安装好新的依赖,再运行grunt试试看。首先concat根据useminPrepare生成的配置,将css和js分别合并到 .tmp/concat/css/technode.css和.tmp/concat/script/technode.js中 ;然后uglify和cssmin分别将这两个文件压缩成了build/css/technode.css和build/script/technode.js,我们的css文件和js文件就打包压缩好了。
500502
501- 除此之外我们还需要把pages中的html内联到index.html中。在Angular.js中 ,我们既可以将模板文件单独放在不同的html文件中,也可以像下面这样,内联在html中:
503+ 除此之外我们还需要把pages中的html内联到index.html中。在Angular中 ,我们既可以将模板文件单独放在不同的html文件中,也可以像下面这样,内联在html中:
502504
503505```
504506<script type="text/ng-template" id="/pages/login.html">
@@ -528,7 +530,7 @@ inline_angular_templates: {
528530}
529531```
530532
531- 使用grunt-rev,为静态文件加上唯一标示 ,使用grunt-contrib-clean在每次打包开始时,清除.tmp和build里的内容:
533+ 使用grunt-rev,为静态文件加上唯一标识 ,使用grunt-contrib-clean在每次打包开始时,清除.tmp和build里的内容:
532534
533535```
534536rev: {
@@ -570,9 +572,9 @@ grunt.registerTask('default', [
570572
571573我们再来回顾一下打包的过程,开始那么多的js,首先被concat到了tmp/concat/technode.js中,然后aglify压缩到build/script/tecnhode.js中,接着rev根据文件内容为其生成了唯一的标示` 7add9650.technode.js ` ,最后,usemin再把build/index.html中的js区块换成了` <script src="/script/7add9650.technode.js"></script> ` 。这就是我们采用的整个打包压缩过程。同理css也是如此。
572574
573- ### 发布
575+ ### 发布TechNode
574576
575- 发布之前我们还需要做一些准备工作,我们需要让生产环境中访问的是打包压缩过的静态文件,express为我们提供了一种区分开发环境和声场环境的方式 :
577+ 发布之前我们还需要做一些准备工作,我们需要让生产环境中访问的是打包压缩过的静态文件,express为我们提供了一种区分开发环境和生产环境的方式 :
576578
577579```
578580app.configure('development', function () {
@@ -586,18 +588,18 @@ app.configure('production', function () {
586588app.use(express.static(__dirname + app.get('staticPath')))
587589```
588590
589- 如果我们运行` node app.js ` express木人采用的是development环境 ,我们可以使用` NODE_ENV=production node app.js ` 来启用生产环境的配置,我们这里的做法很简单,将静态文件的路径指定到编译后的/build目录即可。
591+ 如果我们运行` node app.js ` express默认采用的是development环境 ,我们可以使用` NODE_ENV=production node app.js ` 来启用生产环境的配置,我们这里的做法很简单,将静态文件的路径指定到编译后的/build目录即可。
590592
591- 至于如何部署到线上,我不再多提 ,大家可以参考[ 《使用 Express + MongoDB 搭建多人博客》番外篇之——部署到 Heroku] ( https://github.com/nswbmw/N-blog/wiki/%E7%95%AA%E5%A4%96%E7%AF%87%E4%B9%8B%E2%80%94%E2%80%94%E9%83%A8%E7%BD%B2%E5%88%B0-Heroku ) 将TechNode部署到heroku上。注意一下两点即可:
593+ 至于如何部署到线上,在这里不多做介绍 ,大家可以参考[ 《使用 Express + MongoDB 搭建多人博客》番外篇之——部署到 Heroku] ( https://github.com/nswbmw/N-blog/wiki/%E7%95%AA%E5%A4%96%E7%AF%87%E4%B9%8B%E2%80%94%E2%80%94%E9%83%A8%E7%BD%B2%E5%88%B0-Heroku ) 将TechNode部署到heroku上。注意一下两点即可:
592594
593- - 修改config.js中的mongdb配置,修改成对应的你在mongohq的数据库 ,例如:` mongodb://technode:technode@troup.mongohq.com:10046/technode `
595+ - 修改config.js中的MongoDB配置,修改成对应的你在MongoHQ的数据库 ,例如:` mongodb://technode:technode@troup.mongohq.com:10046/technode `
594596- 修改heroku对应的Procfile文件,添加` web: NODE_ENV=production node app.js ` ,让TechNode以生产模式启动。
595597
596598大家可以访问http://technode.herokuapp.com/(也可以通过www.technode.im来访问),测试我部署在heroku上的TechNode。
597599
598600### 聊天室之旅结束啦!
599601
600- 到这里,我们的聊天室之旅告一段落。TechNode虽然还有很多不足之处,但是我希望,你看完这几章之后,能够有所收获。相信你现在一定能够使用Node.js、socket.io、Angular.js等等这些现在炙手可热的框架来快速搭建一个Web应用吧 !赶紧计划自己的下一个项目吧,如果有进展,可别忘了告诉我!
602+ 到这里,我们的聊天室之旅告一段落。TechNode虽然还有很多不足之处,但是我希望,你看完这几章之后,能够有所收获。相信你现在一定能够使用Node.js、socket.io、Angular.js等等这些现在炙手可热的框架来快速搭建自己的Web应用吧 !赶紧计划自己的下一个项目吧,如果有进展,可别忘了告诉我!
601603
602604
603605
0 commit comments