当前位置: 首页 > 科技观察

我也想写写前端脚手架的那些事儿

时间:2023-03-18 17:21:43 科技观察

这篇文章早就想写了,但是因为别的事情耽误了时间(可能是太懒了),所以一直拖到现在。如果我不写下来,我可能会把它扔掉。现在是云九。NodeJs的出现,加深了前端工程化的概念,正在向正规军靠拢。先是带来了Gulp、Webpack等强大的构建工具,随后又出现了vue-cli、create-react-app等完善的脚手架,提供了完整的项目结构,让我们可以更专注于业务,而不必去关注很多时间花在项目基础设施上。但是,这些现成的脚手架不一定能满足我们的业务需求,也未必是最佳实践。这个时候我们可以自己开发一个脚手架。当然,这个其实很简单,使用npm上现成的轮子就可以搞定。在此记录一下,仅作备忘,抛砖引玉。上半年的一个项目中,需要定制一个脚手架,帮助小伙伴们提高开发效率,统一代码输出质量,解决一些使用中的问题。当然,也是为了装逼。在使用脚手架方式搭建之前,我们遇到了这些问题:创建每个项目时,需要去Git仓库拉取项目模板或者复制之前的项目。这样做有两个问题。从Git中拉取项目后,由于部分人员有push权限。如果他错误地将私有项目中的修改推送到模板仓库,可能会破坏Git上的项目模板。从之前的项目拷贝中,无法获取到最新的项目模板,从而导致一些问题。最新的模板已经修复了,但是新建工程中还是有一些配置信息。项目模板需要填写一些配置信息。开发人员很容易忘记填写。所以让我们来解决这些问题。思路如下:从Git中拉取最新的模板,最后杀掉Git仓库信息,切断与远程仓库的关联初始化的时候,通过问答的方式强制用户输入配置信息,然后根据配置信息生成配置文件,类似于VueCli初始化工程。当然,除了上述需求之外,我们还可以做一些额外的工作:拉取完成后,自动安装项目依赖,打开编辑器,提供帮助信息和常用命令查看和发布到npm。所有人员都可以在全局直接安装使用...先实现可执行模块,我们需要创建一个工程,这里叫yncms-template-cli,工程结构如下:-commands//这个文件夹是用来放置自定义命令-utils-index.js//项目入口-readme.md为了测试,我们先在index.js中放一些内容:#!/usr/bin/envnode//上面的内容必须加入文件头指定运行环境为nodeconsole.log('hellocli');对于一般的nodejs项目,我们可以直接使用nodeindex.js,但是这里是脚手架,所以一定不能这样。我们需要将项目发布到npm,用户可以全局安装,然后就可以直接使用我们自定义的命令,比如yncms-template。因此,我们需要对我们的项目进行更改。首先在package.json中添加如下内容:"bin":{"yncms-template":"index.js"},这样就可以将yncms-template定义为命令,但是只能在项目中使用此时,它不能用作全局命令。这里我们需要使用npmlink将其链接到全局命令。执行成功后,你可以在你的全局node_modules目录下找到对应的文件。然后输入命令进行测试。如果出现如下内容,则第一步已经成功一半以上:PSE:\WorkSpace\yncms-template-cli>yncms-templatehellocli但是这个命令目前只能在我们自己的电脑上使用。也可以安装使用。您需要将其发布到npm。大致流程如下:注册一个npm账号。如果已经有账号,可以跳过这一步,使用npmlogin登录,需要输入用户名、密码和邮箱。使用npmpublic发布相对简单,不多说,但请注意以下几点:如果使用nrm,需要将源切换为npm官方源码包。package.json中有几个字段需要改进:name是发布包的名称,不能和npm已有的包重复version作为版本信息。每个版本必须高于在线版本。还可以添加主页、错误和存储库。对应以下页面,在readme.md中添加脚手架介绍和使用方法,方便大家使用。如果需要在文档中添加logo,显示脚手架的下载次数等,都可以在这里生成。发布成功后,需要等待一段时间,才能到npm仓库中搜索。既然创建命令是脚手架,肯定不能让它输出一段文字。我们还需要定义一些命令。用户在命令行输入这些命令和参数,脚手架就会执行相应的操作。这里我们不需要自己去解析这些输入的命令和参数。有现成的轮子(commander)可以使用,完全可以满足我们的需求。帮助(--help)安装commander后,我们将index.js中的内容改成如下:#!/usr/bin/envnodeconstcommander=require('commander');//使用commander解析命令行输入Commander.parse(process.argv)必须写在所有内容的末尾;这个时候虽然我们还没有定义任何命令,但是commander内部为我们定义了一个帮助命令--help(简称-h):PSE:\WorkSpace\yncms-template-cli>yncms-template-hUsage:index[options]Options:-h,--helpoutputusageinformationversion(--version)接下来我们创建一个查询版本的命令参数,在index.js中添加如下内容://查看版本号commander.version(require('./package.json').version);这样我们就可以在命令行查看版本号了:PSE:\WorkSpace\yncms-template-cli>yncms-template-V1.0.10PSE:\WorkSpace\yncms-template-cli>yncms-template--version1.0.10默认参数是大写的V,如果需要改成小写,只需将上面的内容改成如下://查看版本号commander.version(require('./package.json').version)。option('-v,--version','查看版本号');PSE:\WorkSpace\yncms-template-cli>yncms-template-hUsage:index[options]Options:-V,--versionoutputtheversionnumber-h,--helpoutputusageinformationinitsubcommand接下来,让我们定义一个init命令,例如yncms-templateinittest。在index.js中添加如下内容:commander.command('init')//定义init子命令,为必填参数,可在action的函数中接收。如果需要设置非必要参数,可以使用Brackets.option('-d,--dev','getdevelopmentversion')//配置参数,简写和全写使用,split.description('Createproject')//命令description.action(function(name,option){//命令执行操作,参数对应上面设置的参数//我们需要执行的所有操作都在这里完成console.log(name);console.log(option.dev);});现在测试一下:PSE:\WorkSpace\yncms-template-cli>yncms-templateinittest-dtesttruecommander具体用法请自行查看官方文档。这样,即使完成了自定义命令的原型,还有几件事情要做:实现init命令执行的具体操作,下面会有单独的部分。为了方便维护,将commandaction拆分到commands文件夹中拉取上面的工程。我们定义了init命令,但是并没有达到初始化项目的目的。接下来,我们将实现它。一般来说,项目模板有两种处理方式:将项目模板和脚手架放在一起,好处是用户安装好脚手架后,模板就在本地,初始化会比较快;缺点是项目模板的更新比较麻烦,因为加上脚手架将项目一起放到单独的git仓库中。优点是模板更新比较简单,因为是相互独立的。你只需要维护模板自己的仓库即可。另外可以控制拉取权限,因为如果是私有项目,那么没有权限的人是无法拉取成功的;缺点是每次初始化都需要从GIT中pull,可能会比较慢,但是影响不大,所以推荐选择这种方式。首先,我们使用download-git-repo封装了一个clone方法,用于从git中拉取项目。//utils/clone.jsconstdownload=require('download-git-repo');constsymbols=require('log-symbols');//用于输出图标constora=require('ora');//用于输出加载constchalk=require('chalk');//用于改变文字颜色module.exports=function(remote,name,option){constdownSpinner=ora('正在下载模板...').start();returnnewPromise((resolve,reject)=>{download(remote,name,option,err=>{if(err){downSpinner.fail();console.log(symbols.error,chalk.red(err));reject(err);return;};down??Spinner.succeed(chalk.green('模板下载成功!'));resolve();});});};//commands/init.jsconstshell=require('shelljs');constsymbols=require('log-symbols');constclone=require('../utils/clone.js');constremote='https://gitee.com/letwrong/cli-demo.git';letbranch='master';constinitAction=异步(名称,选项)=>{/0。检查控制台是否可以运行`git`,if(!shell.which('git')){console.log(symbols.error,'sorry,git命令不可用!');shell.exit(1);}//1。验证输入名称是否合法^A-Za-z0-9\u4e00-\u9fa5_-]/g)){console.log(symbols.error,'项目名称存在非法字符!');return;}//2.获取选项,判断模板类型(分支)if(option.dev)branch='develop';//4.下载模板awaitclone(`direct:${remote}#${branch}`,name,{clone:true});};module.exports=initAction;gitinitinitialization,项目可以拉取成功),清理一些多余的文件://commands/init.js//5。清理文件constdeleteDir=['.git','.gitignore','README.md','docs'];//需要清理的文件constpwd=shell.pwd();deleteDir.map(item=>shell.rm('-rf',pwd+`/${name}/${item}`));个性化上面的过程,我们实现了一个脚手架的基本功能,大致分为三个过程(拉取模板->创建项目->整理清理),也解决了我上面项目遇到的第一个问题。接下来,我们看看如何解决第二个问题。解决方法是强制开发者在创建项目时通过命令行输入相应的配置,然后自动写入配置文件,这样可以有效避免忘记填写的尴尬。当然,通过这种方式,项目也可以根据用户的输入进行动态初始化,达到个性化的目的。这里我们可以直接使用现成的轮询器。效果和用VueCli创建项目是一样的。它支持的类型很多,功能比较强大,也比较简单。具体使用请参考官方文档。这里我直接上代码,在第4步(下载模板)前添加如下://init.jsconstinquirer=require('inquirer');//定义要问的问题constquestions=[{type:'input',message:'请输入模板名称:',name:'name',validate(val){if(!val)return'模板名称不能为空!';if(val.match(/[^A-Za-z0-9\u4e00-\u9fa5_-]/g))return'模板名称包含非法字符,请重新输入';returntrue;}},{type:'input',message:'请输入模板关键字(;拆分):',name:'keywords'},{type:'input',message:'请输入模板介绍:',name:'description'},{type:'list',message:'请选择模板类型:',choices:['responsive','desktop','mobile'],name:'type'},{type:'list',message:'请选择模板类别:',choices:['整个站点','单页','专题'],name:'category'},{type:'input',message:'请输入模板style:',name:'style'},{type:'input',message:'请输入模板色系:',name:'color'},{type:'input',message:'请输入你的name:',name:'author'}];//通过查询器获取用户输入的内容constanswers=awaitinquirer.prompt(questions);//打印用户配置确认是否正确console.log('----------------------');console.log(answers);letconfirm=awaitinquirer.prompt([{type:'确认',message:'确认创建?',default:'Y',name:'isConfirm'}]);if(!confirm.isConfirm)returnfalse;获取到用户输入的配置后,可以这样写配置文件还是个性化处理,这个太简单了,这里就不细说了。到这里锦上添花,一个完全满足需求的脚手架就完成了,但是作为一个有追求的程序员,我们可以在界面和易用性上做一些更上一层楼的事情:为异步操作添加loding动画,可以直接使用oraconstinstallSpinner=ora('安装依赖...').start();if(shell.exec('npminstall').code!==0){console.log(symbols.warning,chalk.yellow('自动安装失败,请手动安装!'));installSpinner.fail();//安装失败shell.exit(1);}installSpinner.succeed(chalk.green('依赖安装成功!'));操作成功或失败会有图标提示,可以使用log-symbols给文字加点颜色。同样,用现成的Chalkwheel安装依赖或其他东西也需要很长时间,此时用户可能会将终端切换到后台。这时候我们可以使用node-notifier在操作完成后向用户发送系统通知。notifier.notify({title:'YNCMS-template-cli',icon:path.join(__dirname,'coulson.png'),message:'?(\?\●)?恭喜,项目创建成功!'});在创建项目的时候,我们可能需要执行一些shell命令,这可以使用shelljs来完成。比如我们想在项目创建完成后打开vscode,退出终端//8.打开编辑器if(shell.which('code'))shell.exec('code./');shell.exit(1);结束语到这里,你会发现开发脚手架其实很简单,用现成的轮子就可以搞定。不知道哪位大牛说玩NodeJS就是玩轮子。除了上面的方法,我们也可以直接通过大名鼎鼎的Yeoman来创建,不过个人觉得没必要,毕竟这东西不难。一个好的脚手架应该能够解决工作中遇到的问题,提高开发效率。