AngularJS+Satellizer+Node.js+MongoDB->Instagram-19

Build an Instagram clone with AngularJS, Satellizer, Node.js and MongoDB

19.优化

这节里面我们将会讲述一些通用的优化技巧,用以加速你的应用。首先,让我们来看看没有做任何改变之前我们需要多长时间来加载页面。

https://lh3.googleusercontent.com/-0ECBuRc5gXY/VIiZY48m7TI/AAAAAAAAEpU/7iKSNLcnl7o/w1694-h1290-no/Screenshot%2B2014-12-07%2B16.22.55.png

这是最近的 25 个请求,总通信 1.40 MB 用时 442ms (+/- 100ms) 来加载该页面。编译工具可以拯救它。如果你有读过我前一篇教程的话,你就应该知道我有多爱 gulp.js。

为了提高我们的 web 应用,这有一些我们很容易就能做到的简单优化技巧:

  • 压缩和串联所有的 JavaScript 文件。
  • 压缩 CSS。
  • 在服务端启动 gzip 压缩。
  • 缓存 AngularJS 模板。
  • 在服务端缓存静态文件,包括我们所有的脚本和样式。

不过首先,让我们对文件结构做一个小小的调整。把 package.jsoninstagram/server 提升一级到 instagram 目录。

https://hackhands.com/wp-content/uploads/2014/11/Screenshot-2014-12-07-16.32.37.png

然后,把 “start” 路径改为和新文件结构对应的 “node server/server.js”。这样之后,你仍然可以用 npm start 来自动启动你的 Express 应用。你的 package.json 看起来应该像这样:

<!-- lang: js -->
{
  "name": "instagram-server",
  "version": "0.0.0",
  "scripts": {
    "start": "node server/server.js"
  },
  "dependencies": {
    "bcryptjs": "^2.0.2",
    "body-parser": "^1.8.1",
    "cors": "^2.4.2",
    "express": "^4.9.0",
    "jwt-simple": "^0.2.0",
    "moment": "^2.8.3",
    "mongoose": "^3.8.17",
    "request": "^2.44.0"
  }
}

打开终端窗口,然后进入到 instagram 工程目录,使用如下命令:

<!-- lang: js -->
npm install --save-dev gulp gulp-csso gulp-recess gulp-ng-annotate gulp-uglify gulp-concat gulp-angular-templatecache gulp-complexity gulp-header

https://hackhands.com/wp-content/uploads/2014/11/Screenshot-2014-12-07-17.47.12.png

注意: –save-dev 标签会安装指定的包,并且会自动把它们添加到 package.json 的 devDependencies 对象里面,如下图:

https://hackhands.com/wp-content/uploads/2014/11/Screenshot-2014-12-07-17.48.04.png

instagram 目录下创建一个新的文件,叫 gulpfile.js,内容如下:

<!-- lang: js -->
var gulp = require('gulp');
var csso = require('gulp-csso');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var recess = require('gulp-recess');
var header = require('gulp-header');
var gulpFilter = require('gulp-filter');
var complexity = require('gulp-complexity');
var ngAnnotate = require('gulp-ng-annotate');
var templateCache = require('gulp-angular-templatecache');

我们创建的第一个任务,将会处理 JavaScript 的压缩,串联, AngularJS 内嵌注释和模板的缓存。此外,我们使用 gulp-header 来追加一个通用内容到压缩后文件,包括 工程名, 作者名, 授权最后更新日。虽然对于我们的最终目的这些都不是必须的,但是我觉得在这里介绍一下感觉很赞,因为我觉得应该把 Gulp 也顺带介绍一下。把下面的内容贴到依赖项之后:

<!-- lang: js -->
var banner = ['/**',
    ' * Instagram Demo',
    ' * (c) 2014 Author Name',
    ' * License: MIT',
    ' * Last Updated: <%= new Date().toUTCString() %>',
    ' */',
    ''].join('\n');

gulp.task('minify', function() {
    var templatesFilter = gulpFilter('clients/views/*.html');

    return gulp.src([
        'client/vendor/angular.js',
        'client/vendor/*.js',
        'client/app.js',
        'client/templates.js',
        'client/controllers/*.js',
        'client/services/*.js',
        'client/directives/*.js'
    ])
        .pipe(templatesFilter)
        .pipe(templateCache({ root: 'views', module: 'Instagram' }))
        .pipe(templatesFilter.restore())
        .pipe(concat('app.min.js'))
        .pipe(ngAnnotate())
        .pipe(uglify())
        .pipe(header(banner))
        .pipe(gulp.dest('client'));
});

Gulp 中,所有的流程都是从上到下的,每个 .pipe() 方法会执行修改之后以某种格式或类型输出,一直执行到 gulp.dest() 为止,然后保存到文件中。

之所以传入那么长的一个源文件数组是为了把它们串联起来。比如说,我们必须加载 angular.js 先于所有的 Angular 模块/库,同样我们必须加载 Angular 模块先于加载 app.js ,诸如此类。所以我们要在 index.html 里面 顺序声明 <script> 标签:

<!-- lang: js -->
<script src="vendor/angular.js"></script>
<script src="vendor/angular-route.js"></script>
<script src="vendor/angular-messages.js"></script>
<script src="vendor/satellizer.js"></script>
<script src="app.js"></script>
<script src="controllers/home.js"></script>
<script src="controllers/navbar.js"></script>
<script src="controllers/login.js"></script>
<script src="controllers/signup.js"></script>
<script src="controllers/detail.js"></script>
<script src="services/api.js"></script>
<script src="directives/serverError.js"></script>

然后,我们用 gulp-filter “绕个弯路” 到另外的 gulp.src() 上把 HTML 模板转换成 JavaScript,以便能用上 Angular 的 $templateCache 特性。我们之所以这样做,是因为 Gulp 不允许你在一个任务里面指定多个源文件。在我们完成了 angularTemplateCache 之后在过滤器里面调用 restore() 方法,这样就会把我们重新带回到原来的 gulp.src() 上。

注意: 另外一个代替方案是我们创建另外一个任务,叫 templates,在 minify 任务之前调用它,然后把 templates 任务生成的文件 (比如 templates.js) 导入到 minify 任务,最后,我们把所有的任务都执行完之后删除 templates.js 。这样会多出来很多工作,不过我想如果你不想用 gulp-filter 的话,我应该给你指出来还有这种方法。

剩下来在 minify 里面的事情都不用解释了,字面意思。这里是最后生成的 app.min.js 压缩文件的预览:

https://hackhands.com/wp-content/uploads/2014/11/Screenshot-2014-12-07-17.50.28.png

打开 index.html 然后把原来的 <script> 标签注释掉或者删掉,我们只需要导入一个压缩后的文件:

<!-- lang: js -->
<!--<script src="vendor/angular.js"></script>-->
<!--<script src="vendor/angular-route.js"></script>-->
<!--<script src="vendor/angular-messages.js"></script>-->
<!--<script src="vendor/satellizer.js"></script>-->
<!--<script src="app.js"></script>-->
<!--<script src="controllers/home.js"></script>-->
<!--<script src="controllers/navbar.js"></script>-->
<!--<script src="controllers/login.js"></script>-->
<!--<script src="controllers/signup.js"></script>-->
<!--<script src="controllers/detail.js"></script>-->
<!--<script src="services/api.js"></script>-->
<!--<script src="directives/serverError.js"></script>-->

<script src="app.min.js"></script>

然后下一个 Gulp 任务 – gulp-complexity。它是一个 JavaScript 混淆分析工具。重申一下,这不是优化的一部分,不过我觉得提一下比较赞。下面是这个任务的内容:

<!-- lang: js -->
gulp.task('complexity', function() {
    return gulp.src([
        '!client/vendor/*.*',
        '!client/app.min.js',
        'client/**/*.js'
    ])
        .pipe(complexity());
});

这里,我们用 感叹号(!)来给 Gulp 指出哪些文件和目录应该忽略,现在如果你在终端执行 gulp complexity,你会看到像下面这样的画面:

https://hackhands.com/wp-content/uploads/2014/11/Screenshot-2014-12-07-18.29.30.png

剩下的两个任务,我们将要对 CSS 来进行压缩和提升性能的代码分析。

<!-- lang: js -->
gulp.task('styles', function() {
  gulp.src([
    'client/css/sweet-alert.css',
    'client/css/styles.css'
  ])
    .pipe(concat('styles.min.css'))
    .pipe(csso())
    .pipe(gulp.dest('client/css'));
});

gulp.task('recess', function() {
 gulp.src('client/css/styles.css')
 .pipe(recess())
 .pipe(recess.reporter())
 .pipe(gulp.dest('client/css'));
});

打开 index.html 然后更新样式引用,从这样:

<!-- lang: js -->
<link rel="stylesheet" href="css/sweet-alert.css">
<link rel="stylesheet" href="css/styles.css">

改成这样:

<!-- lang: js -->
<link rel="stylesheet" href="css/styles.min.css">

现在,三个全尺寸的 CSS 文件被我们的一个压缩过的 CSS 文件替代了。我特地把 recess 放到了一个独立的任务里面,因为如果它执行失败,那么它会中止整个 Gulp 进程,这是相当烦人的,特别是当你在后台打开那些“开过就忘的” Gulp 监视器的时候。但是,你可以用 gulp-plumber 或者手动捕获 Gulp 异常事件,如果你想这样做的话。让我们执行一下 gulp recess 看看它运行起来会怎样:

https://hackhands.com/wp-content/uploads/2014/11/Screenshot-2014-12-07-18.58.07.png

最后剩下来的是,我们要添加监控和默认任务:

<!-- lang: js -->
gulp.task('watch', function() {
  gulp.watch(['client/css/*.css', '!client/css/styles.min.css'], ['styles']);
  gulp.watch([
    'client/app.js',
    'client/services/*.js',
    'client/directives/*.js',
    'client/controllers/*.js',
    'client/views/*.html'
  ], ['minify']);
});
gulp.task('default', ['watch', 'styles', 'minify']);

注意: 如果你把你客户端的代码组织得很好的话,你不需要用上面这种傻兮兮的重复路径名了。我只是在开始的时候没有想过我的教程会写成这样。如果你把你的 Angular 应用放在 src 或者 app 目录,把你的样式,vendor 库和压缩过的 app.min.js 在一两层目录外面,那么你可以简单的写成 gulp.watch(‘src//.{html, js}’, [‘minify’])*。这和我们上面做的效果一样。

好了,如果现在你执行 gulp 它会自动的运行 stylesminify 任务,然后开始监视所有文件的改变。也就是说,如果你的 CSS 文件发生改变, Gulp 会自动的重新执行 styles 任务,同样的,如果你的 AngularJS 应用有任何改变的话, Gulp 会自动的重新执行 minify 任务。

这就是我说的 “开过就忘”。我通常都是在终端打开 Gulp 进程,然后把窗口最小化由它去吧。

好了,我们现在已经把 Gulp 弄完了。下面,我们会加上 gzip 压缩和 Express 中的静态资源缓存。安装下面的 NPM 模块:

<!-- lang: js -->
npm install --save compression

https://lh3.googleusercontent.com/-nC20TtSOiVc/VIiZWk1eUoI/AAAAAAAAEpM/ebWnrBJKLpk/w1710-h1290-no/Screenshot%2B2014-12-07%2B19.14.49.png

然后把它加到 server/server.js 的依赖列表里面:

<!-- lang: js -->
var compress = require('compression');

最后在所有的 Express 中间件之前把 compress() 中间件加上去:

<!-- lang: js -->
app.set('port', process.env.PORT || 3000);
app.use(compress());
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));

为了静态资源缓存可用,修改静态资源中间件如下:

<!-- lang: js -->
app.use(express.static(path.join(__dirname, 'public'), { maxAge: 2628000000 }));

注意: 一个月相当于 2628000000 毫秒。你其实应该创建ige独立的变量,比如说 oneDayoneWeekoneMonth,或 oneYear,而不是直接在中间件中用毫秒。

好了就是这样,我们已经完成了优化。让我们来再看看浏览器的 Network 选项卡,我们可以看到比原先更小的请求数目以及更小的负载大小了。

https://lh5.googleusercontent.com/-GeNx18oxabs/VIiXqrI5UrI/AAAAAAAAEpA/ybLrsx5hgZ4/w1694-h1290-no/Screenshot%2B2014-12-07%2B19.29.38.png

这是我们优化结果的一个简单摘要:

  • 我们从 25 个请求降低到 14 个请求。
  • 下载负荷从 1.40 MB 降低到 618 KB。
  • 页面加载时间从 442ms (+/- 100ms) 降低到 240ms。

这就是我说的 唾手可得 的页面优化: 我们不需要对应用逻辑做任何改变,就能得到很不错的结果。

最后最后,我希望你能在本教程中学到其中一些技巧,并在你现在或者将来的工程中应用它们。

让我们把这个应用发布到云上吧,好吧?

你可能感兴趣的:(AngularJS,express,nodejs,node,node.js,OAuth,oauth2,Satellizer,OAuthn)