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

相关推荐
天天打码6 分钟前
ThinkPHP项目如何关闭runtime下Log日志文件记录
android·java·javascript
2401_8574396933 分钟前
智慧社区电商系统:提升用户体验的界面设计
前端·javascript·php·ux
csdnLN1 小时前
$.ajax() 对应事件done() 、fail()、always() 的用法
前端·javascript·ajax
甜味橘阳1 小时前
echarts地图可视化展示
前端·javascript·echarts
谢道韫6662 小时前
今日总结 2024-12-24
javascript·vue.js·elementui
一朵好运莲2 小时前
React引入Echart水球图
开发语言·javascript·ecmascript
梦境之冢3 小时前
axios 常见的content-type、responseType有哪些?
前端·javascript·http
racerun3 小时前
vue VueResource & axios
前端·javascript·vue.js
J总裁的小芒果4 小时前
THREE.js 入门(六) 纹理、uv坐标
开发语言·javascript·uv
m0_548514774 小时前
前端Pako.js 压缩解压库 与 Java 的 zlib 压缩与解压 的互通实现
java·前端·javascript