/技术
分类:技术最近更新:2026-04-15浏览:843
现在前端开发越来越把重心放到业务层上,有效的缩短了研发周期,找个开箱即用的脚手架模板项目,直接开搞。比如 vue-cli,create-react-app,umi 等等。如果我们自己想要搭建一个自己的脚手架,该如何做。
首先得分析一下脚手架做了什么?脚手架主要有两个部分组成,初始化命令模块和模板项目。以 vue-cli 为例,脚手架包含了询问的能力,比如目录的名称,项目的描述,是否需要 css 预处理器,路由组件,单元测试模块等等。其次,脚手架还包含了复制下载模板项目的能力,大部分的脚手架都会把模板放到 github 上或者脚手架自己的目录结构里,不要以为一个空白的目录里多出的文件是写进去的,没那么复杂,大部分都是先搭建一个独立的项目作为之后复制的模板。脚手架只是替换模板里的变量,然后复制模板到项目的路径里。最后,有的脚手架还带了 git 初始化和安装依赖的能力。
所以,脚手架一般的步骤可分为:询问==》下载模板==》复制并写入模板==》git 初始化和安装依赖。
安装脚手架的能力,我们先安装以下几个重要的依赖:
commander // 完整的 node.js 命令行解决方案,帮您通过 shell 里打命令inquirer // node.js 交互式命令行,处理一些询问的功能download-git-repo // 负责从 git 下载仓库到本地文件夹mem-fs-editor // 负责编辑模板里变量其实把这几个主要的依赖包 API 文档看下来,就基本知道我们该如何下手搭建初始化工具了。
除了上述主要依赖包,还有其他辅助依赖:
javascriptconst chalk = require('chalk'); // 带颜色的命令提示 const ora = require('ora'); // 提供交互 spinner,就像 VUE-CLI 中的各种小图标
javascript├── bin // 脚手架启动目录 | ├── index // 入口启动文件,和 package.json 中的 main 值对应 ├── src | ├── project.js // 脚手架构造函数 | └── utils.js // 辅助类工具 └── package.json
javascript#! /usr/bin/env node const program = require('commander'); const fse = require('fs-extra'); const inquirer = require('inquirer'); const Project = require('../src/project'); const { getPackageVersion } = require('../src/utils'); const creactProject = (projectName) => { const project = new Project({ projectName }); project.create(); }; function setProjectName(msg) { let prompts = []; prompts.push({ type: 'input', name: 'projectName', message: msg, validate(value) { if (!value) { return '项目名不能为空'; } if (fse.existsSync(value)) { return '当前目录已存在同名项目,请更换项目名'; } return true; } }); inquirer.prompt(prompts).then(answer => { creactProject(answer.projectName); }); } program .version(getPackageVersion(), '-v, --version', '当前版本') .command('init [dirname]') .description('创建新引用') .action(dirname => { if (dirname && fse.existsSync(dirname)) { setProjectName('当前目录已存在同名项目,请更换项目名') } else if (dirname) { creactProject(dirname); } else { let prompts = [ { type: 'confirm', name: 'empty', message: '确定在当前目录下建立项目吗?', default: false } ]; inquirer.prompt(prompts).then(answer => { if (answer.empty) { creactProject(dirname); } else { setProjectName('请输入目录名称') } }); } }); program.parse(process.argv);
javascript/* 项目初始化 */ /* ================================================== */ Project.prototype.create = function () { let __this = this; let questions = [ { type: 'input', name: 'appName', message: "您的项目名称是什么?", validate: function (value) { let pass = value.match(/^[a-zA-Z_\-]+$/); if (pass) { return true; } return '项目名称最好是英文,下划线或者-'; }, default: 'App' }, // ………… ]; inquirer.prompt(questions).then(answers => { __this.options = Object.assign(__this.options, answers); __this.generate(); }); };
javascript/* 渲染项目 */ /* ================================================== */ Project.prototype.generate = function () { const __this = this; const projectPath = path.join(process.cwd(), this.options.projectName); // 新项目绝对路径 const downloadPath = path.join(projectPath, '__download__'); // 模板 copy 临时目录 const downloadSpinner = ora('正在下载模板,请稍等...'); downloadSpinner.start(); /* 开始下载模板 */ const gitRepositories = 'direct:https://xxxx.git'; // 模板项目仓库地址 download(gitRepositories, downloadPath, { clone: true }, (err) => { if (err) { downloadSpinner.color = 'red'; downloadSpinner.fail(err.message); return; } downloadSpinner.color = 'green'; downloadSpinner.succeed('下载模板成功'); const filesSpinner = ora('开始创建项目文件,请稍等...'); const copyFiles = getDirFileName(downloadPath); const injectFiles = ['package.json', 'app.config.js']; /* 复制文件 */ copyFiles.forEach((file) => { fse.copySync(path.join(downloadPath, file), path.join(projectPath, file)); }); /* 写入变量 */ injectFiles.forEach((file) => { this.memFsEditor.copyTpl( path.join(downloadPath, file), path.join(this.options.projectName, file), { appName: __this.options.appName, description: __this.options.description } ); }); /* 变量写入完成后 */ this.memFsEditor.commit(() => { fse.remove(downloadPath); // 移除临时文件 process.chdir(projectPath); // cd 到项目文件夹目录 downloadSpinner.color = 'green'; downloadSpinner.succeed('项目创建完成'); // ………… }); }) };
<%= 变量名称 %>在调用 copyTpl 时,data 字段中的 key-value 将被写入到模板中。
如 package.json:
json{ "name": "<%= appName %>", "version": "1.0.0", "description": "<%= description %>", "main": "index.js", "scripts": { "lint": "eslint --quiet --no-inline-config src/" } }
编辑好自己的脚手架之后就可以发布到 npm 上,供大家使用了。
bashnpm publish
关于如何在 npm 发布依赖包之前有介绍过,可以点击这里。
查看完整的脚手架代码,可以查看创蓝脚手架:https://github.com/chanlan253/cl253-cli
上一篇:前后端分离下如何SEO网站开发