gulp 基础
gulp 是基于 nodejs 的用自动化构建工具,可以通过 gulp 和各种插件实现前端的开发自动化工作流程。
快速入门
第一步: 全局安装 gulp:
$ npm install --global gulp
第二步: 作为项目的开发依赖(devDependencies)安装
$ npm install --save-dev gulp
第三步:
在项目根目录下创建一个名为 gulpfile.js 的文件:
var gulp = require('gulp');
gulp.task('default', function() {
// 将你的默认的任务代码放在这
});
第四步: 运行 gulp:
$ gulp
默认的名为 default 的任务(task)将会被运行,在这里,这个任务并未做任何事情。 想要单独执行特定的任务(task),请输入 gulp 。
gulp API 文档
gulp 一共就 4 个 api, src
、dest
、task
、watch
。
具体细节请参考官方文档
适合初学者的 gulp+requirejs 的项目模板
虽然,webpack
已经大行其道。作为初学者,gulp 依然是最快的入门学习前端工作流的工具。本项目本着为中小型团队打造一个基于:gulp+sass+requirejs+es6+eslint+arttemplate 的基本的项目模板。
项目模板在线 github 地址
gulp 相关应用
- js(用 requirejs 管理模块)
- 压缩
- 转码
- 版本号
- 自动修改注入 requirejs 的 paths
- es6 转码
- eslint 检测
- 添加版本信息
- style
- sass
- 合并
- 压缩
- 加前缀
- 打版本
- sourcemap(开发)
- 图片压缩优化
- html
- 压缩
- 修改 url 地址
- 打包前清理
- 打包后拷贝目录
编辑器的一致性
- .editorconfig 文件控制编辑器的一致性
- eslint 校验一致性,并配置 vscode 自动修改符合 eslint 校验,可以安装 eslint 插件。
本地测试和代理配置
- 运行本地测试
$ npm run dev
本地启动服务器地址: http://localhost:8000
打包
$ npm run build
# 会把最终结果输出到dist目录
项目的目录结构
.
├── gulpfile.js
├── package.json
├── readme.md
├── src
│ ├── app.js
│ ├── asset
│ │ └── img
│ │ └── a.jpg
│ ├── controller
│ │ └── student
│ │ └── listController.js
│ ├── index.html
│ ├── js
│ │ ├── E.js
│ │ ├── index.js
│ │ ├── rev-manifest.json
│ │ └── tmpl
│ │ ├── header.js // 自动生成
│ │ ├── student.js // 自动生成
│ │ └── user.js
│ ├── lib
│ │ ├── arttemplate
│ │ │ ├── es5-sham.min.js
│ │ │ ├── es5-shim.min.js
│ │ │ ├── json3.min.js
│ │ │ └── template-web.js
│ │ └── require.js
│ ├── service
│ │ └── index.js // 所有的服务
│ ├── style
│ │ ├── a.css
│ │ ├── main.css
│ │ ├── rev-manifest.json
│ │ └── scss
│ │ ├── b.scss
│ │ └── index.scss
│ ├── template // art-template模板
│ │ ├── header
│ │ │ └── header.html
│ │ ├── student
│ │ │ ├── stuList.html
│ │ │ ├── t.html
│ │ │ └── userList.html
│ │ └── user
│ │ └── footer.html
│ └── view
│ ├── about.html
│ └── student
│ └── list.html
└── yarn.lock
gulpfile 细节
'use strict'; const fs = require('fs'); const path = require('path'); // 引入gulp包, nodejs代码 const gulp = require('gulp'), // 引入gulp-sass 包 sass = require('gulp-sass'), // gulp版本定义 rev = require('gulp-rev'), concat = require('gulp-concat'), // 合并文件 --合并只是放一起--压缩才会真正合并相同样式 rename = require('gulp-rename'), // 设置压缩后的文件名 // 替换版本url revCollector = require('gulp-rev-collector'), // css 压缩 cleanCss = require('gulp-clean-css'), // 兼容处理css3 autoprefixer = require('gulp-autoprefixer'), // css代码map sourcemaps = require('gulp-sourcemaps'), // js 压缩 uglify = require('gulp-uglify'), // img 压缩 imgagemin = require('gulp-imagemin'), // 清理文件夹 clean = require('gulp-clean'), // 顺序执行 runSequence = require('gulp-run-sequence'), // 给js文件替换文件路径 configRevReplace = require('gulp-requirejs-rev-replace'), // 过滤 filter = require('gulp-filter'), // js校验 eslint = require('gulp-eslint'), // babel 转换es6 babel = require('gulp-babel'), // 本地的测试服务器 connect = require('gulp-connect'), // 本地测试服务器中间件 proxy = require('http-proxy-middleware'), // 服务器的代理插件 modRewrite = require('connect-modrewrite'), // 打开浏览器 open = require('gulp-open'), // html 压缩 htmlmin = require('gulp-htmlmin'), replace = require('gulp-replace'), // 自动编译artTemplate模板成js文件 tmodjs = require('gulp-tmod'); // minifyHtml = require('gulp-minify-html'); 样式处理工作流:编译sass → 添加css3前缀 → 合并 → // 压缩css →添加版本号 gulp.task('style', function(e) { // 过滤非sass文件 var sassFilter = filter(['**/*.scss'], { restore: true }); return gulp .src(['./src/style/**/*.{scss,css}', '!./src/style/main.css']) // 读取sass文件 .pipe(sassFilter) .pipe(sass()) // 编译sass .pipe(sassFilter.restore) .pipe( autoprefixer({ // 兼容css3 browsers: ['last 2 versions'], // 浏览器版本 cascade: true, // 美化属性,默认true add: true, // 是否添加前缀,默认true remove: true, // 删除过时前缀,默认true flexbox: true // 为flexbox属性添加前缀,默认true }) ) .pipe(concat('main.css')) // 合并css .pipe( cleanCss({ // 压缩css compatibility: 'ie8', // 保留所有特殊前缀 当你用autoprefixer生成的浏览器前缀,如果不加这个参数,有可能将会删除你的部分前缀 keepSpecialComments: '*' }) ) .pipe(rev()) // 文件名加MD5后缀 .pipe(gulp.dest('./dist/style/')) // 输出目标文件到dist目录 .pipe(rev.manifest()) // 生成一个rev-manifest.json .pipe(gulp.dest('./src/style/')); // 将 rev-manifest.json 保存到 src目录 }); // 替换目标html文件中的css版本文件名,js版本的文件名,html 压缩 gulp.task('html', function(e) { return gulp .src(['./src/**/*.json', './src/**/*.html', '!./src/template/**']) // - 读取 rev-manifest.json 文件以及需要进行css名替换的文件 .pipe(revCollector({ replaceReved: true })) // - 执行html文件内css文件名的替换和js文件名替换 .pipe( htmlmin({ removeComments: true, // 清除HTML注释 collapseWhitespace: true, // 压缩HTML // collapseBooleanAttributes: true, //省略布尔属性的值 <input checked="true"/> ==> <input /> removeEmptyAttributes: true, // 删除所有空格作属性值 <input id="" /> ==> <input /> removeScriptTypeAttributes: true, // 删除<script>的type="text/javascript" removeStyleLinkTypeAttributes: true, // 删除<style>和<link>的type="text/css" minifyJS: true, // 压缩页面JS minifyCSS: true // 压缩页面CSS }) ) .pipe(gulp.dest('./dist/')); // - 替换后的文件输出的目录 }); // 图片压缩 gulp.task('imgmin', function(e) { return gulp .src('./src/asset/**/*.{png,jpg,gif,ico}') .pipe( imgagemin({ optimizationLevel: 5, // 类型:Number 默认:3 取值范围:0-7(优化等级) progressive: true, // 类型:Boolean 默认:false 无损压缩jpg图片 interlaced: true, // 类型:Boolean 默认:false 隔行扫描gif进行渲染 multipass: true // 类型:Boolean // 默认:false 多次优化svg直到完全优化 }) ) .pipe(gulp.dest('./dist/asset')); }); // js 压缩添加版本 gulp.task('js', function(e) { return gulp .src(['./src/**/*.js', '!./src/lib/**']) .pipe(eslint()) .pipe( eslint.results(results => { // Called once for all ESLint results. console.log(`JS总校验文件: ${results.length}`); console.log(`JS警告个数:: ${results.warningCount}`); console.log(`JS错误个数: ${results.errorCount}`); }) ) .pipe(eslint.format()) .pipe(eslint.failAfterError()) .pipe(babel({ presets: ['env'] })) .pipe(uglify()) .pipe(rev()) .pipe(gulp.dest('./dist')) .pipe(rev.manifest()) .pipe(gulp.dest('./src/js/')); }); // 给requirejs引用的文件修改版本号的路径 gulp.task('revjs', function() { return gulp .src('./dist/*.js') .pipe( configRevReplace({ manifest: gulp.src('./src/js/rev-manifest.json') }) ) .pipe(uglify()) .pipe(gulp.dest('dist/')); }); // 打包要复制的路径2 var copyPathArr = ['./src/lib/**/*', './src/asset/**/*', './src/*.ico']; // 拷贝gulp文件 gulp.task('copy', function(e) { return gulp.src(copyPathArr, { base: './src' }).pipe(gulp.dest('./dist/')); }); // 清理dist目录下的历史文件 gulp.task('cleanDist', function() { gulp .src(['dist/style/**', 'dist/js/**', 'dist/*.js'], { read: false }) .pipe(clean()); }); gulp.task('dist', [], function() { runSequence( 'cleanDist', 'tpl', 'style', 'js', 'revjs', 'html', 'copy', 'imgmin' ); }); gulp.task('tpl', function() { // 拿到所有的路径 let basePath = path.join(__dirname, 'src/template'); let files = fs.readdirSync(basePath); files.forEach((val, index) => { let dirPath = path.join(basePath, val); let stat = fs.statSync(dirPath); if (!stat.isDirectory()) { return; } var fileter = 'src/template/' + val + '/**/*.html'; console.log(fileter); gulp .src('src/template/' + val + '/**/*.html') .pipe( tmodjs({ templateBase: 'src/template/' + val, runtime: val + '.js', compress: false }) ) // 自动生成的模板文件,进行babel转换,会报错,此转换插件已经停更,所以间接改这个bug // 参考bug:https://github.com/aui/tmodjs/issues/112 主要是this → window .pipe(replace('var String = this.String;', 'var String = window.String;')) .pipe(gulp.dest('src/js/tmpl/')); }); }); // ============= 开发样式处理 gulp.task('style:dev', function(e) { var sassFilter = filter(['**/*.scss'], { restore: true }); return gulp .src(['./src/style/**/*.{scss,css}', '!./src/style/main.css']) // 读取sass文件 .pipe(sourcemaps.init()) .pipe(sassFilter) .pipe(sass()) // 编译sass .pipe(sassFilter.restore) .pipe(concat('main.css')) .pipe(sourcemaps.write()) .pipe(gulp.dest('./src/style/')); }); // 开发监控sass文件变化编译sass到css文件 可以增加es6的编译,jslint gulp.task('dev', ['open'], function() { gulp.watch('src/style/**', ['style:dev']); gulp.watch('src/template/**', ['tpl']); }); // 配置测试服务器 gulp.task('devServer', function() { connect.server({ root: ['./src'], // 网站根目录 port: 38900, // 端口 livereload: true, middleware: function(connect, opt) { return [ modRewrite([ // 设置代理 '^/api/(.*)$ http://192.168.0.102:8080/mockjsdata/1/api/$1 [P]' ]) ]; } }); }); // 启动浏览器打开地址 gulp.task('open', ['devServer'], function() { gulp.src(__filename).pipe(open({ uri: 'http://localhost:38900/index.html' })); });