使用Gulp&Webpack构建前端脚手架

使用构建工具的原因

自从 nodejs 诞生以来,很多前端工作都可以在本地进行,其中就包括 JS 调试和工作流等。最开始应用工作流的插件叫 Grunt,因为起步早,因此插件多,内容丰富,但是执行速度比较慢。后来就出了 Gulp,和 Grunt 类似,不过由于 Gulp 利用了 nodejs 的流(stream),因此效率十分给力,唯一的缺点就是插件没有 Grunt 多,不过发展到今天,插件也逐渐多起来了。大家有可能还听说过 Webpack,最近几年非常热门,但是 Webpack 的亮点主要是模块打包,可以和 Gulp 配合使用。

使用 Gulp 不仅能对资源进行优化,而且在开发过程中能够通过配置自动完成很多重复的任务,让我们可以专注于代码,提高工作效率;而使用 Webpack,可以把众多模块打包成一个文件,减少网络请求次数。尽管 Gulp 和 Webpack 的许多功能可以互相代替,但是它们各自的核心功能还是不同的。

用 Gulp 完成的工作:

  • 模块化
  • 编译 sass
  • 合并优化压缩 css
  • 校验压缩 js
  • 优化图片
  • 添加文件指纹(md5)
  • 浏览器实时刷新

用 Webpack 完成的工作:

  • 打包所有 js 文件

安装配置 Gulp 和 Webpack

首先安装好最新的 nodejs(10.0.0)和 npm(6.0.0)。然后全局安装 gulp,由于我用的 ES6 的语法,因此还安装了 Babel:

  • 第一步:项目中新建以下目录结构
1
2
3
4
5
6
7
8
9
10
dist
src
├──layout/
├──sass/
├──img/
├──js/
└──index.html
.babelrc
package.json
gulpfile.babel.js
  • 第二步:完成 package.json
1
2
3
4
5
6
7
8
9
10
11
{
"name": "gulp-build",
"version": "1.0.0",
"description": "Gulp.js",
"private": true,
"scripts": { // 提供 npm 脚本用于编译
"start": "gulp",
"watch": "gulp watch",
"build": "gulp build --production"
}
}
  • 第三步:完成 .babelrc
1
2
3
4
{
"presets": ["env"], // 使用 babel 默认的转换规则
"plugins": []
}
  • 第四步:完成 gulpfile.babel.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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
* Gulp Build File
* Version: 2.0.0
* User: NickHopps
*/

import gulp from 'gulp';
import plugins from 'gulp-load-plugins';
import yargs from 'yargs';
import browser from 'browser-sync';
import named from 'vinyl-named';
import webpackStream from 'webpack-stream';
import webpack from 'webpack'

const $ = plugins({
rename: {
'gulp-rev-append': 'rev',
'gulp-file-include': 'fileinclude'
}
});

const PORT = 8000;

const PRODUCTION = !!(yargs.argv.production);

const PATHS = {
pages: {
src: 'src/*.html',
dist: 'dist/'
},
styles: {
vendor: ['node_modules/foundation-sites/scss', 'node_modules/bootstrap/scss/'],
src: 'src/sass/**/*.scss',
dist: 'dist/css/'
},
scripts: {
src: 'src/js/**/*.js',
dist: 'dist/js/'
},
images: {
src: 'src/img/**/*.{jpg,jpeg,png}',
dist: 'dist/img/'
}
};

const WPCONFIG = {
mode: PRODUCTION ? 'production' : 'development',
module: {
rules: [{
test: /.js$/,
use: [{
loader: 'babel-loader'
}]
}]
},
devtool: '#source-map'
};

/* Task to build files */
gulp.task('build', gulp.series(clean, gulp.parallel(html, image, sass, javascript)));

/* Default Task */
gulp.task('default', gulp.series('build', server, watch));

/* Task to watch */
gulp.task('watch', gulp.series(server, watch));

function html() {
return gulp.src(PATHS.pages.src)
.pipe($.fileinclude())
.pipe($.cached('html'))
.pipe($.rev())
.pipe(gulp.dest(PATHS.pages.dist));
}

function image() {
return gulp.src(PATHS.images.src)
.pipe($.cached('image'))
.pipe($.if(PRODUCTION, $.imagemin({progressive: true})))
.pipe(gulp.dest(PATHS.images.dist));
}

function sass() {
return gulp.src(PATHS.styles.src)
.pipe($.plumber({errorHandler: $.notify.onError('Error: <%= error.message %>')}))
.pipe($.sourcemaps.init())
.pipe($.sass({includePaths: PATHS.styles.vendor}))
.pipe($.cached('sass'))
.pipe($.autoprefixer('last 2 version'))
.pipe($.if(PRODUCTION, $.cssnano()))
.pipe($.if(PRODUCTION, $.rename({suffix: '.min'})))
.pipe($.sourcemaps.write(''))
.pipe(gulp.dest(PATHS.styles.dist))
.pipe(browser.reload({ stream: true }));
}

function javascript() {
return gulp.src(PATHS.scripts.src)
.pipe(named())
.pipe($.cached('script'))
.pipe(webpackStream(WPCONFIG, webpack))
.pipe($.if(PRODUCTION, $.rename({suffix: '.min'})))
.pipe(gulp.dest(PATHS.scripts.dist));
}

function clean() {
return gulp.src(`${PATHS.pages.dist}/*`)
.pipe($.clean());
}

function server(done) {
browser.init({
server: PATHS.pages.dist,
port: PORT
});
done();
}

function watch() {
gulp.watch('src/**/*.html').on('all', gulp.series(html, browser.reload));
gulp.watch('src/img/**/*.{jpg,jpeg,png}').on('all', gulp.series(image, browser.reload));
gulp.watch('src/sass/**/*.scss').on('all', sass);
gulp.watch('src/js/**/*.js').on('all', gulp.series(javascript, browser.reload));
}
  • 第四步:安装项目开发依赖
1
npm i -D babel-core babel-loader babel-preset-env browser-sync gulp gulp autoprefixer gulp-babel gulp-cached gulp-clean gulp-cssnano gulp-file-include gulp-if gulp-imagemin gulp-load-plugins gulp-notify gulp-plumber gulp-rename gulp rev-append gulp-sass gulp-sourcemaps vinyl-named webpack webpack-stream yargs

使用的各个包及其功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
babel-core        : babel 的核心部分
babel-loader : babel 提供给外部的加载器
babel-preset-env : babel 的 env 规则
browser-sync : 提供浏览器实时更新
gulp : gulp 主程序
gulp-autoprefixer : gulp 的 autoprefixer 插件
gulp-babel : gulp 的 babel 插件
gulp-cached : 缓存 gulp 的文件流
gulp-clean : 清理文件
gulp-cssnano : 压缩 CSS
gulp-file-include : 在 HTML 中使用 include
gulp-if : 提供 if 判断功能
gulp-imagemin : 压缩图片
gulp-load-plugins : 加载 gulp 插件
gulp-notify : 弹窗提醒
gulp-plumber : 处理 gulp 的错误流
gulp-rename : 重命名
gulp-rev-append : 给文件添加 MD5
gulp-sass : sass 编译插件
gulp-sourcemaps : 生成 sourcemap
vinyl-named : 给 vinyl 文件命名
webpack : webpack 主程序
webpack-stream : 把 webpack 转成流供 gulp 处理
yargs : 提供命令参数功能

使用 Gulp 和 Webpack 构建项目

创建好自己的项目文件之后,可运行下列命令构建:

1
2
3
npm start # 相当于执行 gulp
npm run watch # 相当于执行 gulp watch
npm run build # 相当于执行 gulp build --production

使用Gulp&Webpack构建前端脚手架
https://infiniture.cn/2019/09/30/使用Gulp&Webpack构建前端脚手架/
作者
NickHopps
发布于
2019年9月30日
许可协议