|
文章背景 对前端开发者而言,脚手架是快速开发不可或缺的一环。脚手架方便开发者从基于已有的代码库,去下载符合项目所使用的模板,并根据需求修改配置并生成项目文件。从而减少新建项目时重复的配置工作,统一团队开发中各项目的配置和规范。 本文作为本次umi系列的最后一篇,既然umi的源码在前面的文章已经分析完了,那么探索下umi3的脚手架的运行内容也是不错的~在此基础上,也分享一下现阶段转转内部的脚手架zz-cli的一些实现与构建内容。umi脚手架如何运作 从官方文档可以看到,执行以下命令就可以让脚手架运行起来$ yarn create @umijs/umi-app# 或 npx @umijs/create-umi-appcreate-umi-app.js中包含require('../lib/cli')cli.ts cli文件内默认引入执行index.tsrequire('./') .default({ cwd: process.cwd(), args, }) 代码中,先实例化AppGenerator类,然后执行run方法;import AppGenerator from './AppGenerator/AppGenerator';const generator = new AppGenerator({ cwd, args,});generator.run(); 紧接着我们来看看AppGenerator类做了什么事情。AppGenerator.ts AppGenerator类继承Generator类,并定义了writing方法,该方法执行copyDirectory复制目录,方法参数包含版本号、路由模式、模板文件地址、目标产生文件的地址等参数。import { Generator } from '@umijs/utils';export default class AppGenerator extends Generator { async writing() { this.copyDirectory({ context: { version, //根据配置文件获取版本号 conventionRoutes,// 常规路由?约定路由 }, path, // 找到对应templates文件 target: this.cwd, }); }}Generator.ts 介绍Generator类之前,还是要简单提一下umi2的基础上umi3的脚手架做了哪些更改。 umi2的脚手架项目独立于umi项目,在生成模板文件前,会在模板询问处提供3套项目模板供用户选择,选择完成后,直接去远程拉取内容下载;Generator类的构造则是通过yeoman这个脚手架工具去完成。(yeoman是一个通用型脚手架工具,容易拓展。我们在搭建脚手架时也可以把它作为一个方案备选,毕竟构建Generator类的方式多样,使用方式参考这里) umi3则将脚手架的创建功能收拢到umi项目的/packages/create-umi-app文件夹下,项目只有一套默认模板供使用,并且极大程度上的去除了脚手架创建时用户配置能力。 言归正传,我们先来看看umi3的Generator类包含了哪些内容。 最主要的是引用nodejsfs模块的copyFileSync、readFileSync、statSync、writeFileSync等方法,去获取文件的读写能力;引入nodejspath模块获取处理文件路径的能力。 其次引用了chalk(优化命令行显示),glob(文件名称匹配),mkdirp(创建文件夹),Mustache(模板引擎),prompts(询问交互),yargs(获取命令行参数)等工具插件去获取各种能力。 Generator类主要定义了run,prompting,writing,copyTpl,copyDirectory等方法。run是外部调用时执行的入口,如果有其他配置问题需要额外写入的话,也可以在外面手动传入prompting的内容,执行完prompting的内容后才会开始执行writingwriting主要是在执行文件模板拷贝的一个入口方法(在外部的AppGenerator中,writing执行了copyDirectory复制目录)copyTpl解析.tpl格式的模板文件并写入copyDirectory遍历模板文件,根据文件名称进行区分,对.tpl执行copyTpl写入,对普通文件直接进行复制 定义完Generator类、在项目中进行引用之后,我们就可以对模板文件进行解析和操作。class Generator { ... constructor({ cwd, args }: IOpts) { this.cwd = cwd; this.args = args; this.prompts = {}; } /** * run函数执行,当前没有写入prompting内容,需要可以根据需求自行定义 */ run() { this.writing(); } prompting() { return [] as any; } // 写入动作 async writing() {} // 复制模板 copyTpl(opts: { templatePath: string; target: string; context: object }) { const tpl = readFileSync(opts.templatePath, 'utf-8'); // 解析模板替换变量,得到新的模板内容 const content = Mustache.render(tpl, opts.context); mkdirp.sync(dirname(opts.target)); writeFileSync(opts.target, content, 'utf-8'); } // 复制文件目录 这个是脚手架执行的主要内容 copyDirectory(opts: { path: string; context: object; target: string }) { const files = glob.sync('**/*', { cwd: opts.path, dot: true, ignore: ['**/node_modules/**'], }); files.forEach((file: any) => { const absFile = join(opts.path, file); // 判断文件是否是相关的模板引擎文件,如果是的话需要解析模板再输出文件 if (file.endsWith('.tpl')) { this.copyTpl(); } else { // 直接复制文件 const absTarget = join(opts.target, file); mkdirp.sync(dirname(absTarget)); copyFileSync(absFile, absTarget); } }); }}总结 umi3脚手架总体上满足了脚手架的完整功能,既能够创建初始模板项目,也能够预置配置。当前需要用户直接参与进行配置的内容很少,因此手写定义Generator类的方法也是合适的。 讲完了umi3脚手架,再来看看咱们转转的zz-cli:一样是脚手架,却有点不一样。zz-cli zz-cli是转转前端脚手架工具,内置了转转fe各团队在日常开发中的各项代码模板,除了创建项目代码,同时还提供了与公司内部其他项目交互的能力,如统跳平台、zz-ui区块组件文件、上传基建项目文档等等,通过一个脚手架将一份项目代码与fe业务的各个节点衔接了起来。zz-cli的内容揭秘 zz-cli引用了commander做命令管理工具,安装完脚手架后,在命令行输入zz,就会展示下面的内容 可以看到,zz定义了5个Commands,就是脚手架可执行的命令。我们主要要看的是create,这个命令主要执行的是脚手架最基本也是最重要的功能:创建标准模板项目。完成只需要四步,咱们往下看吧第一步:模板选择询问 我们先输入命令行让脚手架的create命令执行起来:zz create my-appnode bin/zz create my-app // 源码调试可以执行这个~原理一样的 执行完之后,找到program.command('create
|
|