当前位置: 首页 > news >正文

Yeoman实战指南:从零打造自定义项目生成器

优秀的开发者从不重复造轮子而是用工具把轮子造得更好,Yeoman就是这样一个工具,它让项目初始化变得如此简单,以至于你可能会忘记曾经为此烦恼过。

目录

初识Yeoman

基础使用 

自定义Generator

发布Generator

初识Yeoman

Yeoman:是一个用于自动化项目脚手架创建的工具链,由Google开发并开源,它通过生成器(Generators)快速搭建项目结构,集成了主流框架(如React、Vue、Angular)和工具(如Webpack、Babel)的最佳实践,帮助开发者避免重复劳动,以下是Yeoman脚手架与其他常用脚手架的区别:

工具定位适用场景
Yeoman通用脚手架工具复杂项目结构、多技术栈
Create React AppReact 专用工具快速启动 React 项目
Vite构建工具 + 脚手架现代前端项目(Vue/React)
Angular CLIAngular 官方工具Angular 项目全生命周期

生成器:在Yeoman生态中生成器(Generator)是核心组件,用于自动化创建项目结构和文件,它可以理解为一个可编程的项目模板,通过代码逻辑而非静态文件来生成定制化的项目,常见的全局生成器如下所示:

生成器名称技术栈支持项目类型核心特点
generator-webappHTML/CSS/JS, Webpack通用Web应用基础Web项目支持PWA、Sass等,可扩展框架
generator-vueVue, Vuex, Vue RouterVue 应用多种 Vue 模板(Webpack/Vite)
generator-reactReact, Redux, TypeScriptReact 应用专注 React 生态,集成 CRA 替代方案
generator-angularAngular, RxJSAngular 应用官方 Angular CLI 的 Yeoman 版本
generator-nodeNode.js, npm, ExpressNode 模块 / 服务创建 npm 包、CLI 工具,集成测试框架
generator-expressExpress.js, MongoDBWeb 后端服务生成 Express 项目骨架,支持 MVC 架构
generator-webpackWebpack打包工具配置定制化 Webpack 配置,支持多入口、代码分割
generator-gulpGulp构建工具配置生成Gulp任务配置,支持文件处理、压缩、编译
generator-eslintESLint代码规范生成ESLint配置文件,支持Airbnb等风格

在Yeoman生态中,yo和生成器是两个核心组件,它们的关系可以类比为 "引擎" 和 "模板",如下:

它们两者的区别如下所示:

特性yo(命令行工具)生成器(Generator)
安装方式全局安装:npm install -g yo全局 / 局部安装:npm install -g generator-webapp
功能定位执行引擎,驱动整个流程定义具体项目模板和逻辑
可扩展性固定功能,通过插件扩展可自定义开发,支持无限扩展
使用频率每次创建项目时调用按需安装不同生成器
典型命令yoyo webapp, yo node

具体详情可以参考官方文档:地址 ,这里不再赘述,如下所示:

基础使用 

接下来我们以generator-node为例,终端执行如下命令全局安装,这里建议node版本不易太高,18版本即可,否则会出现一些依赖的冲突:

npm i -g yo@4.3.1 generator-node

这里之所以指定yo版本,主要是考虑兼容性问题,因为很多生成器模板是基于旧版本的4来进行开发的,yo新版本5来安装就版本的生成器容易出现generator.run is not a function报错问题,所以这里我们指定安装下载量最多的4版本进行下载: 

安装完成之后,我们cmd终端执行如下命令来创建yo脚手架,创建的命令就是将生成器前面的generator删掉保留后面的名称即可,也就是终端执行如下命令:yo node如下所示:

上面的选项也就是要对生成的模块文件中的package.json文件中的字段内容进行填充,当然一些生成器还包含一些子集的生成器,如上面安装的node生成器中就可以执行安装一下子集的生成器:

当然如果我们想安装网站应用的话,可以终端执行如下命令安装generator:

npm i generator-webapp -g

运行项目之后得到的界面如下所示:

 

自定义Generator

创建项目:市面上的generator不一定都适配自己具有业务逻辑的项目,所以这里可以自定义属于自己的生成器模板进行使用,自定义的generator固定格式都是 generator-<name> 的格式,接下来我们开始创建我们自定义的generator,终端执行如下命令初始化项目:

npm init -y

初始化完成之后,我们需要安装下面这个 generator 基类的插件用于自定义开发generator:

npm i yeoman-generator -s

安装完插件之后,我们在项目设置如下基本结构:

如果想添加子生成器,可以参加下面的结构:

最终得到的文件结构如下所示,这里我们在app文件下的index.js文件中编写代码,该文件是作为Generator的核心入口,需要导出一个继承自Yeoman Generator的类型,Yeoman Generator在工作时会自动调用我们在此类型中定义的一些生命周期方法,我们可以在这些方法中通过调用父类提供的一些工具方法实现一些功能,例如如下示例,演示文件写入操作:

编写完成之后,我们通过npm link的方式将这个模块链接到全局范围,使其成为一个全局模块包,这里我们就可以通过yo test的方式,执行我们的程序,这里注意一下要把package中的name属性和文件夹名称保持一致:"name": "generator-test",如下所示:

生成模板:如果想生成模板文件的话,可以在通过如下方式进行:

生成的文件就会把模板文件中的数据给替换掉,相对于手动创建每一个文件,模板的方式大大提高了效率

接收用户输入:如果想通过命令行的方式来接受用户输入的内容,然后再动态的渲染模板,我们可以通过如下的方式进行,根据用户输入的内容来动态渲染模板的上下文内容:

// 核心入口
import Generator from 'yeoman-generator';export default class extends Generator {// 通过prompt方法询问用户问题prompting() {return this.prompt([{type: 'input',name: 'title',message: 'Your project name',default: this.appname, // 默认值},{type: 'confirm',name: 'success',message: 'Do you want to continue?',default: true}]).then(answers => {this.answers = answers;})}writing() {// 模板文件路径const tmpl = this.templatePath('foo.txt');// 输出目标路径const output = this.destinationPath('foo.txt');// 模板数据上下文const context = this.answers;// 渲染模板文件并输出到目标路径this.fs.copyTpl(tmpl, output, context);}
}

效果如下所示:

后期如果想定义一个完整的项目模板的话,可以通过遍历模板中的每一个文件内容,然后通过模板渲染来给模板中有<%%>等标签的内容动态渲染,如下所示:

const templetes = ['src/index.html','src/index.js','src/index.css','src/index.vue'
]templetes.forEach(item => {this.fs.copyTpl(this.templatePath(item),this.destinationPath(item),this.answers,)
})

当然我们创建完项目之后,还可以自动下载依赖操作并提示用户项目模板下载完成:

install() {this.env.options.nodePackageManager = 'npm';
}
end() {this.log('项目生成成功');
}

当然父类Generator中也已经成长好了一些其他关于目录文件的操作方法,子类生成器中可以直接使用,如下所示:

方法名方法描述
this.sourceRoot()设置/获取模板路径
this.destinationRoot()获取/设置项目目录。一般不设置
this.fs.copyTpl(模板,目标,数据)用数据替换模板中的占位,最终拷贝到指定位置
this.destinationPath(index.js')根据项目目录,拼接路径文件名
this.templatePath(index.js')根据模板目录,拼接路径文件名
this.contextRoot()获取从哪里运行的yo
this.fs.write(文件,内容)写入文件,若不存,则创建
this.prompt([( ), .( ])发起询问,并返回Promise对象形式的结果

当然我们在创建Generator的时候,通过使用特定的生命周期函数可以精准的定位到代码的执行时机,以下是具体的生命周期函数的调用及其作用:

方法名方法描述
initializing()初始化,一般用于检查当前项目状态、获取配置等
prompting()提示用户做出选择。一般里面会调用this.prompt()
configuring()保存配置和配置项目。如创建.editorconfig文件和其它元数据文件
default()如果方法名与优先级不匹配,则将其推送到该组
writing()创建项目时,写入文件或创建目录的地方
conflicts()处理冲突的地方。一般为内部使用
install()运行安装的位置。如npm、bower
end()最后调用。通常做一些清理工作。

发布Generator

generator实际上就是一个npm的模块,如果我们想发布generator的话就相当于发布npm模块,这里我们可以直接通过通过npm publish的方式进行发布即可:

具体的发布流程及其注意事项,可以参考我之前的文章:地址 ,这里不再赘述,最终发布到npm平台之后,就可以通过yo 你发布的模块 命令来下载你自定义的模板了,发布成功之后就可以看到你的包名,后面全局下载到本地依赖即可:

相关文章:

  • 人工智能100问☞第23问:卷积神经网络(CNN)为何擅长图像处理?
  • 最新网盘资源搜索系统,电视直播,Alist聚合播放
  • QMK固件OLED显示屏配置教程:从零开始实现个性化键盘显示(实操部分)
  • DevExpressWinForms-TreeList-数据绑定
  • 如何在Edge浏览器里-安装梦精灵AI提示词管理工具
  • OrangePi Zero 3学习笔记(Android篇)10 - SPI和从设备
  • 二程运输的干散货船路径优化
  • 2025年山东省数学建模F题思路
  • 精益数据分析(61/126):移情阶段评分体系构建与实战案例解析
  • MySQL 用户权限管理:从入门到精通
  • 2025年5月-信息系统项目管理师高级-软考高项-成本计算题
  • WebSocket:实时通信(如聊天应用)从零到一的深度解析
  • Cursor打开的文件中文乱码,应该怎么设置
  • 提高绳牵引并联连续体机器人运动学建模精度的基于Transformer的分段学习方法
  • Maven 插件参数注入与Mojo开发详解
  • Secs/Gem第七讲(基于secs4net项目的ChatGpt介绍)
  • 【物联网】基于树莓派的物联网开发【4】——WIFI+SSH远程登录树莓派
  • C 语言实战:使用二维数组进行学生成绩统计与分析
  • Kafka快速安装与使用
  • 【redis】redis常见数据结构及其底层,redis单线程读写效率高于多线程的理解,
  • ESG考证虚火:相比证书,知识结构+实战经验更重要
  • “三个集中”之后:图说浦东新区28次撤乡并镇
  • 人民日报:从“轻微免罚”看涉企执法方式转变
  • 人民日报民生观:转人工客服,怎么这么难?
  • 陕西旱情实探:大型灌区农业供水有保障,大旱之年无旱象
  • 5吨煤炭“瞬间蒸发”?掺水炭致企业损失千万,腐败窝案曝光