乐趣区

关于前端:放弃webpack拥抱gulp

别被题目吓到,哈哈,即便当初 vite 横空出世,社区光芒四射,两个字很快,然而 webpack 仍旧宝刀未老,仍然扛起前端工程化的大梁,然而明天我为啥说要拥抱gulp,因为咱们经经常吃一道菜,所以要换个口味,这样才养分平衡。

gulp定义是:用 自动化构建工具 加强你的 工作流程 , 是一种基于工作 文件流 形式, 你能够在前端写一些自动化脚本,或者降级历史传统我的项目,解放你反复打包,压缩,解压之类的操作。

集体了解 gulp 是一种 命令式编程 的体验,更重视构建过程,所有的工作须要你本人手动创立,你会对构建流程会十分分明,这点不像 webpackwebpack 就是一个开箱即用的 申明式 形式,webpack是一个模块化打包工具,外部细节暗藏十分之深,你也不需关注细节,你只须要照着提供的 API 以及引入对应的 loaderplugin应用就行。

言归正传,为了 饮食平衡,明天一起学习下gulpjs

注释开始 …

搭建一个简略的前端利用

相比拟 webpack,其实gulp 的我的项目构造更偏差传统的利用,只是咱们借助 gulp 工具解放咱们的一些 代码压缩 es6 编译 打包 以及在传统我的项目中都能够应用 less 体验。

gulp 目录下新建01-simple-demo

根目录下生成默认package.json

npm init -y

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

文件构造,大略就这样

而后在装置gulp

npm i gulp --save-dev

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

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

而后咱们在命令行执行

npx gulp

当咱们执行 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

至此你会发现 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,后果如下

图片资源

应用一个 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;

一顿操作发现,最新版本不反对 esm, 所以还是升高版本版本,这里升高到6.2.1 版本

而后运行npx gulp taskImage

图片压缩得不小

在这之前,咱们别离定义了三个不同的工作,gulp导出的工作有
公开工作和公有工作,多个公开工作能够 串行组合 应用

组合工作 series 与 parallel

因而我能够将之前的介个工作组合在一起

// 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

曾经在 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

箭头函数和 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

...
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

创立本地服务

参考 browser-sync

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

咱们应用了一个 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

在启动 server 之前,咱们先执行 npm run build,而后再执行上面命令, 保障browserSync 创立的服务文件夹存在,不然页面关上就 404 谬误

npm run server

至此 gulp 搭建一个简略的应该就曾经齐全 ok 了

页面背景貌似有点黄

总结

  • gulpjs开发是一个工作流的开发方式,它的核心思想就是 用自动化构建工具加强你的工作流 , 所有的自动化工作流操作都牢牢的把握在本人手上,你能够用gulp 写一些自动化脚本,比方,文件上传,打包,压缩,或者革新传统的前端利用。
  • gulp 写了一个简略的利用,然而发现中途须要找好多 gulp 插件,gulp的生态还算能够,3w多个 star,生态绝对丰盛,然而有些插件长年不更新,或者版本更新不反对,比方 gulp-image,当你依照官网文档应用最新的包时,不反对 esm,你必须升高版本6.2.1, 改用cjs 才行
  • 应用 gulp 的一些罕用的 api, 比方 srcdestseries, 以及browser-sync 实现本地服务,更多 api 参考官网文档。
  • 即便我的项目工夫再多,也不要用 gulp 搭建前端利用,因为 webpack 生态很弱小了,看 gulp 的最近更新还是 2 年前,然而写个自动化脚本,还算能够,毕竟 gulp 的理念就是 用自动化构建工具加强你工作流程,兴许当你接盘传统我的项目时,一些打包,拷贝,压缩文件之类的,能够尝试用用这个。
  • 本文示例 code-example

欢送关注公众号:Web 技术学苑
好好学习,天天向上!

退出移动版