上一篇树酱讲《前端工程化那些事》,聊到脚手架,不过时间比较仓促,导致内容较少,而在我实践开发中,随着新项目愈来愈多,脚手架工具就起到提高效能的作用,借此机会跟小伙伴们分享下我是如何从0到1开发一个简单脚手架
1.什么是脚手架
❝脚手架用于快速生成新项目的目录模板,并集成一系列体系化工具的安装,能够提升前端开发人员的效率,减少copy操作
❞
目前比较主流的脚手架:
- Vue脚手架:Vue-cli
- React脚手架:create-react-app
- Yeoman
2.我期望的脚手架
❝而我所期望的脚手架是怎么样的呢?
❞
当我要开启一个新项目的开发,可以快速生成新项目的目录模板,而这个目录结构是每个项目统一个模版规范(目录规范),同时也设定了通用的配置包括如下
- 通用的Webpack配置(vue cli 3x 以上是vue.config.js)
- 统一的Eslint 校验规则:如Airbnb、eslint-plugin-vue等(eslintConfig)
- 统一的单元测试框架配置:单元测试覆盖率、测试的目录等
- 统一的Dockerfile和jenkinsfile (用来打包成镜像和部署流水线定义)
- 统一babel的配置(.babelrc或babel.config.js)
- 统一的常量配置(缓存字段等等)
- 不同环境的配置文件(development、test、production)
❝没有脚手架,我只能通过copy拷贝代码来完成,这样繁琐又机械化的操作浪费大量时间,而且还可能在拷贝过程中,因为某个细节出错,导致项目出错,排查问题又耗时。或许你可能会想,我们不是可以用vue或者react官方的脚手架来生成模版吗?是,但是这种方式创建的模版不一定符合你内部结构化标准
❞
为了解决上述问题,脚手架就起到一个至关重要的角色,我们可以通过脚手架来约束好规范,统一的配置,来打通新项目的开发工具链,一方面提升开发效率,一方面则提高项目对接可维护性及新员工熟悉项目简易性。
3.开发脚手架
3.1 如何开发
❝如果是要开发一个高度可定制化的脚手架,需要考虑的因素很多,因为某种限制,选择了一种简易的方式来实现内部的脚手架工具,远离就是通过准备两个模版,一个是pc端的,另一个是mobile端的模版,然后用git管理起来,我需要如下工具:
❞
- 可用于控制台选择的工具:inquirer
- 可处理控制台命令的工具:commander
- 可改变输出log颜色的工具:chalk
- 可执行shell命令的工具: child_process
入口文件 index.js
#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); const commander = require('commander'); const inquirer = require('inquirer'); const checkDire = require('./utils/checkDire.js'); const { exec } = require('child_process'); const { version } = require('../package.json'); const { promptTypeList } = require('./config'); //version 版本号 commander.version(version, '-v, --version') .command('init <projectName>') .alias("i") .description("输入项目名称,初始化项目模版") .action(async (projectName,cmd) => { await checkDire(path.join(process.cwd(),projectName),projectName); // 检测创建项目文件夹是否存在 inquirer.prompt(promptTypeList).then(result => { const {url, gitName, val} = result.type; console.log("您选择的模版类型信息如下:" + val); console.log('项目初始化拷贝获取中...'); if(!url){ console.log(chalk.red(`${val} 该类型暂不支持...`)); process.exit(1); } exec('git clone ' + url, function (error, stdout, stderr) { if (error !== null) { console.log(chalk.red( `clone fail,${error}` )); return; } fs.rename(gitName, projectName, (err)=>{ if (err) { exec('rm -rf '+gitName, function (err, out) {}); console.log(chalk.red(`The ${projectName} project template already exist`)); } else { console.log(chalk.green(`The ${projectName} project template successfully create(项目模版创建成功)`)); } }); }); }) }); commander.parse(process.argv);
这里定义的是npm包命令bin的入口文件
需要注意在文件前面定义#!/usr/bin/env node
❝#!/usr/bin/env node设置后,可以让系统动态的去查找node,已解决不同机器不同用户设置不一致问题
❞
检测目录是否存在
// utils/checkDire.js const fs = require('fs'); const chalk = require('chalk'); const path = require('path'); module.exports = function (dir,name) { let isExists = fs.existsSync(dir); if (isExists) { console.log(chalk.red( `The ${name} project already exists in directory. Please try to use another projectName` )); process.exit(1); }
配置文件
// config/index.js 配置文件 /* @dest: 使用配置文件 @Author: tree */ module.exports = { promptTypeList:[{ type: 'list', message: '请选择拉取的模版类型:', name: 'type', choices: [{ name: 'mobile', value: { url: '', gitName: 'vue-web-template', val:'移动端模版' } },{ name: 'pc', value: { url: 'https://github.com/littleTreeme/vue-web-template.git', gitName: 'vue-web-template', val:'PC端模版' } }] }], };
❝源码链接:github.com/littleTreem… 如果你觉得实用请给个🌟支持,在此感谢
❞
3.2 工具详解
- inquirer
❝一个用户与命令行交互的工具
❞
基本用法 🔗使用文档
const inquirer = require('inquirer'); const promptList = [ type: 'list', message: '请选择拉取的模版类型:', name: 'type', choices: ['mobile','pc'] ]; inquirer.prompt(promptList).then(type => { console.log(type); // 返回 mobile 或 pc })
场景如下
- commander
❝commander是一个轻巧的nodejs模块,提供了用户命令行输入和参数解析强大功能
❞
使用到的commander API 🔗使用文档
const commander = require('commander'); commander.version(version, '-v, --version') .command('init <projectName>') .alias("i") .description("输入项目名称,初始化项目模版") .action(async (projectName,cmd) => { console.log(projectName,'你输入的<projectName>') }) commander.parse(process.argv); // command – 定义命令行指令,后面可跟上一个name,用空格隔开 // alias – 定义一个更短的命令行指令 // description – 描述,它会在help里面展示 // option – 定义参数 // action – 注册一个callback函数 // parse - 解析命令行
- chalk
❝node终端样式库,让你的日志样式更美观,主要用chalk来区别错误与成功的日志
❞
如何使用 🔗使用文档
const chalk = require('chalk'); // 报错日志用红色来显示 chalk.red(`The project already exists in directory. Please try to use another projectName`)); // 成功日志用绿色来显示 chalk.green(`The project template successfully create(项目模版创建成功)`);
3.3 如何使用
可以先通过试着本地安装尝试流程阅读使用文档
如下所示是自己开发的一个kdv-cli运行时的示意图
那么 kdv-cli 命令是怎样映射进去的?,原因在于 package.json 里面的 定义了 bin 字段;
// package.json "bin": { "kdv-cli": "./bin/index.js" },
我选择pc类型,然后创建名为 test的项目(暂还不支持mobile)
重复创建则报错
拉取后的项目目录结构如下所示:
4.注意事项
- 注意事项
❝当你完成脚手架开发时,你想本地测试是否成功运作,会出现这种情况
❞
这是因为你本地找不到命令执行的路径,没有映射到bin中去,那么如何在本地测试刚开发玩的脚手架工具命令,那就是用npm link
,如下所示即可
5.结尾
❝通过上文所述,我们就从0到1完成kdv-cli脚手架开发 ,该工具或许不太适用于每个场景,但可以梳理一个简单的脚手架的搭建过程,为后期做更全面、功能更强大的脚手架奠定基础,如果你喜欢,请给树酱点个✨ github.com/littleTreem…
❞
往期文章