前端脚手架通过自动化的方式可以提高开发效率并减少重复工作,而最强大的脚手架并不是现成的那些工具而是属于你自己团队量身定制的脚手架!本篇文章将带你了解脚手架开发的基本技巧,帮助你掌握如何构建适合自己需求的工具,并带着你一步步走向前端开发的全新高度。
目录
初识脚手架
脚手架说明:本质上就是一个工具,它可以让我们只用一个命令就生成一个已经配置好的项目而不需要我们再花时间去配置和安装相关依赖,让程序员专注于写具体的代码逻辑从而提升项目的开发效率,很比如我们常用的类似:create-vue 和 create-react-app 就是脚手架,当然很多大厂也都有自己的脚手架,这里我们可以大概的了解一下脚手架工具搭建需要怎样的一个过程:
1)目的:快速生成项目从而避免复制重复内容工作,节省开发时间提升开发效率
2)准备:掌握命令行开发(各种插件包操作)、熟悉node.js应用、npm发包流程
3)功能:快速生成自定义项目配置项,例如:vue-router、pinia功能等
4)完善:根据项目复杂程度不断优化、细化、碎片化功能选择,适配不同项目开发
脚手架演示:接下来我们以vue官方脚手架create-vue为例来演示一下脚手架一个具备哪些功能,终端运行如下命令开始创建项目:
npm create vue@latest
可以看到当我们运行该脚手架命令之后提示我们要执行安装的vue版本,以及介绍了一下vue.js是啥,然后让我们输入项目名称以及相应模板要包含的功能,如下所示:
然后我们就可以切换到下载目录npm i安装依赖,运行项目即可,如下所示:
当然我们开发一个完善的前端脚手架也非一朝一夕能够完成,需要很长的时间去反复试错和打磨才能够生效,这也不是我一两篇文章所能讲解完善的,需要时间不断的积累才能完成,希望读者可以耐心的阅读。
初始化项目
项目初始化:接下来我们开始我们的脚手架项目开发,首先我们先建立一个文件夹然后在文件夹下终端运行命令:npm init 后一路回车操作,文件中会生成一个package.json文件夹,如下所示:
然后接下来我们在根目录下创建bin/index.js文件作为入口文件并添加如下代码,可以看到在文件的第一行有如下这段代码标识,该文件的作用就是告诉系统脚本执行代码的程序为node,系统就会自己寻找node所在路径并直接执行 ./bin/index 文件而不再需要用户再命令前加node了:
#!/usr/bin/env node
配置本地包:然后我们在package包中配置一下bin执行目录,可以自定义执行别名,然后终端执行如下命令将该本地包链接到全局,避免每次都重新安装模块,如果不再需要链接某个模块,可以通过npm unlink来删除全局的符号链接:
配置ts类型:这里我们终端执行如下命令安装typescript以及对应的node类型提示:
npm install typescript -D
npm install @types/node -D
这里我们可以在项目根目录创建.npmrc文件,里面可以配置npm安装工具时的镜像源:
registry=https://registry.npmmirror.com
然后我们在package.json包中配置build打包的时候将ts编译成js代码:
然后接下来我们在项目根目录创建忽略文件.gitignore以及具体业务代码存放地址src目录,然后再创建对ts进行配置的tsconfig.json文件,如下所示:
然后我们在src目录下编写业务代码,终端执行打包命令之后就会把ts代码编译成js文件到bin目录下运行,如下所示:
当然我们也可以配置一些自定义路径,如下所示:
{
"include": ["./src"], // 指定哪些文件和目录需要包含在TypeScript编译过程中
"exclude": ["./node_modules"], // 指定哪些文件和目录应该被排除在TypeScript编译之外
"compilerOptions": { // 控制编译过程的具体行为的选项
"resolveJsonModule": true, // 允许导入JSON模块
"esModuleInterop": true, // 启用ES模块的兼容性,确保能够正确导入其他模块
"allowSyntheticDefaultImports": true, // 允许从没有默认导出的模块导入默认值
"outDir": "./bin", // 指定编译后输出文件的目录
"module": "ESNext", // 指定生成代码的模块系统,例如ESNext表示使用最新的ECMAScript模块规范
"target": "ES6", // 指定ECMAScript目标版本,例如ESNext表示使用最新的ECMAScript规范
"moduleResolution": "Bundler", // 指定模块解析策略,例如Bundler表示使用类似于Webpack的打包工具的策略
"paths": {
"@": ["./src"], // 指定模块的别名,例如"@": ["./src"]表示在代码中可以使用@来引用./src目录下的文件
"@utils": ["./src/utils"],
"@components": ["./src/components"],
"@config": ["./src/config"]
},
}
}
如果想编译打包之后能够解析我们设置的路径@的话,需要安装如下包:
npm i -D tsc-alias
然后我们在配置的打包命令处调用一下即可,如下所示:
命令行工具
初始化玩项目之后,接下来我们就需要借助一些第三方命令行处理工具来实现一下脚手架功能,依赖第三方开源项目的命令行工具大致上如下所示:
1)收集用户输入信息:yargs;commander
2)提供交互体验:inquirer
3)个性化输出:chalk;colors;ora;signale;clui;figlet
4)校验输入准确性:semver;validate-npm-package-name;leven
接下来我们就常用的一些命令行工具插件进行讲解,如下所示:
commander(命令行处理工具)
可以访问 github 链接来查看该工具的具体使用,终端执行如下命令进行安装:
npm install commander
该工具我们基本上用到的就是选项和命令这两个功能,具体可以参考官方文档的讲解,如下所示:
因为我们项目的一些配置信息需要用到package.json文件中配置的一些内容,所以我们需要在下面的配置中配置一下可以引入json文件:
然后我们在src下的index.ts文件中编写如下代码,通过Pack引入json文件,然后引入commander包下的program用于创建命令行的实例,以及一些相应的配置,如下所示:
#!/usr/bin/env node
import Pack from '../package.json'
import { program } from 'commander'
program // 创建命令行接口实例
.name(Pack.name)
.version(Pack.version, '-v, --vers', '输出当前版本号')
.usage('<command>(必填项) [options](可选项)')
.description(`${Pack.description} (作者:${Pack.author})`)
program // 添加命令
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza');
program.parse(process.argv); // 解析命令行参数
const options = program.opts(); // 返回一个包含所有选项的对象
console.log(options)
然后我们终端执行npm run build将ts文件编译成js文件,然后终端运行yyue-cli --help命令,如下:
chalk(命令行输出美化工具)
可以访问 github 链接来查看该工具的具体使用,终端执行如下命令进行安装:
npm install chalk
然后我们可以通过如下的指令来控制显示文字的颜色、样式(如加粗、下划线、背景色等):
import chalk from 'chalk';
console.log(chalk.blue('Hello world!'));
console.log(chalk.red.bold('Hello world!'));
console.log(chalk.green('I am a green line \n\n'));
console.log(chalk.yellow('With a yellow substring') + ' that becomes green again!');
console.log(chalk.hex('#FF6347')('Tomato color'));
console.log(chalk.rgb(123, 45, 67).underline('RGB color'));
inquirer(命令行交互工具)
可以访问 github 链接来查看该工具的具体使用,终端执行如下命令进行安装:
npm install @inquirer/prompts
接下来我们借助这个交互工具来实现一下上面演示create-vue脚手架的命令行操作,这个工具提供的交互命令基本上就是我们常用的一些输入、选择啥的,这里我用输入框、选择框、确认框以及密码框来演示了一下,具体的代码如下所示:
#!/usr/bin/env node
import { input, select, confirm, password } from '@inquirer/prompts';
const answers = {
projectName: // 项目名称
await input({ message: '请输入项目名称\n', required: true, default: 'demo-test', validate: (value) => value.trim().length > 0 }).then((answer) => {
console.log();
return answer;
}),
projectFramework: // 项目框架
await select({ message: '请选择项目框架:(↑/↓ 切换,回车选择)\n', choices: ['vite', 'umi', 'sharepoint'], default: 'vite' }).then((answer) => {
console.log();
return answer;
}),
projectPermission: // 项目权限
await confirm({ message: '是否包含功能A(需要输入密码)', default: false }).then(async (answer) => {
if (answer) {
let attempts = 0; // 初始化尝试次数
const maxAttempts = 3; // 设置最大重试次数
let pwdCorrect = false;
// 循环进行密码输入,最多重试三次
while (attempts < maxAttempts) {
const pwd = await password({ message: '请输入密码', mask: '*' });
if (pwd === '123456') {
console.log('密码正确');
pwdCorrect = true;
break; // 密码正确,跳出循环
} else {
console.log(`密码错误,请重新输入, 剩余尝试次数:` + (maxAttempts - attempts - 1));
attempts++;
}
}
// 如果超过了最大尝试次数,返回 false
if (!pwdCorrect) {
console.log('密码输入错误三次,功能A无法启用');
return false;
}
console.log();
return true; // 密码正确,启用功能A
}
console.log();
return false;
})
};
console.log("最终结果是:", answers);
当然在github上inquirer也是提供了一些其他选项样式的选择,这里需要安装额外的插件,页面也是提供了选项样式的内容,如下所示,根据自身需求选择相应想要的命令行内容,这里不再赘述:
ora(loading美化工具)
可以访问 github 链接来查看该工具的具体使用,终端执行如下命令进行安装:
npm install ora
根据官方文档我们可以通过如下代码进行一个演示:
#!/usr/bin/env node
import ora from 'ora';
const spinner = ora('下载中...').start(); // 启动 spinner
setTimeout(() => {
spinner.color = 'red';
spinner.text = '网络较慢,请稍等...';
}, 1000);
setTimeout(() => {
spinner.succeed('下载完成');
// spinner.fail('下载失败');
}, 4000);
loading-cli(loading美化工具)
可以访问 github 链接来查看该工具的具体使用,终端执行如下命令进行安装:
npm install --save loading-cli
这里我们根据官网给出的案例,简单的封装一下,如下所示:
import loading, { Options, Loading } from "loading-cli";
class Load {
load: null | Loading;
constructor() {
this.load = null;
}
/**
* @param options
* 开始loading状态,第二次开始穿string类型
*/
start(options: Options | any) {
if (!this.load) {
this.load = loading(options).start();
} else {
this.load && this.load.start((options as string));
}
}
/**
* 结束loading状态
*/
stop() {
this.load && this.load.stop();
}
warn(text: string) {
this.load && this.load.warn(text);
}
info (text: string) {
this.load && this.load.info(text);
}
}
export default new Load();
然后我们在调用的loading的时候可以自定义loading的图标内容,如下所示:
#!/usr/bin/env node
import { loading } from "./utils/index.js";
loading.start({
"text":"loading text!!",
"color":"yellow",
"interval":100,
"stream": process.stdout,
"frames":["◰", "◳", "◲", "◱"]
})
setTimeout(() => {
loading.warn("警告信息");
setTimeout(() => {
loading.stop();
}, 1000);
}, 3000);
figlet(生成艺术字)
可以访问 github 链接来查看该工具的具体使用,终端执行如下命令进行安装:
npm install figlet
然后我们根据官网提供的案例,进行使用即可,如下我封装了一个函数然后调用这个函数即可:
const figletFont = () => {
figlet("Hello World!!", function (err, data) {
if (err) {
console.log("Something went wrong...");
console.dir(err);
return;
}
console.log(data);
});
}
当然我们也可以采用调用文本函数的方式,允许从文本创建ASCII Art,也是从官网复制的,如下
fs-extra(操作本地目录)
可以访问 github 链接来查看该工具的具体使用,终端执行如下命令进行安装:
npm install fs-extra
如下我们可以通过process.cwd()来打印当前的项目文件类路径:
然后这里我们借助 fs-extra 工具来实现对项目文件资源的一些操作,以下是校验文件是否存在的演示,这里我们执行一下如果文件存在且用户选择yes的情况下,就对文件进行删除操作:
import { confirm } from '@inquirer/prompts';
import fs from 'fs-extra'
import path from 'path'
// 判断当前文件夹下是否存在传递过来的文件
const isExistFile = async (fileName: string) => {
const targetPath = path.join(process.cwd(), fileName); // 获取当前工作目录下的目标文件路径
if (fs.pathExistsSync(targetPath)) {
const result = await confirm({ message: '是否覆盖已经存在的文件夹内容?', default: false })
if (result) {
fs.removeSync(targetPath)
console.log("覆盖成功")
} else {
// 返回新的名字去创建
return
}
} else {
console.log("targetPath", targetPath)
}
}
git-clone(下载项目模板工具)
可以访问 github 链接来查看该工具的具体使用,终端执行如下命令进行安装:
npm install git-clone
这个工具很简单,就一个函数调用即可,如下我们借上面工具封装的函数进行使用,如果当前文件不存在已经输入的文件夹就创建一个文件,创建的文件内容则拉取我们的模板即可,这里我随便网上找一个模板的下载链接,具体代码如下:
import { confirm, select } from '@inquirer/prompts';
import fs from 'fs-extra'
import path from 'path'
import gitClone from 'git-clone'
// 判断当前文件夹下是否存在传递过来的文件
const isExistFile = async (fileName: string) => {
const targetPath = path.join(process.cwd(), fileName); // 获取当前工作目录下的目标文件路径
if (fs.pathExistsSync(targetPath)) {
const result = await confirm({ message: '是否覆盖已经存在的文件夹内容?', default: false })
if (result) {
fs.removeSync(targetPath)
console.log("覆盖成功")
} else {
// 返回新的名字去创建
return
}
} else {
const projectList = {
vue: 'https://github.com/bailicangdu/vue2-happyfri.git',
react: 'git@github.com:react模板.git'
}
const result: any = await select({ message: '请选择框架模板', choices: ['vue', 'react'] })
await gitClone(projectList[result], fileName, { checkout: 'master' }, function(err) {
if (err) {
console.log('下载模板失败', err)
} else {
console.log("下载模板成功")
}
})
}
}
当然我们在下载模板的时候也可以使用上面讲解到的loading美化工具来进行下载过程的美化:
当然我们下载完项目之后也可以打印一下下载完成的一些提示信息,这里注意一下要删除原项目中的git信息,如下所示:
最后总结: 在这篇文章中,我们探讨了前端脚手架的开发技巧并深入了解了如何构建一个高效、灵活的脚手架系统,掌握这些技巧能够帮助开发者提高工作效率减少重复劳动,同时提供更好的开发体验,无论是自动化的构建流程、模板生成还是模块化的开发结构,都能显著提升项目的可维护性和可扩展性。
当然前端脚手架的世界远不止这些内容,在后续的文章中博主将继续深入探讨更多脚手架相关的知识,包括如何更好地集成第三方工具、优化性能、以及提升团队协作等方面,通过这些分享希望读者能将进一步提升在前端开发中的脚手架使用技巧,创造更高效、更智能的开发流程,后续文章敬请期待!