本文最后更新于:2023年3月19日 晚上
本文转自:https://www.jianshu.com/p/1a255e740710、https://juejin.im/post/6844903870456414216、https://segmentfault.com/a/1190000019650765
1. Gulp
gulp 是基于 Nodejs 的自动任务运行器,能自动化的完成 javascript/coffee/sass/less/html/image/css 等文件的的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。
通俗来讲,gulp 就是一个处理项目文件的工具,通过 gulp 建立一些任务,当我们对相应文件进行修改之后,会自动触发这些任务,自动的对文件进行了处理。不用我们手动的、反复的去处理这些文件,大大提高了我们的工作效率。
举个栗子:
现在已经开始用 less 来写样式(less 的优点很多,写起来要比 css 快很多,在此不多说,盆友们可以上网了解 less),在 html 使用这些样式,需要用一些工具将.less 文件转换为.css 文件,如果用 Sublime 敲代码的盆友可能知道它有插件自动生成 css,但是.less 和生成的.css 文件在同一目录下。
目录.png
这只是文件比较少的情况,如果文件比较多呢?是不是也是一件很头疼的事情…
下面,上代码带你用 gulp 解决令人头疼的问题 ^ _ ^
前提我们已经创建好一个 nodejs 项目,这里我们安装两个插件: gulp 和 gulp-less(编译 less 文件)
npm install gulp --save-dev
npm install gulp-less --save-dev
新建 gulpfile.js 文件
1 2 3 4 5 6 7 8 9 10
| // 说明:gulpfile.js是gulp项目的配置文件,放项目根目录即可。 // 导入工具包 var gulp = require('gulp'), less = require('gulp-less'); // 定义一个testLess任务(自定义任务名称) gulp.task('testLess', function () { gulp.src('public/stylesheets/style.less') //该任务针对的文件 .pipe(less()) //该任务调用的模块 .pipe(gulp.dest('public/css')); //将会在public/css下生成style.css });
|
接下来,我们只需要在命令行输入gulp testLess
我们就可以在 public/css 下看到生成的 style.css 文件了
but 这显然是我们手动来生成的,我们需要的是让它自动生成,那好吧,现在我们需要一个东西能够来监听 less 文件的修改,然后自动去执行任务
1 2 3
| gulp.task('testWatch', function () { gulp.watch('public/stylesheets/*.less', ['testLess']); });
|
好了,现在我们运行gulp testWatch
当修改了 less 文件保存,会看到 css 文件也跟着变了^ _ ^
补充一点,我们在一个项目里使用 gulp 的时候,我们想启动项目,就自动启动了 testWatch,而不是启动项目之后,我们还要手动启动 testWatch,那么,加上下面代码:
1
| gulp.task('default',['testWatch']); // 指定gulp默认启动任务
|
2. Grunt
Grunt 基于 Node.js ,用 JS 开发,这样就可以借助 Node.js 实现跨系统跨平台的桌面端的操作,例如文件操作等等。此外,Grunt 以及它的插件们,都作为一个 包 ,可以用 NPM 安装进行管理。
Grunt 依赖 Node.js 所以在安装之前确保你安装了 Node.js。然后开始安装 Grunt。
Grunt 可以帮助我们减少很多的工作量,比如:检查每个 JS 文件语法、合并两个 JS 文件、将合并后的 JS 文件压缩、将 SCSS 文件编译等,包括我们上面提到的试用 gulp 将.less 文件转换为.css 文件,grunt 也是可以实现的,下面我们用 grunt 实现 gulp 的转换 less 的功能:
和 gulp 一样,首先我们先安装 grunt 以及 grunt-contrib-less
npm install grunt --save-dev
npm install grunt-contrib-less --save-dev
新建 Gruntfile.js 文件
使用 grunt,主要有三块代码:任务配置代码、插件加载代码、任务注册代码。
任务配置代码就是调用插件配置一下要执行的任务和实现的功能,插件加载代码就是把需要用到的插件加载进来,任务注册代码就是注册一个 task,里面包含刚在前面编写的任务配置代码。
需要注意的是,grunt 的配置代码放到
1 2 3
| module.exports = function(grunt)
|
里面,没有为什么…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| // 1. 任务配置代码 grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), //less插件配置 less: { main: { options: { compress: false, yuicompress: false }, files: { './public/css/global.css': './public/less/*.less' } } } }); // pkg 功能是读取 package.json 文件,并把里面的信息获取出来,方便在后面任务中应用 // 2. 插件加载代码 grunt.loadNpmTasks('grunt-contrib-less'); // 3. 任务注册代码 grunt.registerTask('lessjs', ['less']);
|
到这一步,我们已经做好 grunt 配置文件了,控制台输入grunt lessjs
即可实现同 gulp 一样的效果。
同样的,这一样使我们手动生成的,grunt 能像 gulp 一样自动为我们生成吗?答案是肯定的,grunt 同样也有 watch。
首先安装插件 grunt-contrib-watch
npm install grunt-contrib-watch --save-dev
在任务配置代码添加一个 watch 任务:
1 2 3 4 5 6
| watch: { lesses: { files: ['./public/less/*.less'], tasks: ['less'] } }
|
同样需要加载插件代码,任务注册代码:
1 2
| grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('default', ['less', 'watch']);
|
3. grunt 与 gulp 区别
上面的学习,我们知道 grunt 和 gulp 能实现同样的功能,那么它们有什么区别?分别在什么场景下使用呢?
在我看来,其实 grunt 和 gulp 两个东西的功能是一样的,只不过是任务配置 JS 的语法不同,Gulp 配置文件的写法更加通俗易懂,上手更快。但是 Gulp 的插件感觉不如 Grunt,只能满足基本的工作需要;而 Grunt 官方提供了一些常见的插件,满足大部分日常工作,而且可靠值得信赖,而 Gulp 好像没有太多官方出品,各种插件不太规范。
至于在什么场景下使用哪个?这个看个人需要吧,grunt 远远不止上面写的那么简单,如果 gulp 能满足平时工作需要,可以使用 gulp。
总之,用网友的话说,Grunt 和 Gulp 就像 iPhone 与 Android 一样,一个质量高学习难一点,一个学起来简单但是有点那个,你懂得。
什么是 gulp?
gulp 文档上面有这么一句话 ,也就是说 gulp 是一个自动化构建工具; gulp 的一些功能如下(包括但不限于):
gulp 或 grunt 和 webpack 的区别
其实 Webpack 和另外两个并没有太多的可比性
Gulp/Grunt 是一种能够优化前端的开发流程的工具,而 WebPack 是一种模块化的解决方案,不过 Webpack 的优点使得 Webpack 在很多场景下可以替代 Gulp/Grunt 类的工具。
Grunt 和 Gulp 的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。
Webpack 的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack 将从这个文件开始找到你的项目的所有依赖文件,使用 loaders 处理它们,最后打包为一个(或多个)浏览器可识别的 JavaScript 文件。
上述内容转自@zhangwang 的入门 Webpack,看这篇就够了
gulp 起步
傻瓜式起步照搬官网文档 1.安装
1 2 3 4 5 6 7
| $ npm install -g gulp 或者 $ npm install --global gulp
$ npm install --save-dev gulp 复制代码
|
2.在项目根目录下创建一个名为 gulpfile.js 的文件:
1 2 3 4 5
| var gulp = require('gulp'); gulp.task('default', function() { }); 复制代码
|
3.运行 gulp:
默认的名为 default 的任务(task)将会被运行,在这里,这个任务并未做任何事情。 具体详情可以查看gulpjs.com 文档
项目搭建
新建一个项目 gulp-test 环境:
1 2 3
| $ node -v // v9.1.0 $ npm -v // 6.5.0 复制代码
|
1.新建文件以下文件如下
1 2 3 4 5 6 7 8
| gulp-test/ css/ index.scss js/ helloworld.js index.html gulpfile.js 复制代码
|
其中 gulpfile.js 是我们 gulp 的配置文件,启动 gulp 默认会找个这个文件并执行; 2.接下来安装依赖
一直按回车 Enter 初始化 package.json 文件(小技巧: npm iniy -y 可以免去繁琐的 enter 步骤) 此时我们的目录结构是这样了
1 2 3 4 5 6 7 8 9
| gulp-test/ css/ index.scss js/ helloworld.js index.html gulpfile.js package.json 复制代码
|
安装依赖
1 2 3 4 5 6 7 8 9 10 11 12 13
| npm i --save-dev gulp npm i --save-dev gulp-uglify npm i --save-dev gulp-concat npm i --save-dev gulp-jshint npm i --save-dev gulp-rename npm i --save-dev gulp-sass npm i --save-dev gulp-minify-css npm i --save-dev del
npm i --save-dev gulp-connect npm i --save-dev browser-sync npm i --save-dev gulp-livereload 复制代码
|
这里页面实时刷新只讲这个gulp-connect ,其他详情可以参照Browsersync和文章gulp-livereload
安装完依赖后配置 gulpfile.js 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| // 定义依赖项和插件 const gulp=require('gulp'); const uglify=require('gulp-uglify'); //js压缩 const concat=require('gulp-concat'); //文件合并 const jshint = require('gulp-jshint'); //js语法检测 const rename = require('gulp-rename'); // 重命名 const sass = require('gulp-sass'); // 编译scss const minifycss = require('gulp-minify-css'); // 压缩css // const livereload = require('gulp-livereload'); // 自动刷新页面 const del = require('del'); //文件删除 const connect = require('gulp-connect'); // 自动刷新页面 gulp.task('server', function() { connect.server({ port: 8080, //指定端口号,在浏览器中输入localhost:8080就可以直接访问生成的html页面 root: './', //指定html文件起始的根目录 livereload: true //启动实时刷新功能(配合上边的connect.reload()方法同步使用) }); }); // 定义名为 "my-task" 的任务压缩js gulp.task('my-task-js', function(){ gulp.src('./js/*.js') .pipe(jshint()) .pipe(uglify()) .pipe(concat('all.js')) .pipe(rename({suffix: '.min'})) .pipe(gulp.dest('./dist/js')) .pipe(connect.reload()) }); // 定义名为 "my-task-css" 的任务编译scss压缩css gulp.task('my-task-css', function() { gulp.src('./css/*.scss') .pipe(sass().on('error', sass.logError)) .pipe(concat('all.css')) .pipe(rename({suffix: '.min'})) .pipe(minifycss()) .pipe(connect.reload()) .pipe(gulp.dest('./dist/css')) }); gulp.task('html', function(){ gulp.src('*.html') .pipe(gulp.dest('dist/html')) .pipe(connect.reload()) }) //执行压缩前,先删除以前压缩的文件 gulp.task('clean', function() { return del(['./dist/css/all.css', './dist/css/all.min.css', './dist/all.js','./dist/all.min.js', './dist/html']) }); // 定义默认任务 gulp.task('default',['clean'],function() { gulp.start('my-task-js', 'my-task-css', 'watch', 'server' ); }); // 任务监听 gulp.task('watch', function() { // Watch.js files gulp.watch('./js/*.js', ['my-task-js']); // Watch .scss files gulp.watch('./css/*.scss', ['my-task-css']); // Watch .html files gulp.watch('./*.html', ['html']); // Watch any files in dist/, reload on change // gulp.watch(['dist/!**']).on('change', livereload.changed); }); 复制代码
|
大概讲解一下 gulpfile.js:
1 2 3 4 5 6 7 8 9 10 11 12 13
| // ... // 定义名为 "my-task" 的任务压缩js gulp.task('my-task-js', function(){ gulp.src('./js/*.js') .pipe(jshint()) //js检测 .pipe(uglify()) //js压缩 .pipe(concat('all.js')) //合并为all.js .pipe(rename({suffix: '.min'})) // 重命名为all.mim.js .pipe(gulp.dest('./dist/js')) //输出到/dist/js目录 .pipe(connect.reload()) // 更新页面 }); // ... 复制代码
|
gulp.task是 gulp 的 api 定义一个使用 Orchestrator 实现的任务(task) 如上我们定义了my-task-js,my-task-css,html,clean,default,watch,server等任务,其中:
my-task-js 是将 符合所提供的匹配模式的 js 进行检测(gulp-jshint)、压缩(gulp-uglify)、合并(gulp-concat)、重命名(gulp-rename)、输出(gulp.dest)到/dist/js 目录下;
my-task-css 是将 符合所提供的匹配模式的 sass 进行编译(gulp-sass)、压缩(gulp-uglify)、合并(gulp-concat)、重命名(gulp-rename)、输出(gulp.dest)到/dist/css 目录下;
html 是将 符合所提供的匹配模式的 html 进行监听,如果有变化则 connect.reload()
clean 是如果任务重新启动时 删除旧文件;
default gulp 默认启动的任务
watch gulp 的 api 监视文件,并且可以在文件发生改动时候做一些事情。它总会返回一个 EventEmitter 来发射(emit) change 事件。
server 依赖 gulp-connect 启动一个服务器
1 2 3 4 5 6 7 8
| gulp.task('server', function() { connect.server({ port: 8080, //指定端口号,在浏览器中输入localhost:8080就可以直接访问生成的html页面 root: './', //指定html文件起始的根目录 livereload: true //启动实时刷新功能(配合上边的connect.reload()方法同步使用) }); }); 复制代码
|
配置完 gulpfile.js 之后,我们给 js 和 css 及 html 加点东西:
首先 js/helloworld.js
1 2 3
| console.log('hello world') 复制代码
|
css/index.scss
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
$fontColor: #red; $backColor: aqua;
div { p { font-weight: bold; font-size: 20px; color: $fontColor; } } div{ background: $backColor; } 复制代码
|
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>gulp-study</title> <link href=/dist/css/all.min.css rel=stylesheet> </head> <body> <div id="firstDiv"> <p>我是gulp</p> <p>hello world</p> </div> <p>我是p标签</p> <p>我是p标签</p> </body> <script src="/dist/js/all.min.js"></script> </html> 复制代码
|
运行 gulp
浏览器效果:
接下来我们修改 helloworld.js 来看看是否能实时刷新 修改如下:
1 2 3 4 5
| console.log('hello world'); let firstDiv = document.getElementById('firstDiv') console.log(firstDiv) 复制代码
|
按保存之后,终端给我们报了一个错:
查看 js 发现我们用了 es6 语法的声明语句 但当前 gulp 无法处理 es6 语法,有问题解决问题,es6=>es5
解决方案: 安装 gulp-babel babel-core babel-preset-es2015
1 2
| npm i --save-dev gulp-babel babel-core babel-preset-es2015 复制代码
|
gulpfile.js 修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // ... const babel = require('gulp-babel'); // ... // 定义名为 "my-task" 的任务压缩js gulp.task('my-task-js', function(){ gulp.src('./js/*.js') .pipe(babel()) .pipe(jshint()) .pipe(uglify()) .pipe(concat('all.js')) .pipe(rename({suffix: '.min'})) .pipe(gulp.dest('./dist/js')) .pipe(connect.reload()) }); // ... 复制代码
|
运行
依然报上面的错;找了一些原因发现,虽然安装了相关依赖,却没有配置.babelrc 文件,即 babel 还没转化 es6
根目录添加.babelrc 文件
1 2 3 4
| { "presets": ["es2015"] } 复制代码
|
重新运行:
查看 dist 下的 js 文件
改变 helloworld.js 检查页面是否刷新
1 2 3 4 5 6
| console.log('hello world'); let firstDiv = document.getElementById('firstDiv') console.log(firstDiv) firstDiv.style.backgroundColor = 'yellow'; 复制代码
|
保存,页面的天空蓝换成你们喜欢的 yellow 颜色
修改 index.scss 查看是否会刷新页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
$fontColor: #red; $backColor: aqua;
div { p { font-weight: bold; font-size: 20px; color: $fontColor; } } div{ background: $backColor; width: 400px; height: 400px; margin: 0 auto; } 复制代码
|
最后修改 index.html 查看是否会刷新页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>gulp-study</title> <link href=/dist/css/all.min.css rel=stylesheet> </head> <body> <div id="firstDiv"> <p>我是gulp</p> <p>hello world</p> </div> <div> <p>我是真的皮</p> </div> </body> <script src="/dist/js/all.min.js"></script> </html> 复制代码
|
随着前端发展如日冲天,前端项目也越来越复杂,得益于 Nodejs 的发展,前端模块化、组件化、工程化也大势所趋。这些年 Grunt、Gulp 到 Webpack 随着工程化的发展都大行其道。
前端工程化的早期,主要是解决重复任务的问题。Grunt、Gulp 就是其中代表。比如: 压缩、编译 less、sass、地址添加 hash、替换等。
Grunt 官网中就说:
对于需要反复重复的任务,例如压缩(minification)、编译、单元测试、linting 等,完成大部分无聊的工作。
而如今的 Webpack 更像一套前端工程化解决方案。利用强大插件机制,解决前端静态资源依赖管理的问题。
Webpack 作者 Tobias 回复与 Grunt Gulp NPM 脚本的比较
Tobias: NPM 脚本对我而言足矣。实际上,说 webpack 是 Grunt/Gulp 的替代器并不完全准确。Grunt 和 Gulp 以及 NPM 脚本都是任务执行程序。
Webpack 是**模块打包程序**。这两类程序的目标不一样。但 webpack 简化了必须“过度使用”Grunt 和 Gulp 和 NPM 脚本才能实现的 Web 开发任务也是事实。NPM 脚本才是 Grunt 和 Gulp 的替代品。
不过,除了纯粹的构建之外,任务运行程序也有存在的理由,比如部署、代码检查、版本管理,等等。
Webpack 与 Grunt、Gulp 运行机制
1 2 3 4 5 6
| # grunt gulp 思路 【遍历源文件】->【匹配规则】->【打包】 做不到按需加载,对打包的资源,是否用到,打包过程不关心。 # webpack 【入口】->【模块依赖加载】->【依赖分析】->【打包】 在加载、分析、打包的过程中,可以针对性的做一些解决方案。比如:code split(拆分公共代码)
|
Grunt 与 Gulp 性能比较
Grunt: 每个任务处理完成后存放在本地磁盘.tmp 目录中,有本地磁盘的 I/O 操作,会导致打包速度比较慢。
**Gulp: **gulp 与 grunt 都是按任务执行,gulp 有一个文件流的概念。每一步构建的结果并不会存在本地磁盘,而是保存在内存中,下一个步骤是可以使用上一个步骤的内存,大大增加了打包的速度。