当前位置: 首页 > 后端技术 > Node.js

也来盼盼关于前端脚手架的那些事

时间:2023-04-04 00:12:36 Node.js

字数:3621,阅读时间:14分钟,看完原文,早就想写这篇文章了,但是又被其他事情耽误了(可能是我太懒了),所以我就一直拖到现在,再不写下来,我觉得它会被扔到天上去。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登录,需要输入用户名,密码,邮箱,使用npmpublish发布这一步比较简单,就不多说了,大家注意有以下几点:如果使用nrm,需要切换源为npm官方源码包。package.json中有几个字段需要改进:name是发布包的名称,不能和npm已有的包重名。包中重复的版本就是版本信息,每次发布都要高于线上版本。还可以添加主页、错误和存储库。对应以下页面,在readme.md中添加脚手架介绍和使用方法,方便其他人使用。如果需要在文档中添加logo,显示脚手架的下载次数等,都可以在这里生成。发布成功后,需要等待一段时间,才能到npm仓库中搜索。既然创建命令是脚手架,肯定不能让它输出一段文字。我们还需要定义一些命令。用户在命令行输入这些命令和参数,脚手架就会执行相应的操作。这里我们不需要自己去解析这些输入的命令和参数。有现成的轮子(commander)可以使用,完全可以满足我们的需求。帮助(--help)安装commander后,我们将index.js中的内容改成如下:#!/usr/bin/envnodeconstcommander=require('commander');//使用commander解析命令行输入必须写在所有内容的最后commander.parse(process.argv);这个时候虽然我们还没有定义任何命令,但是指挥官在里面为我们定义了一个帮助命令--help(缩写-h):PSE:\WorkSpace\yncms-template-cli>yncms-template-hUsage:index[options]Options:-h,--helpoutputusageinformationversion(--version)接下来我们创建一个查询版本的命令参数,在index.js中添加如下内容://查看版本号commander.version(要求('./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,--versionoutput版本号-h,--help输出使用信息init子命令接下来我们定义一个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');//用于输出loadingconstchalk=require('chalk');//用于改变文本颜色module.exports=function(remote,name,option){constdownSpinner=ora('Downloadingtemplate...').start();returnnewPromise((resolve,reject)=>{download(remote,name,option,err=>{if(err){downSpinner.fail();console.log(symbols.error,chalk.red(err));reject(err);return;};downSpinner.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=async(name,option)=>{//0.检查控制台是否可以运行`git`,if(!shell.which('git')){console.log(symbols.error,'抱歉,git命令不可用!');shell.exit(1);}//1.验证输入名称是否合法返回;}if(name.match(/[^A-Za-z0-9\u4e00-\u9fa5_-]/g)){console.log(symbols.error,'项目名称中存在非法字符!');返回;}//2.获取选项并确定模板类型(分支)if(option.dev)branch='develop';//4.下载模板awaitclone(`direct:${remote}#${branch}`,name,{clone:true});};module.exports=initAction;测试一下,项目就可以了成功拔出,没有任何意外。这里拉取的项目是关联远程仓库的,我们需要将其删除(因为我们的项目是svn管理的,所以直接删除.git文件夹,如果使用git,可以用gitinit初始化),并清理一些冗余文件://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'模板名称包含非法字符,请重新输入';返回真;}},{type:'input',message:'请输入模板关键字(;拆分):',name:'关键字'},{type:'input',message:'请输入模板介绍:',name:'description'},{type:'list',message:'请选择模板类型:',choices:['responsive','desktop','mobile'],name:'type'},{type:'list',message:'请选择模板类别:',choices:['整个站点','单页','专题'],name:'category'},{type:'input',message:'请输入模板样式:',name:'style'},{type:'input',message:'请输入模板颜色系统:',name:'color'},{type:'input',message:'Pleaseenteryourname:',name:'author'}];//通过查询器获取用户输入的内容constanswers=awaitinquirer.prompt(questions);//打印用户配置并确认console.log('------------------------');console.log(answers);letconfirm=awaitinquirer.prompt([{type:'confirm',message:'Confirmcreation?',default:'Y',name:'isConfirm'}]);如果(!confirm.isConfirm)返回false;获取到用户输入的配置后,就可以编写配置文件或者进行个性化处理了,太简单了,这里不再赘述。到这里锦上添花,一个完全满足需求的脚手架就完成了,但是作为一个有追求的程序员,我们可以在界面和易用性上多做一些事情:为异步操作添加loding动画,可以直接使用oraconstinstallSpinner=ora('正在安装依赖...').start();if(shell.exec('npminstall').code!==0){console.log(symbols.warning,chalk.yellow('自动安装失败,请手动安装!'));installSpinner.失败();//安装失败shell.exit(1);}installSpinner.succeed(chalk.green('依赖安装成功!'));在操作成功或失败时给出图标提示,并使用日志符号为文本添加一些颜色。同样的,使用现成的轮子Chalk安装依赖或者其他时间比较长的时候,用户可以将终端切换到后台。这时候我们可以使用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来创建,不过个人觉得没必要,毕竟这东西不难。一个好的脚手架应该能够解决工作中遇到的问题,提高开发效率。