如何通过函数式编程消除 Gulp 构建任务中的重复逻辑

2次阅读

如何通过函数式编程消除 Gulp 构建任务中的重复逻辑

本文介绍如何利用 javascript 闭包与柯里化技术,将多个结构相同但路径不同的 gulp nunjucks 渲染任务抽象为单一可复用函数,避免代码重复、提升可维护性。

本文介绍如何利用 javascript 闭包与柯里化技术,将多个结构相同但路径不同的 gulp nunjucks 渲染任务抽象为单一可复用函数,避免代码重复、提升可维护性。

在构建工具链中,尤其是使用 Gulp v4+ 编写 gulpfile.js 时,常会遇到「功能一致、仅输入路径不同」的重复任务定义问题。例如,你可能需要分别处理 src/views/home/ 和 src/views/blog/ 两个视图目录,并各自输出到不同的目标路径(如 dist/home/ 和 dist/blog/)。若为每个路径单独编写一个 genNunJucks 类函数,不仅违反 DRY(Don’t Repeat Yourself)原则,还会显著增加维护成本——一旦渲染配置(如 nunjucksRender 选项或 htmlbeautify 格式)需调整,就必须同步修改多处。

解决这一问题的核心思路是:将变化的参数(如源路径、目标路径)提取为函数输入,将不变的流程封装为可复用的执行逻辑。这正是函数式编程中 柯里化(Currying)闭包(Closure) 的典型应用场景。

✅ 推荐方案:柯里化 + 闭包生成任务函数

我们定义一个高阶函数 createNunjucksTask,它接收源路径(srcPath)和目标路径(destPath)作为参数,并返回一个符合 Gulp 任务签名((cb) => stream)的函数:

const { src, dest } = require('gulp'); const nunjucksRender = require('gulp-nunjucks'); const htmlbeautify = require('gulp-html-beautify');  // 高阶函数:返回一个可直接注册为 Gulp 任务的函数 function createNunjucksTask(srcPath, destPath) {   return function genNunJucks(cb) {     return src(srcPath)       .pipe(nunjucksRender({         path: ['src/views/'],         ext: '.html',         inheritExtension: false,         envOptions: { watch: true },         manageEnv: manageEnvironment,         loaders: null       }))       .pipe(htmlbeautify({         indentSize: 2,         eol: 'n',         indent_level: 0,         preserve_newlines: false       }))       .pipe(dest(destPath));     cb(); // 注意:Gulp v4 中若返回 stream,无需显式调用 cb();此处保留以兼容回调风格写法   }; }  // 使用示例:动态创建两个任务 exports.genHome = createNunjucksTask(paths.views.src, paths.views.dest); exports.genBlog = createNunjucksTask(paths.views.src2, paths.views.dest2);

? 关键说明

  • createNunjucksTask 在调用时捕获 srcPath 和 destPath,并通过闭包将其持久化到返回的 genNunJucks 函数作用域中;
  • 返回的函数完全复用同一套管道逻辑,仅动态切换 I/O 路径,真正实现「逻辑一处定义,多处实例化」;
  • 此方式天然支持任意数量的视图目录,只需新增一行 exports.xxx = createNunjucksTask(…) 即可。

? 进阶优化:批量注册任务(支持路径数组)

若视图目录较多(如 [‘home’, ‘blog’, ‘docs’]),还可进一步封装为批量注册函数:

function registerNunjucksTasks(configs) {   configs.forEach(({ name, src, dest }) => {     exports[name] = createNunjucksTask(src, dest);   }); }  // 批量声明 registerNunjucksTasks([   { name: 'genHome',  src: paths.views.src,   dest: paths.views.dest   },   { name: 'genBlog',  src: paths.views.src2,  dest: paths.views.dest2  },   { name: 'genDocs',  src: 'src/views/docs/**/*', dest: 'dist/docs/' } ]);

⚠️ 注意事项与最佳实践

  • Gulp 版本兼容性:上述写法适用于 Gulp v4+。若使用 Gulp v3,请确保返回 stream 或显式调用 cb();v4 推荐返回 stream(自动处理异步),此时可省略 cb() 调用;
  • 路径变量安全校验:建议在 createNunjucksTask 内部添加 if (!srcPath || !destPath) throw new Error(‘Missing src/dest path’),避免静默失败;
  • 配置集中管理:将 nunjucksRender 和 htmlbeautify 的通用配置抽离为常量(如 NUJUCKS_OPTS, BEAUTIFY_OPTS),便于全局统一维护;
  • 错误处理增强:可在 .pipe() 链后添加 .on(‘error’, console.error) 或使用 gulp-plumber 插件防止管道中断。

通过函数式抽象,你不仅消除了重复代码,更赋予了构建脚本更强的表达力与扩展性——当业务增长、视图增多时,只需声明新路径,无需重写逻辑。这才是自动化构建应有的优雅姿态。

text=ZqhQzanResources