自动化任务工具
自动化任务工具是用来自动化地执行开发过程中需要重复进行的任务。这些任务包括编译TS文件、压缩JS文件等等。目前较为流行的两个JavaScript自动化工具分别是Grunt和Gulp。
Grunt的插件数相对较多,在Grunt中,我们使用文件作为任务的输入和输出。
Gulp中,我们使用的 流 的方式来构建系统。Gulp插件更倾向于使用代码来描述任务,这使得Gulp的可读性更高。
本人选择Gulp作为自动化任务工具,是因为Gulp更加容易上手,配置起来更简洁。
如果你想了解更多Grunt的内容,你可以访问其官网:http://gruntjs.com/ ,或者去慕课网进行相关的课程学习:http://www.imooc.com/view/30
Gulp
安装
全局安装:
使用cnpm全局安装gulp:
cnpm install -g gulp
局部安装
使用npm局部安装gulp:
npm install gulp --save-dev
gulp设计的时候就已经规定了,必须要全局安装,然后再在项目中在进行局部安装,否则会出现 本地找不到gulp 的错误提示。
安装完成之后,会出现一个node_modules
的文件夹,里面包含了gulp的相关依赖。
创建Gulp项目
在项目的目录下执行npm init
命令:
目录结构
通用的webapp目录结构:
在这个结构中,我们使用app文件夹作为开发目录(所有的源文件都放在这下面),dist文件夹用来存放生产环境下的内容。
创建Gulp任务
在根目录下创建一个gulpfile.js
文件,打开文件,输入下面这行代码:
var gulp = require('gulp');
这行命令告知Node去node_modules中查找gulp包,先局部查找,找不到就去全局环境中查找。找到之后就会赋值给gulp变量,然后我们就可以使用它了。
我们来创建一个简单的任务:
gulp.task('task-name', function() {
// Stuff here
});
task-name
是给你的任务起的名字,稍后在命令行中执行gulp task-name
,将运行该任务。
来一个最经典的输出——hello,world!
gulp.task('hello', function() {
console.log('Hello World!');
});
执行任务
gulp hello
Gulp执行预处理
我们使用gulp-sass插件来编译sass。
- 安装gulp-sass
npm install gulp-sass --save-dev
- 在gulpfile中引入插件,用变量保存
var gulp = require('gulp');
// Requires the gulp-sass plugin
var sass = require('gulp-sass');
- 在任务中使用:
gulp.task('sass', function(){
return gulp.src('source-files')//获取目标文件
.pipe(sass()) // Using gulp-sass,执行sass,转化成css
.pipe(gulp.dest('destination'))//输出到目标文件夹中,如果目标文件夹不存在则创建一个新的文件夹
});
我们需要给sass任务提供源文件和输出位置。所以我们先在项目中创建app/scss文件夹,里面有个tank.scss文件。
这个文件将在gulp.src
中用到。
sass处理之后,我们希望它生成css文件并产出到app/css目录下,可以这样写:
gulp.task('sass', function(){
return gulp.src('app/scss/tank.scss')
.pipe(sass()) // Converts Sass to CSS with gulp-sass
.pipe(gulp.dest('app/css'))
});
Node中的通配符
通常我们不止有一个scss文件。这时候可以使用Node通配符。
使用通配符,计算机检查文件名和路径进行匹配。
大部分时候,我们只需要用到下面4种匹配模式:
*.scss
:*
号匹配当前目录任意文件,所以这里*.scss
匹配当前目录下所有scss文件**/*.scss
:匹配当前目录及其子目录下的所有scss文件。!not-me.scss
:!
号移除匹配的文件,这里将移除not-me.scss*.+(scss|sass)
:+
号后面会跟着圆括号,里面的元素用|分割,匹配多个选项。这里将匹配scss和sass文件。
根据通配符,我们可以让scss目录下的所有scss转变成css文件:
gulp.task('sass', function() {
return gulp.src('app/scss/**/*.scss')
.pipe(sass())
.pipe(gulp.dest('app/css'))
})
gulp-tslint和gulp-typescript
为了检查我们的TypeScript代码是否规范,我们可以安装gulp-tslint。
gulp-typescript 插件用于编译TS代码。
gulp-tslint
- 安装
cnpm install gulp-tslint --save-dev
- 引用和使用
var tslint=require('gulp-tslint');
gulp.task('lint',function(){
gulp.src([
'./app/ts/**/*.ts'
])
.pipe(tslint())
.pipe(tslint.report('verbose'))
})
如果你使用的是 visual studio code ,那么你可以直接在扩展中安装TSLint 插件。
个人更倾向与这种方法,这样你就不必为每个项目安装这种插件的依赖了。(安装之后记得重新加载一下vscode)
gulp-typescript
- 安装
cnpm install gulp-typescript --save-dev
- 引用
var ts = require('gulp-typescript');
- 添加任务
gulp.task('tsProject', function () {
return gulp.src('app/**/*.ts')
.pipe(ts({
noImplicitAny: true,
outFile: 'output.js'
}))
.pipe(gulp.dest('app/js'));
});
我们可以添加一些参数,来控制TS文件的编译效果
参数 | 作用 |
---|---|
outFile (string) | 生成一个JavaScript和一个定义文件。它仅仅在没有模块系统(module system)时被启用。 |
outDir (string) | 将输出移动到不同的(虚拟)目录。请注意,您仍然需要gulp.dest 将输出写入磁盘。 |
noImplicitAny (boolean) | 默认为false 。用隐含的“any”类型警告表达式和声明。 |
suppressImplicitAnyIndexErrors (boolean) | 默认为false 。压缩 --noImplicitAny 索引缺少索引签名的对象的错误。 |
noLib (boolean) | 默认为false 。不包括默认的 lib(包含 - Array,Date等的定义) |
lib (string[]) | 要包含在编译中的库文件列表。 |
target (string) | 指定ECMAScript目标版本:“ES3”(默认),“ES5”或“ES6”。 |
module (string) | 指定模板代码生成:”commonjs”,’amd’,’umd’或’system’ |
jsx (string) | 指定jsx 代码生成:’react’或 ‘preserve’ (TS1.6+). |
declaration (boolean) | 默认为false 。生成相应的.d.ts 文件。 |
removeComments (boolean) | 默认为false 。输出文件将不含注释。 |
allowJs | 默认为false 。允许JavaScript文件被编译。 |
rootDir | 指定输入文件的根目录。只能用来控制outDir 输出文件的目录结构 |
更多的参数请访问:TypeScript Wiki
监听文件
我们可以手动更新scss文件,也可以监听它,然后让他自动执行命令。
Gulp提供watch方法给我们,语法如下:
gulp.watch(glob [, opts], tasks)
或
gulp.watch(glob [, opts, cb])
glob
类型: String
or Array
一个 glob 字符串,或者一个包含多个 glob 字符串的数组,用来指定具体监控哪些文件的变动。
opts
类型: Object
传给 gaze
的参数。
tasks
类型: Array
需要在文件变动后执行的一个或者多个通过 gulp.task()
创建的 task 的名字,
cb(event)
类型: Function
每次变动需要执行的 callback。
现在添加一个监听任务:
gulp.task('watch', function(){
gulp.watch('app/scss/**/*.scss', ['sass']);
// Other watchers
})
Browser Sync自动刷新
对于开发者来说,没有最懒最有更懒。
Browser Sync 能让浏览器实时、快速响应文件的更改(html、js、css、sass、less等)并自动刷新页面。更重要的是 Browsersync可以同时在PC、平板、手机等设备下进项调试。
BrowserSync可以同时同步刷新多个浏览器,更神奇的是你在一个浏览器中滚动页面、点击按钮、输入框中输入信息等用户行为也会同步到每个浏览器中。
安装Browser Sync
npm install browser-sync --save-dev
这里没有gulp-前缀,因为browser-sync支持Gulp,所以没有人专门去搞一个给Gulp用。
browserSync服务模式
browserSync有两种服务模式,一种是服务器模式,一种是代理模式。
服务器模式
如果只希望在对某个css、html文件进行修改后会同步到浏览器里。那么只需要运行命令行工具,进入到该项目(目录)下,并运行相应的命令:
// --files 路径是相对于运行该命令的项目(目录)
browser-sync start --server --files "css/*.css"
如果需要监听多个类型的文件,则需要用逗号隔开。
// --files 路径是相对于运行该命令的项目(目录)
browser-sync start --server --files "css/*.css, *.html"
// 如果你的文件层级比较深,您可以考虑使用 **(表示任意目录)匹配,任意目录下任意.css 或 .html文件。
browser-sync start --server --files "**/*.css, **/*.html"
代理模式
如果我们已经有其他本地服务器环境PHP或类似的,则需要使用代理模式。 BrowserSync会通过代理URL(localhost:3000)来查看指定的网站。
// 主机名可以是ip或域名
browser-sync start --proxy "主机名" "css/*.css"
Browsersync和Gulp
在 gulpfile.js
中引入browsersync。
我们不推荐这样引用:
var browserSync = require('browser-sync')
我们推荐这样引用:
var browserSync = require('browser-sync').create();
前者我们仍然可以用,但是我们更推荐调用.create()
,因为这意味着你将获得一个唯一的引用,同时还能允许你创建多个服务器或者代理。
关于create
的更多操作信息请点这里API-create。
// 如果你是静态服务器
gulp.task('browser-sync', function() {
//启动browsersnyc静态服务器
browserSync.init({
server: {
baseDir: "./"
}
});
});
// 如果你是代理服务器
gulp.task('browser-sync', function() {
//启动browsersnyc代理服务器
browserSync.init({
proxy: "你的域名或IP"
});
});
通过流的方式创建任务流程,我们可以在任务完成之后调用 reload
(重载),这样所有的浏览器都会被告知变化并实时更新。但是 Browersync 仅仅关心编译完成的CSS,这就需要我们在gulp.dest
后面调用.steam()
。
根据上述内容,我们修改gulpfile.js之后的样子:
var gulp=require('gulp');
var sass=require('gulp-sass');
var browserSync = require('browser-sync').create();
// 静态服务器 + 监听 scss/html 文件
gulp.task('serve',['sass'],function(){
browserSync.init({
server:"./app"
});
gulp.watch('app/scss/*.scss',['sass']);//监听scss文件,发生变动启动任务sass
gulp.watch('app/*.html').on("change",browserSync.reload);//监听html文件,发生变动触发重载
})
//编译sass为css,自动注入到浏览器中
gulp.task('sass',function(){
return gulp.src('app/scss/*.scss')
.pipe(sass())
.pipe(gulp.dest('app/css'))
.pipe(browserSync.reload({
stream: true
})))
})
gulp.task('default', ['serve']);
这样当我们修改 tank.scss ,将会在浏览器中得到最直观的展示。
保存了三次,均及时检测到,同时完成编译、浏览器重载。
当然,我们还可以添加对js(ts)文件的监听:
gulp.task('tsproject',()=>{
return gulp.src('app/ts/*.ts')
.pipe(ts({
noImplicitAny: true,
outFile: 'test3.js'
}))
.pipe(gulp.dest('app/js'))
.pipe(browserSync.reload({
stream: true
}))
})
// 静态服务器 + 监听 scss/html/js(ts) 文件
gulp.task('serve',['sass'],function(){
browserSync.init({
server:"./app"
});
gulp.watch('app/scss/*.scss',['sass']);//监听scss文件,发生变动启动任务sass
gulp.watch('app/ts/**/*.ts',['tsproject']);//监听ts文件,发生变动启动任务tsproject
gulp.watch('app/*.html').on("change",browserSync.reload);//监听html文件,发生变动触发重载
gulp.watch('app/js/*/*.js').on('change',browserSync.reload)//监听js文件,发生变化触发重载
})
目前我们已经通过gulp做到了:
- 预处理文件(sass)
- 监听文件变化(html、scss/css、js)
- 自动刷新浏览器(方便查看修改效果)
开发的流程已经讲完,接下来我们来看看如何通过gulp优化项目。
优化CSS和JavaScript文件
目前优化分成两步,第一步是合并文件,第二步是压缩文件。
合并——gulp-useref
通过合并,能够减少CSS和JavaScript的文件数量,从而减少HTTP请求次数,达到优化的作用。
gulp-useref 能将HTML页面中的js、css引用进行合并。即便是js文件的路径不同,它一样可以进行合并。
安装gulp-useref
cnpm install --save-dev gulp-useref
现在我们要对index.html
中的引用文件进行合并。
合并之前:
配置HTML文件
在引用和添加任务之前我们需要配置一下HTML文件。
配置的语法如下:
<!-- build:<type>(alternate search path) <path> <parameters> -->
... HTML Markup, list of script / link tags.
<!-- endbuild -->
参数 | 作用 |
---|---|
type | 合并文件的类型,包括js 、css 或者remove 。remove 类型意味着gulp不会生成文件。 |
alternate search path | 默认情况下,输入文件的路径是相对于处理的文件的。当然我们也可以用搜索路径来替代输入文件的路径。 |
path | 输出目标,即优化之后的文件输出的位置 |
parameters | 可以被添加的额外的标签。如果类型是css ,则默认情况下会添加rel='stylesheet' 。我们可以在js 类型的文件中添加属性async ,这样生成的js文件会自带异步属性。 |
按照上述语法进行配置:
<!-- build:css css/main.css -->
<link rel="stylesheet" type="text/css" href="css/tank.css">
<!-- endbuild -->
<!-- build:js js/main.js -->
<script src='js/test1.js'></script>
<script src='js/test2.js'></script>
<script src='js/test3.js'></script>
<!-- endbuild -->
引用和添加任务
var useref = require('gulp-useref');
gulp.task('useref',function(){
return gulp.src('app/*.html')
.pipe(useref())
.pipe(gulp.dest('dist'))
})
dist目录代表着生产目录,所以我们通过 gulp.dest('dist')
将合并之后的文件输出到dist目录中。
启动任务
gulp useref
合并之后
压缩——gulp-uglify、gulp-clean-css
gulp-uglify 可以将js文件进行压缩,
gulp-clean-css 可以压缩css文件。
压缩js文件
- 安装gulp-uglify
cnpm install --save-dev gulp-uglify
- 引用 gulp-uglify
var uglify=require('gulp-uglify');
- 添加任务
gulp.task('jsmin', function () {
gulp.src(['app/js/*.js'])
.pipe(uglify({
mangle: true,//类型:Boolean 默认:true 是否修改变量名
compress: true,//类型:Boolean 默认:true 是否完全压缩
}))
.pipe(gulp.dest('dist/js'));
});
uglify可以添加一些参数,以获得不同的压缩效果或提示。
参数 | 作用 |
---|---|
warings | 默认为false 。设置为true 将返回result.warnings 的压缩警告。使用verbose 值可以获得更多警告细节。 |
parse | 默认为{} 。选择解析方式。如果希望指定其他parse options,请传递一个对象。 |
compress | 默认为{} 。选择压缩方式。如果输入false ,则跳过完全压缩。更多细节操作,请访问:compress options |
mangle | 默认为true 。是否压缩变量名。如果输入false ,则跳过压缩变量名。更多细节操作,请访问:mangle options |
output | 默认是为null 。选择输出方式。默认设置为最佳压缩。更多细节操作,请访问:output options |
sourceMap | 默认为false 。什么是sourceMap? 更多细节操作,请访问:source map options |
toplevel | 默认为false 。是否启用顶级压缩。如果你希望启用最顶级的变量和函数名的压缩,同时删除未使用的变量和函数,请设置为true 。 |
nameCache | 默认为null 。如果你希望通过minify() 的多个调用来缓存压缩的变量和函数,则传递一个空对象{}或以前使用的nameCache 对象。注意:这是一个读/写属性。minify() 将读取此对象的名称缓存状态并在压缩期间进行更新,以便它可以被重用或由用户外部保留。 |
ie8 | 默认为false 。是否支持IE8。设置为true 将支持IE8。 |
keep_fnames | 默认为false 。是否改变函数名。设置为true 将会防止丢弃或改变函数名。该方法依赖Function.prototype.name |
如果你对压缩没有什么具体要求,那么使用默认模式即可。
gulp.task('jsmin', function () {
gulp.src(['dist/js/*.js'])
.pipe(uglify())
.pipe(gulp.dest('dist/js'));
});
压缩css
gulp-minify-css 用于压缩css,但是gulp目前更推荐使用 gulp-clean-css 插件来压缩css。
- 安装gulp-clean-css
cnpm install gulp-clean-css --save-dev
- 引用gulp-clean-css
var cleancss=require('gulp-clean-css')
- 添加任务
gulp.task('minify-css', () => {
return gulp.src('app/css/*.css')
.pipe(cleancss({compatibility: 'ie8'}))
.pipe(gulp.dest('dist/css'));
});
我们可以在cleancss中加上不同的参数,以获得不同的压缩效果。
参数 | 作用 |
---|---|
compatibility | 控制使用兼容模式。默认是ie10+ ,效果等同于compatibility: '*' ,兼容Internet Explorer 10+模式。你还可以使用ie9 、ie8 、ie7 这几种模式。 |
fetch | 控制处理远程请求的功能。更多操作信息,请访问fetch option |
format | 控制输出的css格式。默认为false 。默认情况下输出CSS的格式不带任何空格。更多操作细节,请访问format |
inline | 控制@import 内联规则; 默认为local ;更多操作细节,请访问inline |
level | 控制优化等级。默认为1 。该参数选项包括0 、1 、2 三种。0 意味着不优化,1级优化选项通常是安全的,而2级优化对大多数用户来说应该是安全的。更多细节操作,请访问Optimization levels |
这里只是罗列部分参数,更多参数请访问:https://github.com/jakubpawlowicz/clean-css#constructor-options
我们可以一个个启动任务,也可以让它识别不同文件(css和js文件)自动进行合并、压缩。
- 安装gulp-if
cnpm install gulp-if --save-dev
- 引用gulp-if
var gulpif=require("gulp-if")
- 使用语法:
gulpif(condition, stream [, elseStream, [, minimatchOptions]]);
//例如gulpif(condition, uglify(), beautify())
- 之前的合并、压缩任务
gulp.task('useref',function(){
return gulp.src('app/*.html')
.pipe(useref())
.pipe(gulp.dest('dist'))
})
gulp.task('jsmin', function () {
gulp.src(['dist/js/*.js'])
.pipe(uglify({
mangle: true,//类型:Boolean 默认:true 是否修改变量名
compress: true,//类型:Boolean 默认:true 是否完全压缩
}))
.pipe(gulp.dest('dist/js'));
});
gulp.task('minify-css',()=>{
gulp.src(['dist/css/*.css'])
.pipe(cleancss({compatibility: 'ie8'}))
.pipe(gulp.dest('dist/css'));
})
- 优化之后的合并、压缩任务
gulp.task('minify',()=>{
return gulp.src('app/*.html')
.pipe(useref())
.pipe(gulpif("*.js",uglify({
mangle: true,
compress: true,
})))
.pipe(gulpif("*.css",cleancss({
compatibility: 'ie8'
})))
.pipe(gulp.dest('dist'));
})
图片优化
除了css、js的文件合并、压缩之外,自然还有图片优化。
图片优化使用gulp-imagemin
- 安装 gulp-imagemin
cnpm install --save-dev gulp-imagemin
- 引用gulp-imagemin
var imagemin=required('gulp-imagemin');
- 添加任务
gulp.task('imagemin', () =>
gulp.src('app/images/*')
.pipe(imagemin())
.pipe(gulp.dest('dist/images'))
);
我们可以添加一些参数,从而控制图片压缩效果。
参数包括图片类型和参数。
- 图片类型
参数 | 作用 |
---|---|
gifsicle | 控制 GIF 图片的压缩效果。 |
jpegtran | 控制 JPEG 图片的压缩效果。 |
optipng | 控制 PNG 图片的压缩效果。 |
svgo | 控制 SVG 图片的压缩效果。 |
- 图片类型的参数
图片类型 | gifsicle | jpegtran | optipng | svgo |
---|---|---|---|---|
参数 | ||||
① | interlaced: 默认为 false 。是否采用渐进渲染。图片渲染分成两种,一种是线性渲染,另一种是交错渲染(又称渐进渲染)。 关于图片渲染的异同,你可以在张鑫旭老师的文章中体验一下。 | progressive: 默认为 false 。是否采用渐进渲染。 | bitDepthReduction: 默认为 true 。应用bit深度减少。 | SVG Optimizer简写为SVGO,是一个基于node.js的插件。它可以删除SVG文件中冗余和无用的信息,同时不影响SVG渲染效果。具体的参数类型,请访问:svgo |
② | optimizationLevel: 默认为 1 。选择压缩等级,共有三个等级,1 到3 。优化级别决定完成多少优化,更高的优化级别将会花费更多的时间,但却能获得更好的优化结果。 | arithmetic: 默认为 false 。是否采用算数编码。 | optimizationLevel: 默认为 3 。选择压缩等级,共有八个等级,0 到7 。0 级优化最少,图像的深度、颜色类型都不会发生改变,也不会对现有的IDAT数据流进行重新压缩。从1 级别开始对单个IDAT进行压缩,级别越高,IDAT压缩越多。 | |
③ | color:Type:number 。减少每个输出的GIF图片中的颜色数。颜色数必须在2到256之间。 | colorTypeReduction: 默认为 true 。应用颜色类型减少。 | ||
④ | paletteReduction: 默认为 true 。减少调色板数据块。 |
根据图片类型和参数,我们来修改压缩效果
gulp.task('imagemin', () =>{
return gulp.src('app/images/*')
.pipe(imagemin([
imagemin.gifsicle({interlaced: true,optimizationLevel:3}),
imagemin.jpegtran({progressive: true}),
imagemin.optipng({optimizationLevel: 5}),
imagemin.svgo({
plugins: [
{removeViewBox: true},
{cleanupIDs: false}
]
})
]))
.pipe(gulp.dest('dist/images'))
});
注:一般来说压缩图片都比较慢,所以为了防止重复压缩图片,我们可以使用 gulp-cached 插件。
清除多余文件
在开发、压缩文件之后,是清理多余文件。
del 可以帮我们完成这项工作。
- 安装del
cnpm install del --save-dev
- 引用del
var del=require('del');
假如我们想要删除dist目录下的文件,那么可以这样添加任务
gulp.task('clean', function() {
del('dist');
});
但是我们又不想图片被删除,我们可以添加新任务
gulp.task('clean:dist', function(){
return del(['dist/**/*', '!dist/images', '!dist/images/**/*']);
});
你可以根据自己实际情况来进行文件的删除。
总结
gulp自动化任务分成两条路线
第一条是开发过程路线,预处理sass、ts文件,监听文件,自动刷新浏览器。
第二条是优化,优化css、压缩js和图片等等,同时删除不必要的文件。
如果我们想让gulp.task
一条条运行,我们需要可以使用 run-sequence 插件。
正常情况下,如果我们这样写:
gulp.task('build', [`clean`, `sass`, `minify`,`imagemin`], function (){
console.log('Building files');
})
Gulp会同时触发[]
的事件,这不是我们想要的结果。来试试run-sequence 的效果。
- 安装run-sequence
cnpm install run-sequence --save-dev
- 使用方法
var runSequence = require('run-sequence');
gulp.task('task-name', function(callback) {
runSequence('task-one', 'task-two', 'task-three', callback);
});
执行task-name时,Gulp会按照顺序执行task-one,task-two,task-three。
如果你要同时执行多个任务,则可以这样
gulp.task('task-name', function(callback) {
runSequence('task-one', ['task-two', 'task-three'], callback);
});
Gulp就会先执行task-one
,然后异步执行task-two
和task-three
。
- 合并之后的代码(以第二条为例子)
gulp.task("optimize",()=>{
runSequence('clean:dist',['minify','imagemin'])
})
注意:如果你没有在gulp.task
中正确使用return
,那么任务将会终止。
例如你在clean:dist
中没有使用return
gulp.task('clean:dist', function(callback){
del(['dist/**/*', '!dist/images', '!dist/images/**/*']);
});
那么 runSequence
在运行完clean:dist
任务之后将会终止,不会继续运行接下来的任务。
其他开发插件
- 使用
Autoprefixer
,你不再需要写CSS浏览器内核前缀 - 增加
Sourcemaps
,让你更方便的调试Sass,coffeescript - 使用
sprity
创建精灵图 gulp-changed
只允许通过修改的文件Babel
或Traceur
写ES6Browserify
,webpack
,jspm
模块化JavaScriptHandlebars
,Swing
模块化Htmlrequire-dir
分割gulpfile成多个文件gulp-moderinizr
自动生成Modernizr脚本unCSS
移除多余的CSSCSSO
更深入地优化CSSCritical
生成行内CSS
参考文档:
Gulp新手入门教程
gulp-typescript
Gulp系列教程:使用BrowserSync浏览及相关配置
Browsersync中文官网
Browsersync-api
gulp-useref-npm
gulp-useref-git
UglifyJS
clean-css
gulp-if
gulp教程之gulp-imagemin
gulp-imagemin