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 插件库可能很久没有更新了, 所以建议不直接使用, 而是参考实现.

相关推荐
姜太公钓鲸2331 天前
ROM就是程序存储器,实际的存储介质是Flash闪存。上述描述中的程序存储器是什么意思?
开发语言·javascript·ecmascript
柳杉1 天前
使用AI从零打造炫酷医疗数据可视化大屏,源码免费拿!
前端·javascript·数据可视化
简单Janeee1 天前
[Vue 3 从零到上线]-第四篇:组件化思维——把网页像积木一样拆解
javascript·vue.js·ecmascript
Heo1 天前
深入React19任务调度器Scheduler
前端·javascript·面试
boooooooom1 天前
Vue3 nextTick 实现大变化:微任务优先,彻底搞懂渲染时机!
javascript·vue.js·面试
用户14436183400971 天前
你不知道的JS上-(九)
前端·javascript
冴羽1 天前
2026 年 JavaScript 框架 3 大趋势
前端·javascript·react.js
思茂信息1 天前
基于CST 3D Combined功能的以太网口RE仿真
开发语言·javascript·单片机·嵌入式硬件·matlab·3d
一拳不是超人1 天前
从“必选项”到“性能包袱”:为什么现代框架开始“抛弃”虚拟 DOM?
前端·javascript·架构
Gogo11211 天前
JavaScript 里的“空”:Null 与 Undefined 的终极指南
javascript