Skip to content

当我们使用上一节的资源处理方式,已经能够将对应资源进行打包处理,但实际应用中,我们不想每次都手动打包,或者需要本地起一个服务器,又甚至需要代理到服务器等等等等,这时候就需要一些辅助工具来帮助我们完成这些工作。

另外,额外说明的是,我们在上一节,已经创建了名为 jscsshtml 以及 image 的任务。

在本节相关代码中,读者需加以关联逻辑、注意区别。

Copy Static

在打包时,我们可能需要将一些资源直接拷贝到 dist 目录下,而不需要做任何处理。

安装 gulp-copy

shell
yarn add gulp-copy -D
js
const { src, dest } = require('gulp')
const gulpCopy = require('gulp-copy')

function copy () {
  return src('lib/*')
    // prefix 表示拷贝时去掉lib目录
    .pipe(gulpCopy('dist', { prefix: 1 }))
}

exports.copy = copy

Clean Directory

在每次打包之前,我们都需要清理一下 dist 目录,以免出现冗余文件。

安装 gulp-clean

shell
yarn add gulp-clean -D
js
const { src, dest } = require('gulp')
const gulpClean = require('gulp-clean')

function clean () {
  // The { allowEmpty: true } options allows the task to continue running even if the dist directory is empty.
  return gulp.src('dist', { allowEmpty: true })
    .pipe(gulpClean())
}

exports.clean = clean

// 另外,应该在其他任务之前,优先执行clean任务,因此此处需要使用series串行及parallel并行。
exports.build = gulp.series(clean, gulp.parallel(js, css, html, image, copy))

Watch Files

watch 方法可以用来监听文件的变化,当文件发生变化时,可以执行对应的任务。

js
const { watch } = require('gulp')

function watchTask() {
  watch('js/*.js', js)
  watch('styles/*.*', css)
  watch('*.html', html)
  watch('images/*.{jpg,png}', image)
}

exports.watch = watchTask

虽然 watch 这里看起来同步的方式,但是由于 watch 在执行时,node 进程并没有停止,因此不会报错 Did you forget to signal async completion?

官网中有指明:

由于文件监控程序会让你的 Node 进程保持持续运行,因此不会有错误或警告产生。由于进程没有退出,因此无法确定任务(task)是否已经完成还是运行了很久很久了。

js
// 测试gulp对异步任务的限制
exports.async = function () {
  // setTimeout(() => {
  //   console.log('async')
  // }, 1000)

  // 不中止进程的话 不会提示Did you forget to signal async completion?
  process.stdin.resume()
  process.stdin.setEncoding('utf8')
  process.stdin.on('data', function(data) {
    console.log(data.trim())
  })
}

Dev Server

Dev Server 可以帮助我们在本地起一个服务器,以便我们在开发时,可以直接在浏览器中查看效果。

安装 gulp-connect

shell
yarn add gulp-connect -D
js
const { src, dest } = require('gulp')
const gulpConnect = require('gulp-connect')

function connect () {
  gulpConnect.server({
    name: 'Dev Server',
    root: 'dist', // 服务根目录 设置成dist 默认为gulpfile.js所在目录
    port: 8080, // 端口号 默认8080
    host: '10.0.41.35', // 主机名 默认localhost 
    livereload: true, // 是否启用实时刷新 默认false 此处开启之后,还需要设置pipe(gulpConnect.reload())才能生效
    middleware: function (connect, opt) {
      return []
    }
  })
}

// 也可以写成gulp.series(gulp.parallel(connect, watch)) 效果一致
exports.start = gulp.parallel(connect, watch)

设置 livereloadtrue 启用之后,还需要在 jscsshtml 以及 image 任务后面,加上 pipe(gulpConnect.reload()) 才能生效。

此处以 js 为例,其他任务类同:

js
const { src, dest } = require('gulp')
const gulpConnect = require('gulp-connect')

function js() {
  return src('js/*.js')
    .pipe(babel({
      presets: ['@babel/env']
    }))
    .pipe(uglify())
    .pipe(dest('dist/js'))
    .pipe(gulpConnect.reload())
}

exports.js = js

TIP

启用 livereload 后,资源变化后,页面会自动重载

但要注意的是,启用 livereload 的话,则不推荐将 host 设置为 localhost,因为在 chrome 浏览器中,会报错 webSocket connection to 'ws://localhost:35729/livereload' failed

这是因为 Chrome doesn't allow unsecure websocket (ws) connections to localhost (only wss, so you should setup a TLS certificate for your local web/websocket server). However the same should work fine with Firefox.

更多详情可见:stackoverflow

HTTP Proxy

HTTP Proxy 可以帮助我们在本地开发时解决跨域问题。

安装 http-proxy-middleware:

shell
yarn add http-proxy-middleware -D
js
const { src, dest } = require('gulp')
const gulpConnect = require('gulp-connect')
const { createProxyMiddleware } = require('http-proxy-middleware')

function connect () {
  gulpConnect.server({
    name: 'Dev App',
    root: 'dist',
    port: 8080,
    host: '10.0.41.35',
    livereload: true,
    middleware: function (connect, opt) {
      return [
        createProxyMiddleware('/api', {
          target: 'https://movie.douban.com/subject/1830575/',
          changeOrigin: true,
          pathRewrite: {
            '^/api': ''
          }
        })
      ]
    }
  })
}

exports.start = gulp.parallel(connect, watch)

Module bundler

gulp 本身是 Task runner,它并不能将 EsModuleCommonJS 转换成浏览器能够执行的 API

因此,webpackrollup 这俩种工具才属于模块打包机Module bundler)。

其中,webpack 能够打包处理 EsModuleCommonJS,而 rollup 专门针对 EsModule

gulp 项目中,如果使用了 EsModule,那么可以使用 gulp-webpackgulp-better-gulp 来处理。

gulp-better-gulp 为例:

js
const rollup = require('gulp-better-gulp')

function gulpJs () {
  return gulp.src(globPaths.js)
    .pipe(sourcemaps.init())
    .pipe(rollup({}, 'iife'))
    .pipe(babel({
      presets: ['@babel/env'],
      plugins: [
        [
          "@babel/plugin-transform-runtime", {
            corejs: 3
          }
        ]
      ]
    }))
    .pipe(uglifyJs())
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('dist/js'))
    .pipe(gulpConnect.reload())
}