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

相关推荐
小白小白从不日白37 分钟前
react 高阶组件
前端·javascript·react.js
程序员大金43 分钟前
基于SpringBoot+Vue+MySQL的智能物流管理系统
java·javascript·vue.js·spring boot·后端·mysql·mybatis
LJ小番茄3 小时前
Vue 常见的几种通信方式(总结)
前端·javascript·vue.js·html
pan_junbiao5 小时前
Vue组件:模板引用ref属性的使用
前端·javascript·vue.js
__lucas6 小时前
javascript-装饰器
开发语言·javascript·ecmascript
春蕾夏荷_7282977256 小时前
electron nsis打包windows应用程序
javascript·windows·electron·nsis
想退休的搬砖人7 小时前
vue组件的生命周期
前端·javascript·vue.js
zhangjin12227 小时前
kettle从入门到精通 第八十五课 ETL之kettle kettle中javascript步骤调用外部javascript/js文件
javascript·数据仓库·etl·kettle调用外部js
CaptainDrake7 小时前
Vue:指令
前端·javascript·vue.js