Skip to content

Commit 231f085

Browse files
committed
chapter 04 done
1 parent c824570 commit 231f085

File tree

2 files changed

+32
-30
lines changed

2 files changed

+32
-30
lines changed

how-to-build-chat-app-with-socket.io-and-angular/03-提供不同的房间.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,4 +735,4 @@ socket.on('leaveRoom', function(leave) {
735735
### 坏代码的味道
736736

737737
一个聊天室基本完成了,但是我们的app.js中充斥了大量的代码,有坏代码的味道。下一章我们将重新梳理一下整个TechNode架构,看看能不能在上面做点什么,让它更容易维护扩展,添加新的功能。
738-
还有,我们还将使用介绍如何使用一些前端工具,做一些上线的准备,将这个应用发布出去。
738+
还有,我们还将使用介绍如何使用一些前端工具,做上线的准备,将这个应用发布出去。

how-to-build-chat-app-with-socket.io-and-angular/04-架构优化与发布.md

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@
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

101101
1. 在第三章的实现中,在房间列表和房间切换时,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

262264
RoomCtrl只需调用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

311313
Grunt是目前JavaScript最流行的项目自动化构建工具。Grunt官方提供了很多插件,也有大量的第三方插件。我们可以轻松地使用Grunt检查、压缩合并代码,甚至发布应用程序。我们将基于grunt-usemin等几个流行的Grunt插件来构建TechNode项目。
312314

@@ -316,7 +318,7 @@ Grunt是目前JavaScript最流行的项目自动化构建工具。Grunt官方提
316318
npm 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
```
382384
module.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
```
465467
module.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
```
534536
rev: {
@@ -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
```
578580
app.configure('development', function () {
@@ -586,18 +588,18 @@ app.configure('production', function () {
586588
app.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

Comments
 (0)