前端工程化实战:手把手教你构建项目脚手架

面对如今丰富的前端生态,开启新项目时你是否经常陷入这样的纠结:

  1. 在选择构建工具、UI框架、要不要TS等技术选型时,是不是都要重新研究最新的最佳实践?
  2. 当团队需要内部的代码规范、工具链配置、私有依赖等总要手动添加,而影响开发效率?
  3. 当新成员加入时,是否需要大量时间理解项目结构、配置规范,导致配置不一致导致各种奇怪问题?
  4. 当团队项目需要添加特定的中后台、组件库等场景,总要重复的基建代码的Copy

以上烦恼都可以通过前端脚手架搞定,从而不再重复造轮子,而是打造专属自身团队的最佳实践。

本文将从0到1带你构建一个简单的脚手架,以抛砖引玉的方式带了解脚手架的开发。

前端脚手架

前端脚手架本质上是一个Node.js命令程序,它通常有以下功能:

  • 交互式询问用户 通过命令行交互,如确定项目名称、选择框架
  • 模板管理 根据命令行交互的结果远程拉取的项目模板
  • 交互式配置 根据命令行让用户自行选择具体配置
  • 依赖安装 自动安装项目依赖(npm/yarn/pnpm)
  • 命令扩展 支持插件化或自定义命令(可选,进阶功能)

在开发脚手架过程中,使用到一些第三方依赖来帮助我们完成脚手架开发:

  • commander 命令行处理工具
  • chalk 命名行输出美化工具
  • inquirer 命名行交互工具
  • ora 终端loading美化工具
  • git-clone 下载项目模板工具,
  • figlet 终端生成艺术字
  • fs-extra 操作本地目录
  • ejs/handlebars 动态渲染模板文件

前端脚手架实现

1. 初始化项目

bash 复制代码
mkdir case-cli && cd case-cli
npm init -y

2.配置命令入口

json 复制代码
{
  "name": "case-cli",
  "version": "0.0.1",
  "main": "index.js",
  "bin": "/bin/index.js",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "chalk": "^4.1.2",
    "commander": "^14.0.2",
    "fs-extra": "^11.3.2",
    "git-clone": "^0.2.0",
    "inquirer": "^8.2.7", 
    "ora": "^5.4.1"
  }
}

📢 注意

package.json中多数依赖包的最新版本都采用ESM模块化,如果采用Common.js模块化方式,需要适当降级

3. 编写入口文件

js 复制代码
#!/usr/bin/env node
const ora = require("ora"); // loading 美化工具
const chalk = require("chalk"); // 命令行美化工具
const inquirer = require("inquirer"); // 命令行交互
const fs = require("fs-extra"); // 操作本地目录
const path = require("path");
const gitClone = require("git-clone"); // 拉取github模板
const packageJson = require("../package.json"); // 获取package.json
const { program } = require("commander"); // 命令行处理工具
console.log(chalk.blue("学习脚手架工具已启动!"));

📢 注意

必须在文件开头添加 #!/usr/bin/env node,告知操作系统 该文件是通过Node执行

现在我们就可以在命令行中输入case-cli后回车:

然后我们再添加一行代码,通过commanderprogram解析命令行参数:

js 复制代码
#!/usr/bin/env node
/* 依赖引入就此省略 */ 
console.log(chalk.blue("学习脚手架工具已启动!"));
// 解析命令行参数
program.parse(process.argv);

输入case-cli -h命令:

添加获取版本的指令

js 复制代码
#!/usr/bin/env node
/* 依赖引入就此省略 */ 
console.log(chalk.blue("学习脚手架工具已启动!"));
program.version(chalk.green.bold(packageJson.version))
// 解析命令行参数
program.parse(process.argv);

输入case-cli -V将显示脚手架版本号,而且case-cli -h也有变化

一般情况下脚手架类似vue create [project name]来创建项目,在没有输入任何指令时如case-cli将会执行case-cli --help命令显示该脚手架有哪些命令操作。可以如下实现:

js 复制代码
program.action(() => program.help());

注册命令

js 复制代码
program
  .command("create <project-name>") // <project-name> 表示必填参数,如果不填写将会报错
  .description("创建新项目")
  .action(async (projectName) => {
    console.log(projectName);
  });

添加交互配置

js 复制代码
program
  .command("create <project-name>") // <project-name> 表示必填参数,如果不填写将会报错
  .description("创建新项目")
  .action(async (projectName) => {
    inquirer.prompt([
      {
        type: "list",
        name: 'framework',
        message: '请选择框架',
        choices: ["vue", "react"],
      }
    ]).then(async (answers) => {
      const { framework } = answers;
      console.log(chalk.green(`正在创建项目 ${projectName}`));
      console.log(chalk.green(`正在创建 ${framework} 项目`));
    })
  });

当我们输入case-cli create app时,将呈现如下画面:

任意选择一项后:

检查项目名称是否重复

脚手架是以项目名称为目录名称,在当前输入指令的目录下创建的,因此需要检查是否有相同的目录名。并给出提示。

js 复制代码
program
  .command("create <project-name>") // <project-name> 表示必填参数,如果不填写将会报错
  .description("创建新项目")
  .action(async (projectName) => {
    inquirer.prompt([
      {
        type: "list",
        name: 'framework',
        message: '请选择框架',
        choices: ["vue", "react"],
      }
    ]).then(async (answers) => {
      const { framework } = answers;
      // 拼接创建项目目录地址
      const projectPath = path.join(process.cwd(), projectName);
      // 检查是否存在相同目录
      const isExist = fs.existsSync(projectPath);
      if (isExist) {
        // 提供交互选择 覆盖则删除之前目录,反之则退出此次命令
        const result = await inquirer.prompt([
          {
            type: "confirm",
            message: "当前目录下已存在同名项目,是否覆盖?",
            name: "overwrite",
            default: false,
          },
        ]);
        if (result.overwrite) {
          fs.removeSync(projectPath);
          console.log(chalk.green("已删除同名项目"));
        } else {
          console.log(chalk.yellow("请重新创建项目"));
          return;
        } 
      }
    })
  });

拉取远程模板

js 复制代码
const spinner = ora(chalk.magenta("正在创建项目...")).start();
const remoteUrl = `https://github.com/gardenia83/${framework}-template.git`;
gitClone(remoteUrl, projectPath, { checkout: "main" }, function (err) {
  if (err) {
    spinner.fail(chalk.red("拉取模板失败"));
  } else {
    spinner.color = "magenta";
    // 由于拉取会将他人的.git,因此需要移除
    fs.removeSync(path.join(projectPath, ".git")); // 删除.git文件
    spinner.succeed(chalk.cyan("项目创建成功"));
    console.log("Done now run: \n");
    console.log(`cd ${projectName}`);
    console.log("npm install");
    console.log("npm run dev");
  }
});

拉取远程模板项目: 拉取完成后:

小结

通过本文,我们完成了一个基础但功能完整的前端脚手架,实现了项目创建、模板拉取、冲突处理等核心功能。这个简单的脚手架已经能够解决文章开头提到的部分痛点:

统一技术选型 - 通过预设模板固化团队最佳实践

快速初始化 - 一键生成项目结构,告别手动配置

规范团队协作 - 新成员无需理解复杂配置,开箱即用

但这仅仅是一个开始! 你可以基于这个基础版本,根据团队实际需求进行深度定制:

🛠 模板动态化 - 集成 ejs 等模板引擎,根据用户选择动态生成配置文件

🛠 生态集成 - 添加 ESLint、Prettier、Husky 等工程化工具链

🛠 场景扩展 - 针对中后台、组件库、H5 等不同场景提供专属模板

🛠 插件机制 - 设计插件系统,让团队成员也能贡献功能模块

最好的脚手架不是功能最全的,而是最适合团队工作流的。 希望本文能成为你打造团队专属工具链的起点,让重复的配置工作成为历史,把宝贵的时间留给更有价值的创新!

相关推荐
醉方休2 小时前
开发一个完整的Electron应用程序
前端·javascript·electron
故作春风2 小时前
手把手实现一个前端 AI 编程助手:从 MCP 思想到 VS Code 插件实战
前端·人工智能
不会算法的小灰2 小时前
Vue.js 基础教程:从入门到实践
前端·javascript·vue.js
掘金一周2 小时前
没开玩笑,全框架支持的 dialog 组件,支持响应式| 掘金一周 11.6
前端·人工智能
拉不动的猪3 小时前
浏览器&Websocket&热更新
前端·javascript·vue.js
im_AMBER3 小时前
React 12
前端·javascript·笔记·学习·react.js·前端框架
开开心心就好3 小时前
电脑音质提升:杜比全景声安装详细教程
java·开发语言·前端·数据库·电脑·ruby·1024程序员节
午安~婉3 小时前
HTML CSS八股
前端·css·html
有事没事实验室3 小时前
css变量
前端·css