Gulp's vinyl

背景

我想要实现一个简单的基于模板生成页面的工具.

第一期的时候, 采用了简单粗暴的文件复制方案.

第二期希望加上基于模板引擎(如: handlebars/ejs)的渲染.

调研了一些实现方案:

基于模板引擎自行实现

使用介绍

以 handlebars 为例, ejs 类似.

  1. 从文件中读取模板内容

  2. 使用 compile 方法渲染并注入变量

JavaScript 复制代码
import Handlebars from "handlebars";
const template = Handlebars.compile("Name: {{name}}");
console.log(template({ name: "Nils" }));
  1. 将渲染完成的字符串写入文件
缺点

模板引擎一般提供单个文件的渲染方式, 需要自己实现文件的遍历.

基于 codesmith 封装

使用介绍

github.com/web-infra-d...

  1. 使用 codeSmith 运行微生成器 generator
JavaScript 复制代码
const smith = new CodeSmith({debug: false,});
await smith.forge({tasks: [{generator: <generatorPath>,config: {},},],pwd: '.',});
  1. 在 generator 中调用 forgeTemplate
JavaScript 复制代码
import { AppAPI } from '@modern-js/codesmith-api-app';

export default async (context: GeneratorContext, generator: GeneratorCore) => {
  const appApi = new AppAPI(context, generator);
  await appApi.forgeTemplate(...);
};
缺点

如果多个模板, 需要封装多个微生成器, 成本较高.

结论

forgeTemplate(...) 方法已经非常满足我们的需求了, 只是不想封装微生成器.

有没有一种方案, 可以通过 glob 快速匹配文件, 通过简单处理, 生成到指定的文件夹呢.

在遥远的过去, 曾经有一个王者: Gulp.

Gulp

Gulp 被 webpack 打败了, 但它并不是 webpack 这类模块打包器(webpack 也打败了同为模块打包器的 browserify).

Gulp 当年以[高性能(基于流而无需重复读写磁盘生成文件)/轻量简单易用]打败了同为 task 任务编排的 Gruntjs.

因为前端的 task 任务编排多为文件处理, 随着前端开发模式成熟, 任务模式收敛, 编译流程逐步统一, 被 webpack 这个模块打包器降维打击了.

Gulp 的方法可以简单分为两类.

如果我只是想用文件处理, 而不需要任务编排呢.

查看 gulp 源码, 我们发现, 原来 gulp 的 src/dest 方法已经被抽成了 vinyl-fs 库.

gulp 已经很久没有更新了, 而 vinyl-fs 还在活跃.

Vinyl-fs

vinyl-fs 与 gulp 的使用完全一致.

JavaScript 复制代码
vfs.src(['./js/**/*.js', '!./js/vendor/*.js'])
    .pipe(...)
    .pipe(vfs.dest('./output'));

如果文件在 pipe 中不通过处理, src 通过 glob 读取文件后, dest 到指定目录, 就相当于简单的 copy.

glob 介绍

juejin.cn/post/684490...

实现一个简单的 pipe 插件

JavaScript 复制代码
import vfs from "vinyl-fs";
import File from "vinyl";

// 将管道中的文件后缀修改为 .x
vfs.src(...)
  .pipe(new Transform({
    objectMode: true,
    transform(file: File, enc, callback) {
      if (!file.isDirectory()) {
        file.extname = '.x';
      }
      callback(null, file);
    },
  }))
  .pipe(vfs.dest(...))

vinyl 文件系统

vinyl 是一个非常简单的元数据对象,描述一个文件。当你想到一个文件时,你会想到两个属性: path 和 contents。这些是 vinyl 对象的主要属性。

这里结合 handlebars 来举例

JavaScript 复制代码
import vfs from "vinyl-fs";
import File from "vinyl";

// 实现
vfs.src(...)
  .pipe(new Transform({
    objectMode: true,
    transform(file: File, enc, callback) {
      const tpl = file.contents.toString();
      const template = Handlebars.compile(tpl);
      file.contents = Buffer.from(template({ ... }));
      callback(null, file);
    },
  }))
  .pipe(vfs.dest(...))

注意事项

请注意 glob 生效的目录范围

glob 永远相对于 process.cwd(), 且无法超出目录范围, 如 vfs.src('../../xxx') 将无法生效, vfs.src('/otherDir/xxx') 使用绝对路径也无法生效.

解决方案: 可以通过指定 cwd 来解决, 如我们的 模板生成页面工具中, cli 内置模板目录没有在项目中

JavaScript 复制代码
  const templateDir = path.resolve(dirname(fileURLToPath(import.meta.url)), `../template-${_templateObj.name}`)
  
  vfs
    .src(["page/**/*"], {
      cwd: templateDir,
    })

后记

使用 vinyl-fs, 我们可以通过 glob 快速匹配文件, 通过简单处理, 生成到指定的文件夹.

实现的 transformer 也可以与主流程解偶, 代码更加清晰, 可扩展性强. 就算你只是简单的 copy, 也非常推荐.

实现上, 我们也可以参考强大的 gulp 插件库.(虽然是过去式了).

gulp 插件与 vinyl transformer 完全通用, 但由于 gulp 插件库可能很久没有更新了, 所以建议不直接使用, 而是参考实现.

相关推荐
指尖跳动的光几秒前
前端如何通过设置失效时间清除本地存储的数据?
前端·javascript
于慨18 分钟前
dayjs处理时区问题、前端时区问题
开发语言·前端·javascript
拖拉斯旋风40 分钟前
深入理解 LangChain 中的 `.pipe()`:构建可组合 AI 应用的核心管道机制
javascript·langchain
肖老师xy41 分钟前
Ai生成时间排期进度
javascript·vue.js·elementui
Aliex_git42 分钟前
Vue 错误处理机制源码理解
前端·javascript·vue.js
ejjdhdjdjdjdjjsl43 分钟前
Winform初步认识
开发语言·javascript·ecmascript
掘金安东尼3 小时前
Vercel:我们为 React2Shell 发起了一项价值 100 万美元的黑客挑战
前端·javascript·github
掘金安东尼3 小时前
浏览器处理Base64数据的速度有多快?
前端·javascript·github
掘金安东尼3 小时前
为不同场景设计多样化的页面过渡动画
前端·javascript·github
持续升级打怪中3 小时前
深入解析深浅拷贝:原理、实现与最佳实践
开发语言·前端·javascript