前言:紧接上篇,这篇文章着重讲解 create.js,这是创建项目的主文件。
从上而下,逐行解析。
- 先看依赖文件
const fs = require('fs-extra') // 这个是操作文件的库,node 自带的库需要写很多的 hack
const path = require('path') // 路径
const chalk = require('chalk') // console 上色
const inquirer = require('inquirer') // 这个很重要,是命令行交互用的,比如我们创建项目的时候,他会让你选择是否采用 vue-router, 是否使用 vuex。const Creator = require('./Creator') // 这个后面细说
const {clearConsole} = require('./util/clearConsole') // 这个就是 console 一些信息,不重要
const {getPromptModules} = require('./util/createTools') // 这个后面说
const validateProjectName = require('validate-npm-package-name') // 校验包名字,不重要
- 重点来看 create 方法,我删除一些不重要的内容
async function create (projectName, options) {
// projectName 就是我们创建的项目名字,比如 hello-word
// options 就是我们输入的参数了,例如 -p -f -d
const cwd = options.cwd || process.cwd() // 运行脚手架的目录
const inCurrent = projectName === '.' // 项目名是否是个.(也可以理解为目录,如果是. 就代表当前目录,不需要创建了)const name = inCurrent ? path.relative('../', cwd) : projectName
const targetDir = path.resolve(cwd, projectName || '.') // 会创建目录
const result = validateProjectName(name) // 校验名字是否合理,例如是否有空格
// 校验错误流被我删除了,无外乎提示错误。if (fs.existsSync(targetDir)) { // 目录是否存在
if (options.force) { // 参数是否有 -f,如果有就强制删除目录
await fs.remove(targetDir)
} else { // 否则就让用户自己选择,是覆盖啊,合并啊,还是啥的
await clearConsole() // 输出一些提示吧,这个不重要
if (inCurrent) { // 如果是当前目录创建项目,给个 confirm 提示
const {ok} = await inquirer.prompt([
{
name: 'ok',
type: 'confirm',
message: `Generate project in current directory?`
}
])
if (!ok) {return}
} else { /
const {action} = await inquirer.prompt([
{
name: 'action',
type: 'list',
message: `Target directory ${chalk.cyan(targetDir)} already exists. Pick an action:`,
choices: [{ name: 'Overwrite', value: 'overwrite'},
{name: 'Merge', value: 'merge'},
{name: 'Cancel', value: false}
]
}
])
if (!action) {return} else if (action === 'overwrite') {console.log(`\nRemoving ${chalk.cyan(targetDir)}...`)
await fs.remove(targetDir)
}
}
}
}
// 总结上面的一堆代码,就是创建一个项目目录,来放代码,如果目录存在是强制删除还是覆盖合并。const creator = new Creator(name, targetDir, getPromptModules()) // 真正创建的代码在这里面去了。// name 就是项目名
// targetDir 就是 path.resolve(cwd, projectName || '.') 就是目录地址
// getPromptModules 这个是引入第三方库
await creator.create(options)
}
看下 getPromptModules(),他引入了好多的库, 这些库,在用户选择不同的初始化项目的时候用得到。
return [
'babel',
'typescript',
'pwa',
'router',
'vuex',
'cssPreprocessors',
'linter',
'unit',
'e2e'
].map(file => require(`../promptModules/${file}`))
真正的创建看来还是要等到下一篇文章再说了,再重点介绍下 inquirer 这个库。
inquirer 命令行交互的库,比如我们常见的
一个是让你手动输入,y 或者 n,一个是选择。
当所有的交互完成会,会返回一个对象,就是用户选择或者输入的值,后续的输出逻辑就依赖这个值。
const inquirer = require('inquirer');
inquirer.prompt([
{
type: 'input',
message: '请选择是否创建(y/n)',
name: 'xory',
default: 'y',
},
{
type: 'list',
message: '你喜欢什么水果?',
name: 'xig',
default: '0',
choices:['西瓜','苹果','香蕉']
},
{
type: 'input',
message: '请输入手机号:',
name: 'phone',
validate: function(val) {if(val.match(/\d{11}/g)) { // 校验位数
return true;
}
return "请输入 11 位数字";
}
}
]).then(answer => {console.log(answer); // answer 就是用户的输入
})
重点的方法就 prompt,prompt 接收一个数组,数组里面是一个配置对象,重点是 type,不同的 type 需要配置不同的数据,比如当 type 是 list 的时候,需要传一个 choices,用于给用户选择。