准备工作
在开始搭建前,确保本机安装了node,为避免奇奇怪怪的问题 建议node版本16以上
搭建脚手架
使用过vue ,react,angular的同学都知道 ,应该对脚手架有一定的理解,比如vue-cli的 vue create myApp ,其中vue 就是vue-cli声明的一个命令,下来我们创建一个项目并声明自己的命令。
创建项目
创建一个空的文件夹 demo,并在demo下初始化npm, 其中 -y 表示都采用默认值,执行成功后会在该目录下 生成package.json文件
npm init -y
声明命令
1.在package.json中 添加 “bin” 字段,来声明命令,其中 create-demo
就是我们本次声明的命令,./bin/index.js
是我们运行 create
后执行的文件路径。
"bin" :{
"create-demo" : "./bin/index.js"
}
- 创建 bin/index.js , 在根目录下 创建bin 目录 ,并且创建index.js, 创建后的内容如下
#!/usr/bin/env node
console.log('简易脚手架demo')
#!/usr/bin/env node 其中
#!
代表标注文件可以当做脚本运行/usr/bin/env
代表绝对地址node
代表需要使用的执行器类型
搭建本地调式环境
创建完成后,我们不能直接执行create-demo
命令,需要搭建一个本地调式的环境,这部分最简单的就是 npm link
, 这个命令就是将本地的项目链接到全局,等同于 npm i -g xxx
.
npm link
执行后如下
npm link
后不生效问题
这个问题是一般都是因为环境变量没有配置正确导致的 ,我们先检查下 当前npm 的配置npm config list --global
找到prefix
的配置路径,将其添加在本机的 环境变量中
这时候代表我们已经初始化成功,这个时候再去调用 create-demo
,这个时候就可以看到之前在 bin 目录下打印的内容
校验node版本
刚开始我们说了,为避免不必要的麻烦,尽量要求用户nodejs版本16以上,这里我们可以用 semver
这个来做个校验。
创建util文件夹,这里文件夹主要放我们封装的一些工具函数, 创建 index.js
const semver = require('semver');
/**
* 检测nodejs版本 是否大于16
*/
function checkNodeVersion() {
const unSupportedVer = semver.lt(process.version, 'v16.0.0');
console.log(`当前node版本${process.version}`);
if(unSupportedVer){
throw new Error('Node.js 版本过低,推荐升级 Node.js 至 v16.0.0+')
}
}
module.exports = {
checkNodeVersion
}
然后在 /bin/index.js
中添加
const { checkNodeVersion } = require('../util/index');
console.log('简易脚手架demo');
checkNodeVersion();
到这里我们一个简易的脚手架就搭建成功了,当然一个脚手架不止这些内容,比如与用户的交互,下载依赖等等,下面将一一讲述。
与用户的交互
命令参数的获取
Node.js 中的 process 模块提供了当前 Node.js 进程相关的全局环境信息。其中process.argv
表示用户输入的 命令行信息,我们可以简单打印下。
console.log(process.argv)
打印结果如下
可以看到 其中 myApp 就是本次输入的内容,当然参数还有多种情况输入 比如下面这两种
create-demo --name=myApp
create-demo --name myApp
这个时候我们打印出来的信息也不同,意味这我们需要处理参数的多种情况,这里推荐一个插件minimist
,来处理。
npm install minimist
通过上面的打印我们注意到,用户输入的从数组的第二个开始,这里我们处理下
#!/usr/bin/env node
const minimist = require('minimist');
const { checkNodeVersion } = require('../util/index');
console.log('简易脚手架demo');
checkNodeVersion();
console.log(minimist(process.argv.slice(2)))
使用结果如下,这样我们就处理了命令参数这块。
这里添加几个简单的命令 --h,--v
对应help和version,以及init,添加后的代码如下
在 bin/index.js 中
#!/usr/bin/env node
const minimist = require('minimist');
const { checkNodeVersion , getPkgVersion } = require('../util/index');
function run (){
checkNodeVersion();
parseArgs();
}
// 处理用户命令行输入信息
function parseArgs(){
const args = minimist(process.argv.slice(2),{
alias :{
version: "v",
help: ["h"],
}
});
// 这里指的是 用户输入的第二个命令 比如 vue create-app 中的 create-app
const command = args._[0];
if(command){
switch (command) {
case 'init':
console.log('创建简易脚手架demo');
break;
default:
break;
}
} else {
if (args.h) {
console.log('Usage: create-demo <command> [options]')
console.log()
console.log('Options:')
console.log(' -v, --version 查看当前版本')
console.log(' -h, --help 查看帮助')
console.log()
console.log('Commands:')
console.log(' init 创建一个新的项目')
} else if (args.v) {
console.log(getPkgVersion());
}
}
}
run();
在 util/index.js 中
const semver = require("semver");
const path = require('path');
/**
* 检测nodejs版本 是否大于16
*/
function checkNodeVersion() {
const unSupportedVer = semver.lt(process.version, "v16.0.0");
console.log(`当前node版本${process.version}`);
if (unSupportedVer) {
throw new Error("Node.js 版本过低,推荐升级 Node.js 至 v16.0.0+");
}
}
/**
* 获取根目录
*/
function getRootPath() {
return path.resolve(__dirname, "../");
}
/**
* 查看当前版本信息
*/
function getPkgVersion(){
return require(path.join(getRootPath(), 'package.json')).version
}
module.exports = {
checkNodeVersion,
getPkgVersion
}
来看下 输出结果
此时整个目录结构如下
- demo
- bin
- index.js
- util
- index.js
- package.json
- bin
询问式交互
一个好的脚手架,与用户的交互是少不了的,这里我们采用询问式交互, 一问一答的来收集用户选择的信息。比如npm init 通过询问式的交互来完成package.json 内容。
这里推荐使用 inquirer
库来完成,这个相对来说功能比较齐全,比如对值的一些校验,过滤不必要的值等等。
npm i inquirer
我们这里 主要是通过 inquirer.prompt
来完成,prompt接收一个数组,其中每一项都是一个问题对象,每一项下面简单描述下问题对象参数的含义
参数名称 | 值的含义 | 类型 |
---|---|---|
type | 问题的类型 |
input | confirm | list | checkbox
|
name | 问题答案的变量 | string |
message | 问题描述 | string |
default | 默认值 | any |
choices | 列表选项 | any[] |
validate | 对当前值的校验 | Function |
filter | 过滤所选值 | Fucnton |
下来我们直接在刚刚的init里面,添加让用户输入项目名称的问题。
case 'init':
console.log('创建简易脚手架demo');
inquirer.prompt([{
type: "input",
name: "projectName",
message: "请输入项目名称!",
}])
break;
这个时候我们再去终端查看
当然一般创建项目肯定不止一个问题 ,下面我们完善下代码
把 问题部分抽离出成一个ask.js, 并添加其余情况的处理,该文件位于 bin 目录下bin/ask.js
const inquirer = require("inquirer");
const fs = require("fs");
async function init(cb) {
try {
const answer = await ask();
console.log(answer);
cb(answer);
} catch (err) {
console.log(chalk.red("项目创建失败:", err));
}
}
/**
* 询问式收集信息
* @returns 用户的答案信息
*/
async function ask() {
let prompts = [];
let ans = {};
// 每一个问题一个函数
askProjectName(ans, prompts);
const answer = await inquirer.prompt(prompts);
return answer;
}
/**
* 创建的项目名称
* @param {*} ans 答案信息
* @param {*} prompts 问题对象
*/
function askProjectName(ans, prompts) {
if (typeof ans.projectName !== "string") {
prompts.push({
type: "input",
name: "projectName",
message: "请输入项目名称!",
validate(input) {
if (!input) {
return "项目名不能为空!";
}
if (fs.existsSync(input)) {
return "当前目录已经存在同名项目,请换一个项目名!";
}
return true;
},
});
} else if (fs.existsSync(ans.projectName)) {
prompts.push({
type: "input",
name: "projectName",
message: "当前目录已经存在同名项目,请换一个项目名!",
validate(input) {
if (!input) {
return "项目名不能为空!";
}
if (fs.existsSync(input)) {
return "项目名依然重复!";
}
return true;
},
});
}
}
module.exports = {
askInit
};
同时创建init.js,预留后续会有新的命令出现,这里把之前校验node版本的也放进来
bin/init.js
const { askInit } = require('./ask');
const { checkNodeVersion } = require('../util/index');
// 执行 init 命令后的处理
async function init(){
checkNodeVersion();
await askInit();
}
module.exports = {
init
}
在 bin/index.js
中,引入init 在用户输入init
后执行
///...
const { init } = require('./init');
///...
case 'init':
init();
break;
弄完我们试验下
到这里我们与用户交互大致就完成了。下来我们来创建项目。
创建项目
创建项目这里大部分都是复制文件去生成,但是因为我们搭建的是一个简易的脚手架,没有要生成的目录,所以这里我们直接去git上去 克隆一个项目来生成自己的项目。
这里我们直接 在终端输入 git clone 你的项目地址
来完成克隆。这里 我们先用 child_process
中的 exec 来实现
child_process 为node 内置的模块 ,无需额外下载 。 具体使用参考官网 https://nodejs.org/api/child_process.html
bin/init.js
const {askInit} = require("./ask");
const {checkNodeVersion} = require("../util/index");
const {exec} = require("child_process");
const chalk = require('chalk');
// 执行 init 命令后的处理
async function init() {
checkNodeVersion();
await askInit(createProject);
}
/**
* 创建项目
*/
function createProject(answer) {
const {projectName} = answer;
exec(`git clone 你的项目地址 ${projectName}`, (err) => {
if (err) {
console.log(chalk.red(err));
} else {
console.log(chalk.green("项目创建成功,即将安装依赖"));
// 安装依赖
}
});
}
module.exports = {
init,
};
此时我们去一个新的目录打开终端,并输入 create-demo init
,结果如下
这个时候项目已经创建成功了。下面将安装依赖
chalk
: 命令行输出美化工具 这里我使用的是 3.0.0版本的
安装依赖
基于克隆项目,安装依赖这里就相对比较简单了,也是直接去掉终端。
/**
* 创建项目
*/
function createProject(answer) {
const {projectName} = answer;
exec(`git clone git@gitee.com:leadersir/resume.git ${projectName}`, (err) => {
if (err) {
console.log(chalk.red(err));
} else {
console.log(chalk.green("项目创建成功,即将安装依赖"));
// 安装依赖
const command = `cd ${process.cwd()}/${projectName} && yarn`;
install(command);
}
});
}
/**
* 下载依赖
*/
function install(command) {
const child = exec(command,(err)=>{
if(err){
console.log(chalk.red('安装项目依赖失败,请自行重新安装!'))
} else {
console.log(chalk.gray('安装成功'))
}
});
// 输出安装的信息
child.stdout.on('data',(data)=>{
console.log(data);
})
// 输出安装的信息
chalk.stderr.on('data',(data)=>{
console.log(data);
})
}
此时我们运行,就可以正常安装依赖了。当然安装依赖这里我们可以加个loading,让用户更直观的感受到,这里推荐使用开源库ora来实现加载动画。
npm i ora@5.0.0
优化后代码如下
function install(command) {
const installSpinner = ora(`正在安装依赖, 请耐心等待...`).start()
const child = exec(command,(err)=>{
if(err){
installSpinner.fail(chalk.red('安装项目依赖失败,请自行重新安装!'))
} else {
installSpinner.succeed(chalk.gray('安装成功'));
}
});
child.stdout.on('data',(data)=>{
installSpinner.stop()
console.log(data.replace(/\n$/, ''))
installSpinner.start()
})
child.stderr.on('data',(data)=>{
console.log(data.replace(/\n$/, ''))
installSpinner.start()
})
}
最后我们再给用户一个提示 说明项目已经创建成功了 ,可以开发了。
/**
* 创建项目成功回调
*/
function callSuccess() {
console.log(chalk.green(`创建项目成功!`));
console.log(chalk.green(` 开始工作吧!😝`));
}
添加到依赖下载成功这里
installSpinner.succeed(chalk.gray("安装成功"));
callSuccess();
此时执行完成后 结果如下
到这里我们的脚手架就搭建成功了,最后我们发布到github 上
npm publish
项目目录&依赖版本信息
文章来源:https://www.toymoban.com/news/detail-828339.html
常用依赖包
-
chalk
: 命令行美化工具 -
inquirer
: 命令行交互工具 -
minimist
: 命令行处理工具 -
ora
: 命令行加载美化工具 -
semver
: 版本比较工具 -
request
: 处理请求
结语
上面主要是讲述如何搭建一个脚手架,比较简单,但这些方法都比较常用,是搭建脚手架的基本功,希望对你有所帮助。以上代码已上传至 gitee中 ,大家可以下载实践。文章来源地址https://www.toymoban.com/news/detail-828339.html
到了这里,关于前端如何搭建脚手架并在本地运行的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!