你是否还在手动压缩图片、js、css?
在前端技术飞速发展的今天,如果你还在日复一日地重复着那无休止的体力劳动,那你要反思了,这年头不懂捣鼓个工作流出来都不好意思说自己学前端的。 毕竟javascript是最好的编程语言嘛。:) 玩笑开到这里,到底什么是工作流?
如题,此次的分享主题'自动化构建',目的便是介绍如何根据需要搭建一套工作流,由浅入深。 一、前提往下看之前,请自备以下知识: 以上知识点请务必掌握,不然没法接着往下讲,切勿好高骛远; 我们先来设定一个简单的需求:
一言不合就上图: 自动化构建(入门).png 好,有了这个规划之后,我们开始着手来实现它。 二、源码首先新建一个项目文件夹project,接着我们打开命令行工具,切换到这个目录下,开始初始化这个项目: cd projectnpm init 按照提示完成初始化,打开项目我们会得到一个package.json文件像这样: // package.json{ 'name': 'project', 'version': '1.0.0', 'description': 'a test project', 'main': 'index.js', 'scripts': { 'test': 'node ./build/test.js' }, 'author': 'jack lo', 'license': 'ISC'} 紧接着我们先来创造一份源码,结构如下: - project |- src // 源文件夹 | |- tpl | | `- index.html | |- css | | `- index.css | |- js | | `- index.js |- dist // 打包文件夹 `- package.json 三、编译打包一切材料就绪,我们开始安装工具包,这一次我们主要使用gulp来构建,不需要用到webpack,同时,为了实现浏览器自动刷新的功能,我们还需要用到browser-sync。 npm install gulp browser-sync --save-dev 这需要花点时间。 ---------- 虚度光阴中 --------- 事实上,这里有个小坑需要我们提前准备一下,因为我们接下来会用到gulp的cli,所以这里我们需要全局安装gulp,没错,我就是设套让你装两遍! 这是个好习惯,请务必以后也坚持这么做。把开发时候用到的所有npm包都记录在package.json文件中,可以方便日后自己以及他人的使用。 npm install gulp -g mac用户可能还要sudo一下: sudo npm install gulp -g 回车后按提示输入密码,继续回车,就可以正常安装了。 ---------- 再一次虚度光阴中 --------- ok,安装完后,我们可以开始做点有意义的事了。 gulp的用法其实相当简单,api也就那么几个。简单来说,就是写好一个配置文件gulpfile.js,然后在命令行里执行它。 首先在根目录下新建一个gulpfile.js,然后随便建一个叫做test的任务: // gulpfile.jsvar gulp = require('gulp')// 创建一个名为test的任务,任务内容只是简单地在控制台输出一段文本gulp.task('test', function () { return console.log('this is a test')}) ok,保存。然后我们回到命令行,输入以下命令然后回车 gulp test gulp后面跟的是你所要执行的任务名,于是我们得到了这样一个结果 [22:56:35] Using gulpfile ~/project/gulpfile.js[22:56:35] Starting 'test'...this is a test[22:56:35] Finished 'test' after 149 μs 成功输出文本!恭喜,你已经掌握了gulp一半以上的用法。 现在我们尝试着来操作文件,我们将src/html里面的html文件给拷贝到dist文件夹(如果不存在则新建)里面去,怎么做? // 创建一个copy的任务gulp.task('copy', function () { return gulp.src('src/tpl/*.html') .pipe(gulp.dest('dist'))}) 这里的pipe其实是一种管道,你可以一直pipe连着pipe下去,也就是这个工作流可以一直这样一层层执行下去,随便你定义多少个处理任务都行,这就是gulp的特点,简单明了的工作流程。很多人不明白gulp到底是做什么的,其实到这里,就可以有个大概的认识了:
在gulp以前,处理一份sass文件,你可能需要先执行一次编译的任务,编译成css之后,再执行一遍压缩css的任务,压缩完之后,再手动拷贝到打包文件夹里。原本需要分三步来操作的一件事情,在gulp里面就是一个任务的sei而已: // 创建一个css的处理任务gulp.task('sass', function () { return gulp.src('src/sass/*.scss') .pipe(sass()) .pipe(minifycss()) .pipe(gulp.dest('dist/static'))}) 甚至我们可以同时执行多个任务,我可以定义好coffee、sass、image、html的四个任务,再把他们合并到一个任务当中去,一次性执行完: // 创建一个build的处理任务gulp.task('build', ['coffee', 'sass', 'image', 'html']) 我只需要: gulp build 回车,这酸爽。 好了,介绍了这么多内容,最后我们还是回到最开始那份需求的实现上来。 我们来建几个任务,分别处理js、css、html文件,把js和css文件放到dist/static目录下,把html文件放到dist下: gulp.task('css', function () { return gulp.src('src/css/*.css') .pipe(gulp.dest('dist/static'))})gulp.task('js', function () { return gulp.src('src/js/*.js') .pipe(gulp.dest('dist/static'))})gulp.task('html', function () { return gulp.src('src/tpl/*.html') .pipe(gulp.dest('dist'))})gulp.task('build', ['css', 'js', 'html']) 命令行gulp build一下,回车!再看看dist文件夹,done! 咋一看,好像没什么问题,但好像又有哪里不太对劲。不对啊,除了复制到dist文件夹,好像没啥功能啊,说好的压缩合并呢?说好的处理预编译呢?嗯,有了这个框架,这些功能我们想加多少加多少。 这样简单的项目,我们没法玩出花样,我们来点预编译语言,css用sass代替,html用swig代替: - project |- src // 源文件夹 | |- tpl | | `- index.swig | |- sass | | `- index.scss | |- js | | `- index.js |- dist // 打包文件夹 `- package.json 编译sass需要安装gulp-sass模块,编译swig需要gulp-swig。注意到了吗?基本上gulp的模块都以 npm install gulp-sass gulp-swig --save-dev ---------- 虚度光阴中 --------- 安装完后,我们再来修改一下配置文件gulpfile.js var gulp = require('gulp')var sass = require('gulp-sass')var swig = require('gulp-swig')gulp.task('sass', function () { return gulp.src('src/sass/*.scss') .pipe(sass({ outputStyle: 'compressed' // 此配置使文件编译并输出压缩过的文件 })) .pipe(gulp.dest('dist/static'))})gulp.task('js', function () { return gulp.src('src/js/*.js') .pipe(gulp.dest('dist/static'))})gulp.task('tpl', function () { return gulp.src('src/tpl/*.swig') .pipe(swig({ defaults: { cache: false // 此配置强制编译文件不缓存 } })) .pipe(gulp.dest('dist'))})gulp.task('build', ['sass', 'js', 'tpl']) 再接着gulp build一下,编译结束,可以看看dist下是不是有index.html,dist/static下是不是有编译完成且压缩过的index.css,没有你找我!这里就不分别展示源文件和打包后文件的内容了,因为并不重要,想看演示项目内容的朋友,可以在文章结尾处找到本次演示项目的git仓库链接。各种预编语言和前端模板大家可以根据自己的喜好选择,笔者只是选择自己熟悉的几种来做演示。 到这里我们基本上已经完成打包的工作了,我们来试着搭建开发环境。 四、搭建开发环境前文我们提到一个工具browser-sync,还记得吗?现在用得上了!我们先创建一个开发任务,像gulp build一样简单的任务,我们的目标是: gulp.task('dev', ['js:dev', 'sass:dev', 'tpl:dev'], function () { // do something here...}) 看着好像稍有不同。这一次我们创建一个叫做dev的任务,这个任务先执行 在这之前,我们先来了解一下browser-sync:
事实上Browsersync可以理解为一个本地服务器,类似于Apache,不同的是,Browsersync只提供很简单的http功能,它的主要功能,是通过搭建一个本地服务器,并且监听文件的更改,自动刷新浏览器,实时地呈现最新内容。 browser-sync的用法: var browserSync = require('browser-sync').create()browserSync.init({ server: { baseDir: './' // 设置服务器的根目录 }}) 其实也很简单,没有太多内容,现在我们要将它整合进我们的gulp任务里,捣鼓几下,我们得到: gulp.task('dev', ['js:dev', 'sass:dev', 'tpl:dev'], function () { browserSync.init({ server: { baseDir: './dist' // 设置服务器的根目录为dist目录 }, notify: false // 开启静默模式 }) // 我们使用gulp的文件监听功能,来实时编译修改过后的文件 gulp.watch('src/js/*.js', ['js:dev']) gulp.watch('src/sass/*.scss', ['sass:dev']) gulp.watch('src/tpl/*.swig', ['tpl:dev'])}) 这里的watch行为可以简单理解为:我(gulp)就这么盯着你(src/js/*.js文件),只要你被改动了,我就马上执行 那么,我们现在还需要补充一下 var browserSync = require('browser-sync').create()var reload = browserSync.reloadgulp.task('sass:dev', function () { return gulp.src('src/sass/*.scss') .pipe(sass()) .pipe(gulp.dest('dist/static')) .pipe(reload({stream: true}))})gulp.task('js:dev', function () { return gulp.src('src/js/*.js') .pipe(gulp.dest('dist/static')) .pipe(reload({stream: true}))})gulp.task('tpl:dev', function () { return gulp.src('src/tpl/*.swig') .pipe(swig({ defaults: { cache: false // 此配置强制编译文件不缓存 } })) .pipe(gulp.dest('dist')) .pipe(reload({stream: true}))}) 这里的
最终我们整理得到一份gulpfile.js文件: var gulp = require('gulp')var sass = require('gulp-sass')var swig = require('gulp-swig')var browserSync = require('browser-sync').create()var reload = browserSync.reloadgulp.task('sass', function () { return gulp.src('src/sass/*.scss') .pipe(sass({ outputStyle: 'compressed' // 此配置使文件编译并输出压缩过的文件 })) .pipe(gulp.dest('dist/static'))})gulp.task('js', function () { return gulp.src('src/js/*.js') .pipe(gulp.dest('dist/static'))})gulp.task('tpl', function () { return gulp.src('src/tpl/*.swig') .pipe(swig({ defaults: { cache: false // 此配置强制编译文件不缓存 } })) .pipe(gulp.dest('dist'))})gulp.task('sass:dev', function () { return gulp.src('src/sass/*.scss') .pipe(sass()) .pipe(gulp.dest('dist/static')) .pipe(reload({stream: true}))})gulp.task('js:dev', function () { return gulp.src('src/js/*.js') .pipe(gulp.dest('dist/static')) .pipe(reload({stream: true}))})gulp.task('tpl:dev', function () { return gulp.src('src/tpl/*.swig') .pipe(swig({ defaults: { cache: false // 此配置强制编译文件不缓存 } })) .pipe(gulp.dest('dist')) .pipe(reload({stream: true}))})gulp.task('dev', ['js:dev', 'sass:dev', 'tpl:dev'], function () { browserSync.init({ server: { baseDir: './dist' }, notify: false }) gulp.watch('src/js/*.js', ['js:dev']) gulp.watch('src/sass/*.scss', ['sass:dev']) gulp.watch('src/tpl/*.swig', ['tpl:dev'])})gulp.task('build', ['sass', 'js', 'tpl']) 嗯,完美,到这一步,我们这个自动化构建已经基本完成了,而且还算是完整。现在,我们开发的时候,就执行gulp dev,打包的时候就执行gulp build,是不是很方便?
五、补充然而事情还没完,我们的目标是: 有三个地方其实我们还没做到位:
二话不说就开工! 第一点很好解决,我们可以把脚本作为一项配置存放在package.json文件中: { 'name': 'project', 'version': '1.0.0', 'description': 'a test project', 'main': 'index.js', 'scripts': { 'dev': 'gulp dev', // 开发脚本 'build': 'gulp build', // 打包脚本 'test': 'node ./build/test.js' }, 'author': 'jack lo', 'license': 'ISC', 'devDependencies': { 'browser-sync': '^2.13.0', 'gulp': '^3.9.1', 'gulp-sass': '^2.3.2', 'gulp-swig': '^0.8.0' }} 注意到了吗?scripts项就是用来预定义脚本的地方,我们可以很方便地把脚本按照上面的形式封装好,然后执行的方式就变成了: npm run dev // 执行开发npm run build // 执行打包 搞定! 我们接着看第二点,删除dist文件夹,多简单的事啊!鼠标右键,删除,搞定! 哪有这么low的事,我们的目标是: 有一个叫做rimraf的包,可以帮我们做这事,我们需要用到它的cli,所以跟gulp一样,我们全局安装它: npm install rimraf -g 安装完后,我们再重新修改一下package.json文件中的scripts内容: { 'name': 'project', 'version': '1.0.0', 'description': 'a test project', 'main': 'index.js', 'scripts': { 'dev': 'gulp dev', // 开发脚本 'build': 'rimraf dist && gulp build', // 打包脚本 'test': 'node ./build/test.js' }, 'author': 'jack lo', 'license': 'ISC', 'devDependencies': { 'browser-sync': '^2.13.0', 'gulp': '^3.9.1', 'gulp-sass': '^2.3.2', 'gulp-swig': '^0.8.0' }} ok,现在试试执行 完美。 第三点放到最后才补充,主要是考虑到它并不是必要的,因为有些项目并不需要ftp上传,一般是提交svn,然后再由后端或者运维去部署,笔者是需要将静态资源上传到cdn服务器进行加速的,所以需要这样一个任务,在此我们简单介绍一下。 举一反三一下,我们再创建一个upload任务: var ftp = require('gulp-ftp')var gutil = require('gulp-util')gulp.task('upload', function () { return gulp.src('dist/**') .pipe(ftp({ host: '8.8.8.8', // 远程主机ip port: 22, // 端口 user: 'username', // 账号 pass: 'password', // 密码 remotePath: '/project' // 上传路径,不存在则新建 })) .pipe(gutil.noop())}) 自行安装一下gulp-ftp和gulp-util两个包,然后在package.json文件中的scripts补充一个脚本npm run upload来执行gulp upload。 笔者通常都是打包之后顺便上传,命令行直接输入npm run build && npm run upload,回车,然后就可以愉快地去跟旁边的妹纸聊天了。 六、总结到这里,我们已经完整搭完了这一套简易自动化工具,好像讲了很多东西,其实总结起来内容非常少:我们只不过分别用三个小任务(sass、js、tpl),组成了build和dev这两个大任务,仅此而已。熟练的情况下操作起来,整个过程也不过十分钟! ps:网络太差怪我咯?呵呵。 由于时间和篇幅关系,我们只简单处理了css、js和html,事实上,你还可以在这个基础上继续完善下去,js可以由coffeejs编译得到,而且还可以继续压缩,甚至可以把全部js文件合并成一个!html也一样可以继续压缩。而且,你完全可以自己创建一个任务去处理其他诸如图片、字体等等。 好啦,简易版就讲到这里了。 后话:gulp与webpack都是目前比较流行的编译打包工具,那么它们之间有什么异同点?怎么去进行选择?我们下一节将介绍如何去用webpack搭建一套开发工具,从中我们可以感受这两者的差别,敬请留意。 本次演示项目的git地址:gulp_base (文章有任何谬误之处,欢迎留言指出) |
|