从0开始写一个前端脚手架
入职两年了,一直都在疑惑,前端脚手架是怎么生成的,原理是什么。
刚开始参加工作的时候因为项目很忙,工作经验不多,所以一直没有什么空余时间去做一些自己想做的事情。 现在在空余时间学习了一下如何从0开始自己搭建一个自己的脚手架。
写脚手架的方式有很多种,我所知道的方式有shell还有node shell没有搞懂,所以先搞了搞node的方式。
初始化npm
npm init 一路回车下去 或者执行 npm init -y。就可以免去一路回车的痛苦了
创建执行文件 cli.js
在package.json中添加"bin":"cli.js"
将当前文件的执行方法使用npm link连接到全局
mac有可能会提示没有权限请使用 sudo执行。 window用户就请自行百度啦。
执行 npm link or sudo npm link
出现下方情况则代表连接成功
创建模版
1、创建一个文件夹就叫templates
2、创建下方一个文件格式
具体文件内内容请查看juejin.cn/post/700215…
模版我们要使用到ejs插件来写模版 需要添加ejs依赖
npm install ejs -S
参数在模版中是怎样使用的呢🧐
如下
那参数是如何传进模版中的呢? 在编写cli.js文件的时候我会详细说明。
编写cli文件内容
首先要进行交互 使用inquirer插件 使用方法blog.csdn.net/qq_26733915…
cli.js文件 执行 npm install inquirer -S 安装inquirer
// 这句话很重要代表使用node环境
#! /usr/bin/env node
// inquirer插件可以做人机交互,询问创建者问题。
const inquirer = require('inquirer');
......
// question 是人机交互的问题和字段
const question = [
{
type: 'input',
message: 'projectName',
default: 'my-app',
name: 'projectName',
},
{
name: 'language',
type: 'list',
choices: ['react', 'vue'],
default: 'react',
},
{
name: 'style',
type: 'list',
choices: ['less', 'sass'],
default: 'less',
},
{
name: 'jsType',
type: 'list',
choices: ['js', 'ts'],
default: 'js',
},
{
type: 'input',
message: 'version',
name: 'version',
default: '1.0.0',
},
{
type: 'input',
message: 'prot',
default: '3000',
name: 'prot',
},
];
inquirer.prompt(question).then((answer)=>{console.log(answer)})
因为上面已经将文件名字链接到全局了所以直接执行 myl-cli
然后就可以进行生成文件了
就叫他 createProject
// node中文件函数
const fs = require('fs');
// node中路径函数
const path = require('path');
const createProject=(answer)=>{
// 首先我们要使用fs方法获取到当前文件的路径 fs方法是node提供的方法直接引用就可以了
// 然后创建一个文件夹,文件夹名字就从answer中取
// 创建文件之前需要获取到当前文件的路径。使用path方法与fs一样
/**
先获取到当前文件中的templates文件夹
__dirname 代表当前文件的路径
*/
const templatespath = path.join(__dirname, 'templates');
fs.mkdir(answer.projectName,{ recursive: true },()=>{
createFIles(templatespath, answer, answer.projectName);
})
/**
ej.renderFile 无法读取文件夹,所以需要手动创建,脚手架的结构是固定的所以不需要写太多逻辑在里面
只需在需要创建文件夹的地方通过fs.mkdir创建一下
*/
fs.mkdir(`${answer.projectName}/src/app`, { recursive: true }, () => {
const app = `${process.cwd()}/templates/src/app`;
createFIles(app, answer, `${answer.projectName}/src/app`);
});
}
再提取一个createFIles方法出来,创建文件
/**
create files
*/
const createFIles = (templatespath, answer, paths) => {
// 使用文件函数读取当前文件夹内的文件
fs.readdir(templatespath, (err, files) => {
// files是一个数组 遍历这个数据
files.forEach((file) => {
if (file === 'src' || file === 'app') return;
// 通过ejs渲染模版 ejs通过renderFile会获取到文件里面的内容
ejs
.renderFile(path.join(templatespath, file), answer)
.then((data) => {
/**
下面两个if是为了兼容不同的后缀文件
path.extname(file) 获取文件的后缀名称
path.basename(file, '.ejs') 获取后缀名称为.ejs的文件名称。
*/
if (path.extname(file) === '.ejs') {
file = `${path.basename(file, '.ejs')}.js`;
}
if (path.basename(file, '.js') === 'package') {
file = 'package.json';
}
// 通过fs.writeFileSync方法将 renderFile获取到的文件内容写进新的文件中
fs.writeFileSync(path.join(__dirname, `${paths}/${file}`), data);
})
.catch((err) => {
console.log(err);
});
});
});
};
然后我们优化一下 最后完整的cli.js文件如下
#! /usr/bin/env node
// inquirer插件可以做人机交互,询问创建者问题。
const inquirer = require('inquirer');
// node中文件函数
const fs = require('fs');
// node中路径函数
const path = require('path');
// ejs插件 创建ejs模版可以将用户输入的问题渲染到模版中
const ejs = require('ejs');
// 执行任务的loading
const Listr = require('listr');
// node中child_process的exec 可以在node中执行shell脚本
const { exec } = require('child_process');
// 自动执行install的插件
const { projectInstall } = require('pkg-install');
// 打印logo
const figlet = require('figlet');
// 输出文字的颜色样式。
const chalk = require('chalk');
const axios = require('axios');
const question = [
{
type: 'input',
message: 'projectName',
default: 'my-app',
name: 'projectName',
},
{
name: 'language',
type: 'list',
choices: ['react', 'vue'],
default: 'react',
},
{
name: 'style',
type: 'list',
choices: ['less', 'sass'],
default: 'less',
},
{
name: 'jsType',
type: 'list',
choices: ['js', 'ts'],
default: 'js',
},
{
type: 'input',
message: 'version',
name: 'version',
default: '1.0.0',
},
{
type: 'input',
message: 'prot',
default: '3000',
name: 'prot',
},
];
/**
create files
*/
const createFIles = (templatespath, answer, paths) => {
// 使用文件函数读取当前文件夹内的文件
fs.readdir(templatespath, (err, files) => {
// files是一个数组 遍历这个数据
files.forEach((file) => {
if (file === 'src' || file === 'app') return;
// 通过ejs渲染模版 ejs通过renderFile会获取到文件里面的内容
ejs
.renderFile(path.join(templatespath, file), answer)
.then((data) => {
/**
下面两个if是为了兼容不同的后缀文件
path.extname(file) 获取文件的后缀名称
path.basename(file, '.ejs') 获取后缀名称为.ejs的文件名称。
*/
if (path.extname(file) === '.ejs') {
file = `${path.basename(file, '.ejs')}.js`;
}
if (path.basename(file, '.js') === 'package') {
file = 'package.json';
}
// 通过fs.writeFileSync方法将 renderFile获取到的文件内容写进新的文件中
fs.writeFileSync(path.join(__dirname, `${paths}/${file}`), data);
})
.catch((err) => {
console.log(err);
});
});
});
};
console.log(
chalk.green(
'\r\n' +
figlet.textSync('myl-cli', {
font: 'Ghost',
horizontalLayout: 'default',
verticalLayout: 'default',
width: 80,
whitespaceBreak: true,
}),
),
);
/**
* 获取模板列表
* @returns Promise
*/
async function getRepoList() {
return axios.get('https://api.github.com/orgs/zhurong-cli/repos');
}
/**
* 获取版本信息
* @param {string} repo 模板名称
* @returns Promise
*/
async function getTagList(repo) {
return axios.get(`https://api.github.com/repos/zhurong-cli/${repo}/tags`);
}
// create project
const createProject = (answer) => {
// const res = await getRepoList();
/**
先获取到当前文件中的templates文件夹
__dirname 代表当前文件的路径
*/
const templatespath = path.join(__dirname, 'templates');
/**
1、先创建 ${answer.projectName} 文件夹
2、然后创建文件夹中的文件
*/
fs.mkdir(answer.projectName, { recursive: true }, () => {
createFIles(templatespath, answer, answer.projectName);
});
/**
ej.renderFile 无法读取文件夹,所以需要手动创建,脚手架的结构是固定的所以不需要写太多逻辑在里面
只需在需要创建文件夹的地方通过fs.mkdir创建一下
*/
fs.mkdir(`${answer.projectName}/src/app`, { recursive: true }, () => {
const app = `${process.cwd()}/templates/src/app`;
createFIles(app, answer, `${answer.projectName}/src/app`);
});
};
inquirer.prompt(question).then(async (answer) => {
/**
Listr 是一个一步的loading插件。
task指的是要执行那一个任务
Listr执行顺序是从上到下依次执行
*/
const tasks = new Listr([
{
title: chalk.yellow('create directory and file'),
task: () => createProject(answer),
},
{
title: chalk.blue('create git'),
task: () =>
exec('git init', {
cwd: path.join(__dirname, answer.projectName),
}),
},
{
title: chalk.magenta('npm install'),
task: () =>
projectInstall({
cwd: path.join(__dirname, answer.projectName),
prefer: 'npm',
}),
},
]);
await tasks.run();
});
然后执行 myl-cli 就可以啦
此文是我学习了掘金一个大佬的文章之后写的。写的不是很详细。
最后附上大佬文章地址 juejin.cn/post/696611… 大佬的文章更详细