[关闭]
@a06062125 2016-11-01T18:24:09.000000Z 字数 5539 阅读 667

3663-gulp-tasks

未分类


1. 背景介绍

3663项目依赖工作流执行静态文件预编译打包、调试辅助、代码风格检查等工作, 大大提升开发人员的调试效率和代码可靠性。

然而随着项目迭代, 编译任务的启动和执行也越来越慢(平均时长1min++), 漫长的等待时间实在让人抓狂。

以v1.9-dev某次gulp任务启动日志为例, 启动耗时1分29秒, 观测某次脚本变化到打包完毕10s+, 这还不是最慢的情况。

启动日志

  1. Starting php-fpm done
  2. [16:54:55] Using gulpfile ~/devspace/town/gulpfile.js
  3. ...
  4. [16:55:41] bizScripts all files 1.25 MB
  5. [16:55:41] Finished 'bizScripts' after 42 s
  6. ...
  7. [16:55:47] Finished 'bundle_ng_js' after 5.2 s
  8. ...
  9. [16:56:24] Finished 'bizScripts:watch' after 36 s
  10. // 执行时长:00:01:29

观测到变化后重新打包

  1. [17:42:06] watchify.onUpdatd [ '/home/weimengxi/devspace/town/src/www/business/apps/live-room/index.js' ]
  2. [17:42:26] Browserify Bundle start
  3. [17:42:26] watchify.onLog 694391 bytes written (19.74 seconds)
  4. [17:42:26] Browserify Bundle finish live-room/index.js after 92.18 ms
  5. [17:42:26] bizScripts > live-room/index.js all files 694.39 kB
  6. // 从观测到变化到打包完毕: 20s

辣么问题来了, 怎么能让它 一点呢???


2. 现有任务分解

gulp工作流在3663项目中主要在两个过程中发挥作用:

静态资源编译过程又可细分为

任务概览

3663-gulp-tasks.png-45.3kB

启动任务

  1. // 流程控制
  2. gulp.task('default', ['clean'], function(cb) {
  3. runSequence(
  4. ['styles', 'images', 'fonts'], ['npapiStyle','ieBgcolorstyles','bizScripts', 'libScripts'],
  5. 'bundle_ng_js',
  6. 'entry', ['watch', 'serve'],
  7. cb
  8. )
  9. });

如果把runSequence()的参数列表展开放到一个串行的任务数组tasks中
(...args) =>{ let tasks = args }, 可以发现, 串行任务越多 , 执行时间越长。

任务时序图

Created with Raphaël 2.1.2任务时序图gulpgulptasks[0]tasks[0]tasks[1]tasks[1]tasks[2]tasks[2]tasks[3]tasks[3]tasks[4]tasks[4]styles, fonts, imagesdonenpapiStyle, ieBgcolorstyles, bizScripts, libScriptsdonebundle_ng_jsdoneentrydonewatch, servedone

3. 定位问题

通过分析, 发现现有流程存在这些问题

watch时重新执行compile类任务, 2 x nbizapp x tbizapp
tbizapp 单个bizapp打包所用时间
nbizapp bizapp的总数
  1. gulp.task('npapiStyle', function() {
  2. var file = fs.createWriteStream('dist/' + ENV.get("build_mode") + '/static/styles/npapi-fix.css');
  3. file.write(npapiCss);
  4. });

4. 解决方案

启动过程提速

做了依赖分析后的任务流程控制

  1. gulp.task('default', ['clean'], function(cb) {
  2. var cps = ['images', 'fonts', 'libScripts'],
  3. compiles = ['styles', 'bizScripts', 'bundle_ng_js', 'entry'],
  4. fallbacks = ['npapiStyle','ieBgcolorstyles'];
  5. runSequence(cps.concat(compiles, fallbacks), ['watch', 'serve']);
  6. });

执行时间为max(ttasks[0], ttasks[1])

默认启用broswerify的watchify模式, 不再在watch任务中重复执行bizScripts任务

  1. gulp.task('watch', function() {
  2. var watchOpts = {
  3. readDelay: 10
  4. };
  5. // ... other watch tasks, styles, images, fonts, entry .etc
  6. // return gulp.start('bizScripts:watch');
  7. return util.noop();
  8. })

注意: 你的任务是否在这些前置依赖的任务完成之前运行了?请一定要确保你所依赖的任务列表中的任务都使用了正确的异步执行方式:使用一个 callback,或者返回一个 promise 或 stream。
https://github.com/gulpjs/gulp/blob/master/docs/API.md

  1. // 把内存中的数据写入到目标文件中
  2. var buffer2Stream = function(buffer, targetFile, destDir) {
  3. var stream = source(targetFile);
  4. stream.write(buffer);
  5. process.nextTick(function() {
  6. stream.end();
  7. });
  8. // 返回vinyl stream
  9. return stream
  10. .pipe(vinylBuffer())
  11. .pipe(gulp.dest(destDir));
  12. }
  13. function styleFallback(fallbackStyles, fileName) {
  14. var destDir = path.join('dist/', ENV.get("build_mode"), 'static/styles');
  15. return buffer2Stream(fallbackStyles, fileName, destDir);
  16. }
  17. gulp.task('npapiStyle', ['styles'], function(){
  18. return styleFallback(npapiCss, 'npapi-fix.css');
  19. });

观测任务提速

通过配置 opts.cache , opts.packageCache 开启watchify的缓存模式。

开启缓存模式后的 watchify 会在首次执行打包任务时保存脚本的依赖分析结果,当观测到文件变化时, 仅需要对变化的文件重新进行依赖分析和相关转换。

第一次执行打包任务所花时间不变的情况下, 开启缓存时, 观测到文件变化后执行打包任务所花的时间会大大减少(包规模越大, 提速越明显); 没有开启缓存模式时,每次执行打包任务所花的时间都会和第一次一样长。

  1. if (isWatching) {
  2. opts.plugin = opts.plugin || [];
  3. // 更多说明请戳 https://github.com/substack/watchify
  4. opts.plugin.push(watchify);
  5. // 缓存modules的依赖分析结果
  6. opts.cache = {};
  7. // 缓存package.json的依赖分析结果
  8. opts.packageCache = {};
  9. }

tips: 这里的 opts.cache , opts.packageCachebrowserify 的一个共享配置变量, browserify 里依赖的 module-deps 模块也需要这两个配置变量来进行依赖分析结果管理。

引入gulp-changed 判断样式文件是否发生变化, 未发生变化的样式文件不再执行编译打包工作。

如果项目中需要编译的.less文件数目是n, 时间将缩减为原来的1/n 。

  1. var styleGlob = 'src/www/static/styles/*.{less,css}',
  2. dist = 'dist/' + ENV.get("build_mode") + '/static/styles';
  3. return gulp.src([styleGlob])
  4. .pipe(plumber())
  5. .pipe(gulpif(/\.less$/, less()))
  6. // 对比源文件和dist目录中目标文件的修改时间, 仅传递修改过的文件到后续操作中
  7. .pipe(gulpif(ENV.get("is_debug"), changed(dist, {
  8. extension: '.css',
  9. hasChanged: changed.compareSha1Digest
  10. })))
  11. .pipe(postcss(processors))
  12. .pipe(gulpif(ENV.get("is_compress"), cleanCss()))
  13. .pipe(size({
  14. title: 'styles'
  15. }))
  16. .pipe(gulp.dest(dist));

其它

将任务按图1中的类别拆分到 _gulpfiles/tasks/ 目录下, 方便维护;


5. 收效

通过以上优化, 目前任务启动仅受限于最慢的单个任务 (我会告诉你是打包业务脚本吗) 。 在相同开发机上测试, 启动时间缩减一半以上。

优化前: 1min29s
优化后: 39s

执行日志

  1. Starting php-fpm done
  2. [20:14:14] Using gulpfile ~/devspace/town/gulpfile.js
  3. ...
  4. [20:14:53] Finished 'bizScripts' after 39 s
  5. [20:14:53] Starting 'watch'...
  6. [20:14:53] Starting 'serve'...
  7. [20:14:53] Finished 'serve' after 61 μs
  8. // 执行时长:00:00:39

开启了 watchify 的缓存模式后,browserify 单次编译打包时间由蜗牛速度提至小兔子速度

优化前: 20s
优化后: 1s

  1. [17:46:17] watchify.onUpdate [ '/home/weimengxi/devspace/town/src/www/business/apps/live-room/index.js' ]
  2. [17:46:18] Browserify Bundle start
  3. [17:46:18] watchify.onLog 694394 bytes written (0.69 seconds)
  4. [17:46:18] Browserify Bundle finish live-room/index.js after 92.63 ms
  5. [17:46:18] bizScripts > live-room/index.js all files 694.39 kB
  6. // 从观测到变化到打包完毕:1s

优化前:5s+
估化后:1s+

代码已提交至 master 远程分支。


6. 有待解决

由于某些历史原因, 前端同学开发某些依赖php环境的页面时, 必须在开发机上(eg. 31)上跑编译任务。编译任务执行缓慢, 原因不明。 目前还没想到好的办法。

7. 结语

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注