使用构建工具的原因

自从 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:

  • 第一步:项目中新建以下目录结构
dist
src
├──layout/
├──sass/
├──img/
├──js/
└──index.html
.babelrc
package.json
gulpfile.babel.js
  • 第二步:完成 package.json
{
  "name": "gulp-build",
  "version": "1.0.0",
  "description": "Gulp.js",
  "private": true,
  "scripts": { // 提供 npm 脚本用于编译
    "start": "gulp",
    "watch": "gulp watch",
    "build": "gulp build --production"
  }
}
  • 第三步:完成 .babelrc
{
  "presets": ["env"], // 使用 babel 默认的转换规则
  "plugins": []
}
  • 第四步:完成 gulpfile.babel.js
/**
 * 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));
}
  • 第四步:安装项目开发依赖
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

使用的各个包及其功能

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 构建项目

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

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


前端   JavaScript  

自动化 脚手架

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!