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

相关推荐
℘团子এ4 分钟前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z10 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁33 分钟前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜34 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish35 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple35 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five36 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
临枫54137 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
酷酷的威朗普38 分钟前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5
前端每日三省38 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript