放弃webpack,拥抱gulp

简介: gulp定义是:用自动化构建工具增强你的工作流程,是一种基于任务文件流方式,你可以在前端写一些自动化脚本,或者升级历史传统项目,解放你重复打包,压缩,解压之类的操作。

别被标题吓到,哈哈,即使现在vite横空出世,社区光芒四射,两个字很快,但是webpack依旧宝刀未老,依然扛起前端工程化的大梁,但是今天我为啥说要拥抱gulp,因为我们常常吃一道菜,所以要换个口味,这样才营养均衡🤣。


gulp定义是:用自动化构建工具增强你的工作流程,是一种基于任务文件流方式,你可以在前端写一些自动化脚本,或者升级历史传统项目,解放你重复打包,压缩,解压之类的操作。


个人理解gulp是一种命令式编程的体验,更注重构建过程,所有的任务需要你自己手动创建,你会对构建流程会非常清楚,这点不像webpackwebpack就是一个开箱即用的声明式方式,webpack是一个模块化打包工具,内部细节隐藏非常之深,你也不需关注细节,你只需要照着提供的API以及引入对应的loaderplugin使用就行。


言归正传,为了饮食均衡,今天一起学习下gulp


正文开始...


搭建一个简单的前端应用


相比较webpack,其实gulp的项目结构更偏向传统的应用,只是我们借助gulp工具解放我们的一些代码压缩es6编译打包以及在传统项目中都可以使用less体验。


gulp目录下新建01-simple-demo


根目录下生成默认package.json

npm init -y

然后在public目录下新建imagescssjsindex.html


文件结构,大概就这样

bd483346aabbaf4692b51537896ec974.png

然后在安装gulp

npm i gulp --save-dev

在根目录下新建gulpfile.js我们先在gulpfile.js中写入一点内容,测试一下

const defaultTask = (cb) => {
  console.log('hello gulp')
  cb();
}
exports.default = defaultTask;

然后我们在命令行执行

npx gulp

bca6ddb1141adf24f6a3b8a13178bc31.png

当我们执行npx gulp时会默认运行gulpfile.js导出的default,在gulpfile.js导出的任务会注册到gulp任务中


gulp中任务主要分两种,一种是公开任务、另一种是私有任务


公开任务可以直接在命令执行npx gulp xxx调用执行,比如下面的defaultTask就是一个公开任务,只要被导出就是一个公开任务,没有被导出就是一个私有任务。


...
exports.default = defaultTask;

公有任务taskJS

// gulpfile.js
const { src, dest } = require('gulp');
const pathDir = (dir) => {
  return path.resolve(__dirname, dir);
}
// todo 执行ts任务,将js目录下的js打包到dist/js目录下
const taskJS = () => {
  return src(pathDir('public/**/*.js'), { sourcemaps: true }).pipe(dest(pathDir('dist/js')))
}
exports.taskJS = taskJS;

然后你在命令行执行

npx gulp taskJS

071c5f524938c5ad8785e4123fa89aab.png

至此你会发现dist目录下就有生成的js


安装less


npm i less gulp-less --save-dev

css/index.less中写入测试css的代码

@bgcolor: yellow;
@defaultsize: 20px;
body {
  background-color: @bgcolor;
}
h1 {
  font-size: @defaultsize;
}

gulpfile.js中写入编译less的任务,需要gulp-less

const { src, dest } = require('gulp');
const less = require('gulp-less');
const pathDir = (dir) => {
  return path.resolve(__dirname, dir);
}
...
// todo less任务
const taskLess = () => {
  // css目录洗的所有.less文件,dest输出到dist/css目录下
  return src(pathDir('public/css/*.less')).pipe(less()).pipe(dest(pathDir('dist/css')))
}
exports.taskLess = taskLess;

命令行运行npx gulp taskLess,结果如下

f2985390f4c00a5bd09509cb4fac6dd6.png


图片资源


使用一个gulp-image插件对图片进行无损压缩处理

// gulpfile.js
const { src, dest } = require('gulp');
const image = require('gulp-image');
const path = require('path');
const pathDir = (dir) => {
  return path.resolve(__dirname, dir);
}
...
// todo 图片资源
const taskImage = () => {
  return src(pathDir('public/images/*.*')).pipe(image()).pipe(dest(pathDir('dist/images')))
}
exports.taskImage = taskImage;

b3cfedfa311425d3a54707f763e7f9bd.png

一顿操作发现,最新版本不支持esm,所以还是降低版本版本,这里降低到6.2.1版本,这里只能使用ejs方式


然后运行npx gulp taskImage

8d8ec6a45a458f3fd95a97214c7d6834.png

图片压缩得不小


在这之前,我们分别定义了三个不同的任务,gulp导出的任务有公开任务和私有任务,多个公开任务可以串行组合使用


组合任务 series


因此我可以将之前的介个任务组合在一起

// gulpfile.js
const { src, dest, series } = require('gulp');
const less = require('gulp-less');
const image = require('gulp-image');
const path = require('path');
const pathDir = (dir) => {
  return path.resolve(__dirname, dir);
}
// todo js任务
const taskJS = () => {
  return src(pathDir('public/**/*.js'), { sourcemaps: true }).pipe(dest(pathDir('dist/js')))
}
...
// series组合多个任务
const seriseTask = series(taskJS, taskLess, taskLess, taskImage)
exports.seriseTask = seriseTask;

当我在命令行npx gulp seriseTask

9f83e9a9edb3a0d0052eb19e07d9f04c.png

已经在dist生成对应的文件了


编译转换es6


在我们index.js,很多时候是写的es6,在gulp中我们需要一些借助一些插件gulp-babel,另外我们需要安装另外两个babel核心插件@babel/core,@babel/preset-env

npm i gulp-babel @babel/core @babel/preset-env

gulpfile.js中我们需要修改下

...
const babel = require('gulp-babel');
// todo js任务
// 用babel转换es6语法糖
const taskJS = () => {
  return src(pathDir('public/**/*.js'), { sourcemaps: true }).pipe(babel({
    presets: ['@babel/preset-env']
  })).pipe(dest(pathDir('dist/js')))
}

当我们在js/index.js写入一段测试代码

js/index.js
const appDom = document.getElementById('app');
appDom.innerHTML = 'hello gulp';
const fn = () => {
  console.log('公众号:Web技术学苑,好好学习,天天向上')
}
fn();

运行npx gulp seriseTask

4c0122b7f06edd12cc44fa15fd7f46a1.png

箭头函数和const申明的变量就变成了es5


通常情况下,一般打包后的dist下的css或者js都会被压缩,在gulp中也是需要借助插件来完成


压缩js与css


压缩js

...
const teser = require('gulp-terser');
// todo js任务
const taskJS = () => {
  return src(pathDir('public/**/*.js'), { sourcemaps: true }).pipe(babel({
    presets: ['@babel/preset-env']
  })).pipe(teser({
    mangle: {
      toplevel: true // 混淆代码
    }
  })).pipe(dest(pathDir('dist/js')))
}
...

压缩css

...
const uglifycss = require('gulp-uglifycss');
// todo less任务 
const taskLess = () => {
  return src(pathDir('public/css/*.less')).pipe(less()).pipe(uglifycss()).pipe(dest(pathDir('dist/css')))
}
...

在这之前我们在输出dest时候我们都指向了一个具体的文件目录,在src这个api中是创建流,从文件中读取vunyl对象,本身也提供了一个base属性,因此你可以像下面这样写

const { src, dest, series } = require('gulp');
const less = require('gulp-less');
const image = require('gulp-image');
const babel = require('gulp-babel');
const teser = require('gulp-terser');
const uglifycss = require('gulp-uglifycss');
const path = require('path');
const pathDir = (dir) => {
  return path.resolve(__dirname, dir);
}
// 设置base,当输出文件目标dist文件时,会自动拷贝当前文件夹到目标目录
const basePath = {
  base: './public'
};
// todo js任务
const taskJS = () => {
  return src(pathDir('public/**/*.js', basePath)).pipe(babel({
    presets: ['@babel/preset-env']
  })).pipe(teser({
    mangle: {
      toplevel: true // 混淆代码
    }
  })).pipe(dest(pathDir('dist')))
}
// todo less任务 
const taskLess = () => {
  return src(pathDir('public/css/*.less'), basePath).pipe(less()).pipe(uglifycss()).pipe(dest(pathDir('dist')))
}
// todo 图片资源,有压缩,并输出到对应的dist/images文件夹下
const taskImage = () => {
  return src(pathDir('public/images/*.*'), basePath).pipe(image()).pipe(dest(pathDir('dist')))
}
// todo html
const taskHtml = () => {
  return src(pathDir('public/index.html'), basePath).pipe(dest(pathDir('dist')))
}
const defaultTask = (cb) => {
  console.log('hello gulp')
  cb();
}
// series组合多个任务
const seriseTask = series(taskHtml, taskJS, taskLess, taskLess, taskImage)
exports.default = defaultTask;
exports.taskJS = taskJS;
exports.taskLess = taskLess;
exports.taskImage = taskImage;
exports.seriseTask = seriseTask;


将资源注入html中


gulp中,任务之间的依赖关系需要我们自己手动写一些执行任务流,现在一些打包后的dist的文件并不会自动注入html中。


参考gulp-inject[1]

...
const inject = require('gulp-inject');
...
// 将css,js插入html中
const injectHtml = () => {
  // 目标资源
  const targetSources = src(['./dist/**/*.js', './dist/**/*.css'], { read: false });
  // 目标html
  const targetHtml = src('./dist/*.html')
  // 把目标资源插入目标html中,同时输出到dist文件下
  const result = targetHtml.pipe(inject(targetSources)).pipe(dest('dist'));
  return result
}
// series串行组合多个任务
const seriseTask = series(taskHtml, taskJS, taskLess, taskLess, taskImage, injectHtml)
exports.seriseTask = seriseTask;

注意一个执行顺序,必须是等前面任务执行完了,再注入,所以在series任务的最后才执行injectHtml操作


并且在public/index.html下,还需要加入一段注释

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>gulp</title>
    <!-- inject:css -->
    <!-- endinject -->
  </head>
  <body>
    <div id="app"></div>
    <!-- inject:js -->
    <!-- endinject -->
  </body>
</html>

当我们运行npx gulp seriseTask

8ce7802d42b296634ef03d9186ba3d96.png


创建本地服务


我们需要将前面所有的js,css,html组织起来,在本地服务中使用


参考browser-sync[2]

const { src, dest, series, watch } = require('gulp');
const browserSync = require('browser-sync');
...
const taskBuild = seriseTask;
// 本地服务
const taskDevServer = () => {
  // 监听public所有目录下,只要文件发生改变,就重新加载
  watch(pathDir('public'), taskBuild);
  // 创建服务
  const server = browserSync.create();
  // 调用init开启端口访问
  server.init({
    port: '8081', //设置端口
    open: true,  // 自动打开浏览器
    files: './dist/*', // dist文件
    server: {
      baseDir: './dist'
    }
  })
}
exports.taskDevServer = taskDevServer;

当我们运行npx gulp taskDevServer时,浏览器会默认打开http://localhost:8081

b6fc653660e687e8c71c91a1eee0e8f3.png

我们使用了一个watch监听public目录下的所有文件,如果文件有变化时,会执行taskBuild任务会在dist目录下生成对应的文件,然后会启动一个本地服务,打开一个8081的端口就可以访问应用了。


至此一个一个用gulp搭建的前端应用终于可以了。


重新组织gulpfile


最后我们可以再重新组织一下gulpfile.js,因为多个任务写在一个文件里貌似不太那么好维护,随着业务迭代,会越来越多,因此,有必要将任务分解一下


在根目录新建task,我们把所有的任务如下


common.js

// task/common.js
const path = require('path');
const pathDir = (dir) => {
  return path.join(__dirname, '../', dir);
}
const rootDir = path.resolve(__dirname, '../');
const basePath = {
  base: './public'
};
const targetDest = 'dist';
module.exports = {
  rootDir,
  pathDir,
  basePath,
  targetDest
};

injectHtml.js

// task/injectHtml.js
const { src, dest } = require('gulp');
const inject = require('gulp-inject');
const { targetDest, rootDir } = require('./common.js');
// 将css,js插入html中
const injectHtml = () => {
  // 目标资源
  const targetSources = src([`${rootDir}/${targetDest}/**/*.js`, `${rootDir}/${targetDest}/**/*.css`]);
  // 目标html
  const targetHtml = src(`${rootDir}/${targetDest}/*.html`)
  // 把目标资源插入目标html中,同时输出到dist文件下
  const result = targetHtml.pipe(inject(targetSources, { relative: true })).pipe(dest(targetDest));
  return result
}
module.exports = injectHtml;

taskDevServer.js

const { watch } = require('gulp');
const path = require('path');
const browserSync = require('browser-sync');
const { pathDir, targetDest, rootDir } = require('./common.js');
const taskDevServer = (taskBuild) => {
  return (options = {}) => {
    const defaultOption = {
      port: '8081', //设置端口
      open: true,  // 自动打开浏览器
      files: `${rootDir}/${targetDest}/*`, // 当dist文件下有改动时,会自动刷新页面
      server: {
        baseDir: `${rootDir}/${targetDest}` // 基于当前dist目录
      },
      ...options
    }
    // 监听public所有目录下,只要文件发生改变,就重新加载
    watch(pathDir('public'), taskBuild);
    const server = browserSync.create();
    server.init(defaultOption);
  }
}
module.exports = taskDevServer;

...

task/index.js

const injectHtml = require('./injectHtml.js');
const taskDevServer = require('./taskDevServer.js');
const taskHtml = require('./taskHtml.js');
const taskImage = require('./taskImage.js');
const taskJS = require('./taskJS.js');
const taskLess = require('./taskLess.js');
module.exports = {
  injectHtml,
  taskDevServer,
  taskHtml,
  taskImage,
  taskJS,
  taskLess
}

gulpfile.js中,我们修改下

// gulpfile.js
const { series } = require('gulp');
const { injectHtml, taskDevServer, taskHtml, taskImage, taskJS, taskLess } = require('./task/index.js')
// series组合多个任务
const seriseTask = series(taskHtml, taskJS, taskLess, taskLess, taskImage, injectHtml);
// 本地服务
const devServer = taskDevServer(seriseTask);
// 启动服务
const server = () => {
  devServer({
    port: 9000
  });
}
const taskBuild = seriseTask;
const defaultTask = (cb) => {
  console.log('hello gulp')
  cb();
}
exports.default = defaultTask;
exports.server = server;
exports.build = taskBuild;

我们在package.json中新增命令

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "server": "gulp server",
    "build": "gulp build"
  },

npm run build

53528e9a0cba3bdc8d9e821205e7f561.png

在启动server之前,我们先执行npm run build,然后再执行下面命令,保证browserSync创建的服务文件夹存在,不然页面打开就404错误


npm run server

03a287d0d52916dcff4dbfa53574d562.png

至此gulp搭建一个简单的应该就已经完全ok了

3fe814b031ea8a1c4438d021d5195a22.png

这页面背景貌似有点黄🤣


总结


  • gulpjs开发是一个任务流的开发方式,它的核心思想就是用自动化构建工具增强你的工作流,所有的自动化工作流操作都牢牢的掌握在自己手上,你可以用gulp写一些自动化脚本,比如,文件上传,打包,压缩,或者改造传统的前端应用。
  • gulp写了一个简单的应用,但是发现中途需要找好多gulp插件,gulp的生态还算可以,3w多个star,生态相对丰富,但是有些插件常年不更新,或者版本更新不支持,比如gulp-image,当你按照官方文档使用最新的包时,不支持esm,你必须降低版本6.2.1,改用cjs才行
  • 使用gulp的一些常用的api,比如srcdestseries,以及browser-sync实现本地服务,更多api[3]参考官方文档。
  • 即使项目时间再多,也不要用gulp搭建前端应用,因为webpack生态很强大了,看gulp的最近更新还是2年前,但是写个自动化脚本,还算可以,毕竟gulp的理念就是用自动化构建工具增强你工作流程,也许当你接盘传统项目时,一些打包,拷贝,压缩文件之类的,可以尝试用用这个。
  • 本文示例code-example[4]
相关文章
|
2月前
|
缓存 监控 前端开发
前端工程化:Webpack与Gulp的构建工具选择与配置优化
【10月更文挑战第26天】前端工程化是现代Web开发的重要趋势,通过将前端代码视为工程来管理,提高了开发效率和质量。本文详细对比了Webpack和Gulp两大主流构建工具的选择与配置优化,并提供了具体示例代码。Webpack擅长模块化打包和资源管理,而Gulp则在任务编写和自动化构建方面更具灵活性。两者各有优势,需根据项目需求进行选择和优化。
80 7
|
2月前
|
缓存 前端开发 JavaScript
前端工程化:Webpack与Gulp的构建工具选择与配置优化
【10月更文挑战第27天】在现代前端开发中,构建工具的选择对项目的效率和可维护性至关重要。本文比较了Webpack和Gulp两个流行的构建工具,介绍了它们的特点和适用场景,并提供了配置优化的最佳实践。Webpack适合大型模块化项目,Gulp则适用于快速自动化构建流程。通过合理的配置优化,可以显著提升构建效率和性能。
62 2
|
8月前
|
前端开发 JavaScript Go
webpack -vite(Rollup )-Gulp (一)
webpack -vite(Rollup )-Gulp (一)
128 0
|
8月前
|
JavaScript 前端开发 API
轻松掌握构建工具:Webpack、Gulp、Grunt 和 Rollup 的使用技巧(下)
轻松掌握构建工具:Webpack、Gulp、Grunt 和 Rollup 的使用技巧(下)
轻松掌握构建工具:Webpack、Gulp、Grunt 和 Rollup 的使用技巧(下)
|
8月前
|
JSON JavaScript 前端开发
轻松掌握构建工具:Webpack、Gulp、Grunt 和 Rollup 的使用技巧(上)
轻松掌握构建工具:Webpack、Gulp、Grunt 和 Rollup 的使用技巧(上)
轻松掌握构建工具:Webpack、Gulp、Grunt 和 Rollup 的使用技巧(上)
|
监控 前端开发 JavaScript
Git Gulp webpack
Git Gulp webpack
Git Gulp webpack
webpack与grunt、gulp的不同?
webpack与grunt、gulp的不同?
|
前端开发 JavaScript 算法
Gulp 或 Webpack是干什么的?底层原理是什么?
Gulp 或 Webpack是干什么的?底层原理是什么?
136 0
|
JSON JavaScript 前端开发
谈谈gulp和webpack?✔️
谈谈gulp和webpack?✔️
|
机器学习/深度学习 JavaScript 前端开发
gulp + webpack 搭建项目环境
搭建一个 gulp + webpack 的环境去学 react ,环境没有区分生产和开发环境,后续会完善,新手可以看一下。
1600 0