既然从新学习了 Gulp,那索性就再把之前用 Gulp 写的东西拿出来,从新写一遍。此次写的时候要把要点记录下来,否则之后忘了就无法回忆了。javascript
由于 Gulp 如今使用没有之前那么多了,因此就不写复杂的应用了。此次写一个简单的 Demo 处理工具,只是为了把 PSD 转成 HTML 的时候,减小一些重复性操做。css
因为写 Demo 的时候,只去关心页面结构和对应的样式,所以,不去考虑 js 相关的内容。主要实现如下几个功能点 :html
为了这几个小目标,下面开始逐步实现这些功能点。java
使用的 Gulp 版本是
3.9.1
。
建立一个 package.json :node
$ npm init -y
安装 Gulp 做为项目的开发依赖 :git
$ yarn add gulp -D
在项目开始以前,首先须要搭建目录结构。github
. ├── gulpfile.babel.js gulp 的配置文件 ├── package.json ├── src Demo 源代码存放的目录 │ ├── common 公共文件存放的目录,未来注入到html文件中 │ │ ├── flexible.min.js │ │ └── reset.css │ ├── css │ │ ├── _none.scss │ │ └── useful.scss │ ├── html │ │ └── index.html │ └── imgs │ └── test.png ├── task 任务文件夹 │ ├── task-clean.js 删除 dist 目录 │ ├── task-css.js 处理 css 的任务 │ ├── task-default.js 默认任务 │ ├── task-html.js 处理 html 的任务 │ └── task-img.js 处理图片的任务 └── yarn.lock
若是须要在 gulpfile.js 中使用 ES6 相关语法,就须要把 gulpfile.js 改为 gulpfile.babel.js。更改文件名以后,在文件中写入 ES6 的代码。npm
import gulp from 'gulp'; gulp.task('default', () => console.log(123));
运行命令以后,发现,报错了。json
$ ./node_modules/.bin/gulp [14:15:34] Failed to load external module @babel/register [14:15:34] Failed to load external module babel-register [14:15:34] Failed to load external module babel-core/register [14:15:34] Failed to load external module babel/register
出现这个错误是由于使用 ES6 语法后,须要通过 babel 进行转码后才能正常执行任务,所以,要配置 babel 相关的内容。gulp
首先是须要安装 babel 的依赖 :
$ yarn add babel-cli babel-preset-env -D
babel 在运行的时候,须要读取其配置文件中的信息,将对应的代码进行转译。因此,.babelrc
文件必不可少。
{ "presets": ["env"] }
再次运行 Gulp 命令的时候,会发现仍是有错误。
$ ./node_modules/.bin/gulp [14:20:33] Failed to load external module @babel/register
这个错误就很奇怪了,命名已经安装了 babel 的依赖了,为何还会有这个错误呢?这是由于在解析 .babel.js
文件中,加载 babel 组件不正确形成的。babel-core/register 已通过时了,如今使用 babel-register 来代替了。若是要修复这个问题,就须要修改 node_modules 中 interpret 模块中的 index.js
。
将 module: '@babel/register'
改为 module: 'babel-register'
。具体修改的位置就是下面代码中的第三行 :
'.babel.js': [ { module: '@babel/register',
在 gulpfile.babel.js 中,咱们将 task 目录下的文件加载到该配置文件中,这个时候须要用到的模块是 require-dir
。
在项目中安装 require-dir
模块 :
$ yarn add require-dir -D
模块完成以后,改造 gulpfile.babel.js。
import requireDir from 'require-dir'; requireDir('./task');
通常状况下,把最终生成好的文件放入 dist 目录中,那么目标目录就是 dist。
删除文件夹或文件,通常使用 del
模块,这个模块异步方法返回的是一个 Promise 对象。
// ./task/task-clean.js gulp.task('clean', () => { return del('./dist').then(paths => { // 若是 paths 长度为 0,说明文件夹不存在 if (paths.length) { console.log(paths + ' 删除成功') } else { console.log('文件夹不存在'); } }); });
经过上面的代码建立的 clean 任务,在执行任务 clean 的时候,就能够把 dist 目录进行删除了。
图片的处理相对就比较简单了,只须要压缩图片并拷贝到对应的目录中就能够了。压缩图片使用的模块是 gulp-imagemin
。
gulp.task('imgs', () => { return gulp.src([ './src/imgs/*.jpg', './src/imgs/*.png', './src/imgs/*.gif', './src/imgs/*.svg' ], { base: 'src' }) .pipe(imagemin()) .pipe(gulp.dest('./dist')); });
经过上面的代码建立的 imgs 任务,在执行任务 imgs 的时候,就能够把 ./src/imgs
中的内容压缩并拷贝到 ./dist/imgs
中了。
处理 html 文件的时候,就稍微复杂点了。由于牵涉到压缩和替换。若是要替换 html 中的内容,使用的模块是 gulp-replace
;若是须要压缩 html 文件,那么就须要使用 gulp-htmlmin
。
在这里,须要替换 html 中引入 flexible.js 的地方为对应的内容,就须要写成下面的形式 :
.pipe(replace('<script type="text/javascript" src="../common/flexible.min.js"></script>', () => { // 获取 flexible.min.js 文件中的内容 let flexibleData = fs.readFileSync(path.resolve(__dirname, '../src/common/flexible.min.js')); // 返回一个流,写入共下一个内容使用 return `<script type="text/javascript">\n${flexibleData}</script>`; }))
同时初始化 css 的样式也要注入到 html 文件中,这个时候就要把 html 中对应的路径替换成对应的文件内容 :
.pipe(replace('<link rel="stylesheet" type="text/css" href="../common/reset.css">', () => { // 获取 reset.css 文件中的内容 let flexibleData = fs.readFileSync(path.resolve(__dirname, '../src/common/reset.css')); // 返回一个流,写入共下一个内容使用 return `<style type="text/css">\n${flexibleData}</style>`; }))
对应的文件替换完成以后,就须要删除 html 文件中的全部注释信息 :
.pipe(htmlmin({ collapseWhitespace: false, // 删除文档中的空格和换行,默认是false,不删除 removeComments: true // 清除注释内容 }))
最后就是将对应的 html 文件拷贝到对应的文件夹中。完整的任务代码以下 :
gulp.task('html', () => { return gulp.src('./src/html/*.html', { base: 'src' }) .pipe(replace('<script type="text/javascript" src="../common/flexible.min.js"></script>', () => { // 获取 flexible.min.js 文件中的内容 let flexibleData = fs.readFileSync(path.resolve(__dirname, '../src/common/flexible.min.js')); // 返回一个流,写入共下一个内容使用 return `<script type="text/javascript">\n${flexibleData}</script>`; })) .pipe(replace('<link rel="stylesheet" type="text/css" href="../common/reset.css">', () => { // 获取 reset.css 文件中的内容 let flexibleData = fs.readFileSync(path.resolve(__dirname, '../src/common/reset.css')); // 返回一个流,写入共下一个内容使用 return `<style type="text/css">\n${flexibleData}</style>`; })) .pipe(htmlmin({ collapseWhitespace: false, // 删除文档中的空格和换行,默认是false,不删除 removeComments: true // 清除注释内容 })) .pipe(gulp.dest('./dist')); });
样式文件的处理相对就比较复杂一点了。须要实现如下几个功能点 :
首先定义一个规则,如下划线开头的 .scss 文件不进行转换。将如下划线开头的 .scss 文件做为提供公共方法的文件。
gulp.src(['./src/css/*.scss', '!./src/css/_*.scss'], { base: 'src' })
对 .scss 文件进行转译,生成的 css 文件不删除注释,由于注释信息在 px 转成 rem 的时候会用到。在转换的过程当中,使用的模块是 gulp-sass
。
.pipe(sass({ outputStyle: 'compact' // sass文件的输出方式,保留注释内容 }))
将 px 转成对应的 rem,须要依赖两个模块 gulp-postcss
和 postcss-px2rem
。postcss-px2rem
是核心的转换模块。
.pipe(postcss([px2rem({ remUnitpx2rem: 75 // // 将px转成rem,基准值是75,也就是用 75px/75=1rem })]))
接下来就能够作 css 的兼容处理,为属性增长浏览器前缀了。自动增长浏览器前缀使用到的模块是 gulp-autoprefixer
。
.pipe(autoprefixer({ browsers: ['last 2 versions', 'Android >= 4.0'] // 控制增长前缀的版本 }))
根据页面中的标签及标签中的选择器属性,来精简样式。主要就是使用 postcss 的插件 postcss-uncss
来精简样式,可是 postcss-uncss
模块又依赖 uncss
模块,所以,这两个模块都须要安装。安装完成以后,就能够设置精简样式的代码了。
.pipe(postcss([uncss({ // 去除多余的样式 html: ['./src/**/*.html'] })]))
下面作的就是排序和压缩 css 了。排序 css 属性用到的模块是 gulp-csscomb
,压缩 css 用到的模块是 gulp-cssnano
。
.pipe(csscomb()) // 排序CSS属性 .pipe(cssnano()) // 压缩CSS代码 .pipe(gulp.dest('./dist'));
在默认任务中,首先序列化任务的执行顺序。在每次执行任务以前,先把 dist 目录删除,而后执行 html css imgs 任务。这个时候就要使用 gulp-sequence
模块来定义任务执行的顺序。为了实现任务的顺序执行,就要在每一个任务中返回一个流,或者调用一个回调函数,不然执行顺序会不正常。
gulp.task('build', gulpSequence('clean', ['html', 'css', 'imgs']));
若是要实现代码修改,浏览器自动刷新,就要使用 browser-sync
模块了。
gulp.task('default', ['build'], () => { // 启动浏览器 browserSync({ server: { baseDir: './dist' }, }, (err, bs) => { console.log(bs.options.getIn(["urls", "local"])); }); // 监视文件变化,执行对应的任务 gulp.watch('src/html/*.*', ['html']); gulp.watch('src/css/*.*', ['css']); gulp.watch('src/imgs/*.*', ['imgs']); });
在执行默认任务后,修改代码,并无发现浏览器跟着刷新。
$ ./node_modules/.bin/gulp
这是由于每一个任务中没有通知浏览器刷新,所以,要在每一个任务中加入流发生变化,通知浏览器刷新。
.pipe(browserSync.reload({ // 管道刷新 stream: true })) .pipe(gulp.dest('./dist'));
<script type="text/javascript" src="../common/flexible.min.js"></script>
和 <link rel="stylesheet" type="text/css" href="../common/reset.css">
./node_modules/.bin/gulp
固然,命令也能够简化,就是在 package.json 中配置。
"scripts": { "gulp": "gulp" }
这个时候在命令行执行命令就变成下面的形式了 :
$ npm run gulp