实现一个简单的前端脚手架

news2024/11/14 5:24:40

核心要点

  • 前端脚手架概念
  • 实现前端脚手架

什么是前端脚手架?

随着前端工程化的概念越来越深入人心,脚手架应运而生。简单来说,「前端脚手架」就是指通过选择几个选项快速搭建项目基础代码的工具

前端脚手架可帮我们做什么?

  • 可以帮助我们快速生成项目的基础代码
  • 脚手架工具的项目模板经过了开发者的提炼和检验,一定程度上代表了某类项目的最佳实践
  • 脚手架工具支持使用自定义模板,我们可以根据不同的项目进行“定制”

我为什么要搭建一个前端脚手架?

主要在开发过程中,要做的项目很多,有涉及到vue2、vue3、react、uniapp、小程序等等,然后又有PC端、移动端;针对这些端的项目,我搭建好了基础的开发模板,但是呢,感觉每次新建一个项目,都需要从仓库中下载,感觉总少了点什么。于是想到了像vue-cli一样,搭建一个脚手架来创建项目行不行呢?
所以,我的目标很明确,我的脚手架的功能就是通过命令的方式,下载不同的项目模板,然后供所有项目组人员快速建立项目模板。

实现前端脚手架

1、安装前端脚手架制作所需要的一些插件

npm install chalk@4.0.0 commander@9.4.1 download-git-repo@3.0.2 fs-extra@11.1.0 inquirer@8.0.0 log-symbols@4.0.0 ora@5.0.0 path@0.12.7 update-notifier@4.0.0
"chalk": "^4.0.0", //用于修改终端(terminal)输出的字符串样式,包括字体色、背景色、字体样式
"commander": "^9.4.1", //是 node.js 命令行解决方案
"download-git-repo": "^3.0.2", //主要用来从一个代码仓库中下载文件的到目标文件夹
"fs-extra": "^11.1.0", //加强版的 fs(node 文件系统模块),主要用于操作文件
"inquirer": "^8.0.0", //inquirer.js 是一个用来实现命令行交互式界面的工具集合,它帮助我们实现与用户的交互式交流
"log-symbols": "^4.0.0", //为各种日志级别提供着色的符号
"ora": "^5.0.0", //主要用于创建和展示终端加载动画
"path": "^0.12.7", //主要用于格式化或拼接完整路径
"update-notifier": "^4.0.0" //检测 npm 包是否更新

2、文件结构如下,创建相应的文件,配置 package.json文件

在这里插入图片描述

3、编写 /bin/index.js,主要是编写交互的命令以及对应执行的操作

#!/usr/bin/env node

// 请求 commander 库
const program = require('commander')

// 从 package.json 文件中请求 version 字段的值,-v和--version是参数
program.version(require('../package.json').version, '-v, --version')


// 请求 lib/update.js
const updateChk = require('../lib/update')

// upgrade 检测更新
program
    .command('upgrade')
    .description("Check the mtt-cli version.")
    .action(() => {
      updateChk()
    });

// 请求 lib/init.js
const initProject = require('../lib/create')

// init 初始化项目
program
    .name('mtt-cli')
    .usage('<commands> [options]')
    .command('create <project_name>')
    .description('create a new project')
    .action(project => {
      initProject(project)
    })

// 解析命令行参数
program.parse(process.argv)

4、编写 /lib/create.js,主要的操作将会在这里完成

// 请求 fs-extra 库,用于文件操作
const fse = require('fs-extra');
// 请求 ora 库,用于初始化项目时等待动画
const ora = require('ora');
// 请求 chalk 库
const chalk = require('chalk');
// 请求 log-symbols 库
const symbols = require('log-symbols');
// 请求 inquirer 库,用于控制台交互
const inquirer = require('inquirer');

const path = require('path');

// 请求 download.js 文件,模板不在本地时执行该操作
const dlTemplate = require('./download');

// prompt文件
const promptProjectCover = require('./promptModules/projectCover');
const promptProjectSelect = require('./promptModules/projectSelect');

// 初始化项目
async function initProject(projectName) {
  try {
    const targetDir = path.join(process.cwd(), projectName);
    if (fse.existsSync(targetDir)) {
      // 项目重名时提醒用户
      inquirer.prompt(promptProjectCover).then((answers) => {
        if (answers.projectCover) {
          createProject(targetDir, true);
        } else {
          console.log(symbols.error, chalk.red(`项目名称已存在,请重新设置!`));
          process.exit();
        }
      }).catch((error) => {
        console.log(symbols.error, chalk.red(`error:${error}`));
        process.exit();
      });
    } else {
      createProject(targetDir);
    }
  } catch (err) {
    console.error(err);
    process.exit();
  }
}

// 开始创建项目
async function createProject(targetDir, removeDir) {
  // 执行控制台交互
  inquirer.prompt(promptProjectSelect).then(async (answers) => {
    // 获取选择的模板
    const projectSelect = answers.projectSelect;

    // Spinner 初始设置
    const initSpinner = ora(chalk.cyan('创建目录...'));
    // 开始执行等待动画
    initSpinner.start();

    // 移除已有项目
    if(removeDir){
      try {
        await fse.remove(targetDir)
      } catch (err) {
        // 如果出错,Spinner 就改变文字信息
        initSpinner.text = chalk.red(`移除原有目录失败: ${err}`);
        // 终止等待动画并显示 X 标志
        initSpinner.fail();
        // 退出进程
        process.exit();
      }
    }
    //判断目录是否存在,不存在则创建
    fse.ensureDir(targetDir);

    // 开始下载模板
    try {
      initSpinner.text = `下载模板...`;
      await dlTemplate(targetDir, projectSelect);
    }catch (e) {
      // 如果成功,Spinner 就改变文字信息
      initSpinner.text = chalk.red(e);
      // 终止等待动画并显示 ✔ 标志
      initSpinner.fail();
      // 退出进程
      process.exit();
    }
    // 如果成功,Spinner 就改变文字信息
    initSpinner.text = '初始化项目完成.';
    // 终止等待动画并显示 ✔ 标志
    initSpinner.succeed();

  }).catch((error) => {
    console.log(symbols.error, chalk.red(error));
  });
}

// 将上面的 initProject(projectName) 方法导出
module.exports = initProject;

5、编写 /lib/download.js,用于从指定仓库中下载文件模板

// 请求 download-git-repo 库,用于下载模板
const download = require('download-git-repo');
const path = require('path');

// 请求 mirror.js 文件
const dataMirror = require('./mirror');

async function dlTemplate(targetDir, projectSelect) {
  return new Promise(((resolve, reject) => {
    download(dataMirror[projectSelect], targetDir, {clone: true}, function (err) {
      if (err) {
        reject(`模板下载失败. ${err}`)
      } else {
        resolve(`模板下载完成`)
      }
    });
  }))
}

// 将上面的 dlTemplate() 方法导出
module.exports = dlTemplate;

6、编写 /lib/update.js,检测npm仓库中我们的脚手架插件是否有新的版本

// 引用 update-notifier 库,用于检查更新
const updateNotifier = require('update-notifier')
// 引用 chalk 库,用于控制台字符样式
const chalk = require('chalk')
// 引入 package.json 文件,用于 update-notifier 库读取相关信息
const pkg = require('../package.json')

// updateNotifier 是 update-notifier 的方法,其他方法可到 npmjs 查看
const notifier = updateNotifier({
  // 从 package.json 获取 name 和 version 进行查询
  pkg,
  // 设定检查更新周期,默认为 1000 * 60 * 60 * 24(1 天)
  // 这里设定为 1000 毫秒(1秒)
  updateCheckInterval: 1000,
})

function updateChk() {
  // 当检测到版本时,notifier.update 会返回 Object
  // 此时可以用 notifier.update.latest 获取最新版本号
  if (notifier.update) {
    console.log(`New version available: ${chalk.cyan(notifier.update.latest)}, it's recommended that you update before using.`)
    notifier.notify()
  } else {
    console.log('No new version is available.')
  }
}

// 将上面的 updateChk() 方法导出
module.exports = updateChk

7、编写 /lib/mirror.js,我的远程仓库地址,目前是固定写死的,这部分可以根据自身未来的项目需求,写成可以自动配置的方式

module.exports = {
  '0': 'direct:https://gitee.com/meme-project/m-template-vue2-mb.git',
  '1': 'direct:https://gitee.com/meme-project/m-template-vue2-pc.git',
  '2': 'direct:https://gitee.com/meme-project/m-template-vue3-mb.git',
  '3': 'direct:https://gitee.com/meme-project/m-template-vue3-pc.git',
  '4': 'direct:https://gitee.com/meme-project/m-template-wx.git',
};

8、编写/lib/promptModules/projectCover.js、/lib/promptModules/projectSelect.js,这个主要功能是编写inquirer终端命令行需要交互的命令

//projectSelect.js

module.exports = [{
  type: 'list',
  message: '请选择要创建的模板',
  name: 'projectSelect',
  default: 0,
  choices: [
    {value: 0, name: 'vue2 移动端模板'},
    {value: 1, name: 'vue2 PC端后台模板'},
    {value: 2, name: 'vue3 移动端模板'},
    {value: 3, name: 'vue3 PC端后台模板'},
    {value: 4, name: '微信小程序模板'}
  ]
}];
//projectCover.js

module.exports = [{
    type: 'confirm',
    message: '目录下存在相同名字的文件名称,是否覆盖?',
    name: 'projectCover',
    default: 'true'
}];

运行测试脚手架

1、以上把我要实现的整体的脚手架代码已经编写完成了,接下来我们测试下;首先通过npm link软连接的方式,在项目本地测试脚手架的功能,可以看到已经成功完成了我想要的功能

在这里插入图片描述

选择vue2 移动端模板进行下载

在这里插入图片描述
2、发布到npm仓库

通过 npm publish 命令,将当前脚手架发布到npm库

在这里插入图片描述

再次安装测试,得到一样的预期结果,完工,按照预期完成了自己想要的脚手架

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/510335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

fine tune openAI model ( 微调chatgpt)

了解如何为自己的应用程序定制模型。 介绍 微调可以从API提供的模型中获得更多信息&#xff1a; 比 prompt 设计更高质量的结果能够在超过 prompt 范围的示例上进行训练更短的 prompt 节省了token更低的延迟请求 微调包括以下步骤&#xff1a; 准备并上传训练数据训练一个…

【异常解决】vim编辑文件时提示 Found a swap file by the name “.start.sh.swp“的解决方案

vim编辑文件时提示 Found a swap file by the name ".start.sh.swp"的解决方案 一、问题描述二、原因说明三、解决方案3.1 方案1 删除即可3.2 方案2 禁止生成swp文件 一、问题描述 vim编辑文件时提示 Found a swap file by the name “.start.sh.swp”&#xff0c;如…

周杰伦、张韶涵巡演重磅回归,联诚发LED屏幕燃爆全场!

灯光点亮&#xff0c;音乐响起&#xff0c;所有粉丝随着节奏开始疯狂摇摆呐喊。2023年&#xff0c;随着演出市场全面复苏&#xff0c;各大活动演出随处可见&#xff0c;众多歌手也重新回归舞台&#xff0c;开启世界巡回演唱会活动&#xff0c;周杰伦、张韶涵等重磅明星纷纷进行…

6 接口、多态、断言、项目【Go语言教程】

6 接口、多态、断言、项目【Go语言教程】 1 接口 1.1 概念 Golang 中 多态特性主要是通过接口来体现的。 interface 类型可以定义一组方法&#xff0c;但是这些不需要实现。并且 interface 不能包含任何变量。到某个自定义类型(比如结构体 Phone)要使用的时候,在根据具体情况…

Jupyter Notebook为什么适合数据分析?

Jupyter Notebook 是一个 Web 应用程序&#xff0c;便于创建和共享文学化程序文档&#xff0c;支持实时代码、数学方程、可视化和 Markdown&#xff0c;其用途包括数据清理和转换、数值模拟、统计建模、机器学习等等。目前&#xff0c;数据挖掘领域中最热门的比赛 Kaggle 里的资…

数据结构与算法基础(王卓)(37):选择排序(简单选择、堆排序)

目录 简单选择排序 堆排序 堆的调整&#xff1a; 大根堆 小根堆 整个堆调整的完整工序如下&#xff1a; 根据按照操作对程序注解标注&#xff1a;&#xff08;看过了注解就知道程序他每一步是怎么操作的了&#xff09; 堆的建立 问题 简单选择排序 #include<iostre…

Feign踩坑源码分析--@FeignClient注入容器

一. EnableFeignClients 1.1.类介绍 从上面注释可以看出是扫描声明了FeignClient接口的类&#xff0c;还引入了 FeignClientsRegistrar类&#xff0c;从字面意思可以看出是进行了 FeignClient 客户端类的注册。 1.2.FeignClientsRegistrar 详解 最主要的一个方法&#xff1a;re…

喜报丨酷雷曼荣膺最佳创新品牌价值奖

2023年4月&#xff0c;“元力觉醒新浪VR 2022年度行业奖项”颁奖盛典成功举行&#xff0c;酷雷曼VR&#xff08;北京同创蓝天云科技有限公司&#xff09;荣获“最佳创新品牌价值奖”荣誉称号&#xff01; 本次大会由元宇宙产业的权威门户媒体新浪VR主办&#xff0c;中国民协元…

从入门到精通:网络爬虫开发实战总结

从入门到精通&#xff1a;网络爬虫开发总结 专栏&#xff1a;Python网络爬虫1.认识网络爬虫2.网络爬虫——HTML页面组成3.网络爬虫——Requests模块get请求与实战4.网络爬虫—Post请求(实战演示)5.网络爬虫——Xpath解析6.网络爬虫——BeautifulSoup详讲与实战7.网络爬虫—正则…

网络原理(五):IP 协议

目录 认识IP 地址 子网掩码 作用 动态分配IP 地址 NAT 机制 认识MAC地址 MAC地址如何工作 认识IP 地址 概念&#xff1a; IP地址&#xff08;Internet Protocol Address&#xff09;是指互联网协议地址&#xff0c;又译为网际协议地址。 作用&#xff1a; IP地址是I…

遗传算法(GA)

理论&#xff1a; 遗传算法是一种通过模拟生物进化的方式来寻找最优解的一类优化算法。这种算法主要依靠遗传、突变和自然选择的机制对问题求解进行高效的迭代搜索。 遗传算法的基本思想是将问题的解表示成一个个个体&#xff0c;然后根据适应度函数的定义来评估每个个体的适…

【数组排序算法】

目录 一、数组排序算法1、冒泡排序算法1.1、图形解释1.2、冒泡算法的脚本写法 二、直接选择排序1.1、动态图解1.2、直接选择排序算法的脚本编写 三、直接插入排序1.1、基本思想&#xff1a;1.2、动态图解1.3、直接插入排序的算法脚本编写 四、反向序列算法1.1、反向序列算法的脚…

linux:文件替换的三种方式sed、awk、perl

文章目录 背景sed语法问题1、加个空字符串2、下载gnu-sed awk语法举例 perl语法示例 总结 背景 linux 文件内容替换&#xff0c;网上看了下大致就这三种 sed、awk、perl&#xff0c;今天挨个使用一下看看怎么样 sed 语法 Linux sed 命令是利用脚本来处理文本文件。详细文档…

网络基础认知(上)

如今使用过计算机的人们都接触过网络&#xff0c;但是网络究竟是什么&#xff0c;计算机又是怎样通过网络来进行互相之间通信的&#xff0c;这还需要我们深入了解。 目录 网络发展 初识协议 什么是协议 为什么需要协议&#xff1f; 网络协议初识 协议分层 为什么网络协议要…

澳鹏与Reka AI强强联合,构建高质量的多模态LLM应用

近日&#xff0c;澳鹏Appen官宣与AI新兴公司Reka AI合作&#xff0c;以实现世界级数据服务与多模态语言模型的结合。 ChatGPT等创新应用的崛起让大型语言模型&#xff08;LLM&#xff09;实现了突飞猛进的发展。LLM可以助力企业提升运营效率&#xff0c;并为最终用户提供耳目一…

Java基础--->JVM(3)【垃圾回收】

文章目录 垃圾回收&#xff08;GC&#xff09;什么内存需要回收&#xff08;什么样的对象是垃圾&#xff09;&#xff1f;为什么要进行垃圾回收&#xff1f;内存溢出和内存泄露的区别&#xff0c;如何解决分区收集思想 Minor GC、Major GC、Full GC垃圾回收相关算法引用计数算法…

如何搭建产品操作手册

对于企业来说&#xff0c;拥有一份完备的产品操作手册无疑是至关重要的。操作手册不仅是新员工学习产品使用及维护的重要参考&#xff0c;也是产品售后服务与客户支持的必备文件。在手册编写上&#xff0c;应清晰明了地介绍产品的功能、配置及故障排除等信息&#xff0c;使用户…

统计学下的假设检验

由于本人才疏学浅&#xff0c;再加上时间仓促&#xff0c;难免有疏漏之处&#xff0c;恳请批评指正. 1,预备知识 数理统计&#xff1a;以概率论为基础&#xff0c;研究如何有效的去搜集、整理、分析带随机性影响的数据 总体与样本&#xff1a;研究对象的全体就称为总体 样本&a…

VTK交互-vtkBoxWidget2

VTK交互Widget widget包含两个重要的组成部分&#xff1a;Interaction和Representation. Interaction是一些名叫vtk*Widget的类&#xff08;比如vtkBoxWidget2&#xff09;。它包含了交互的所有选项和事件处理。 Representation是显示并与之交互的一类对象&#xff0c;以名叫v…

origin 拟合计算酶的Kcat Km 值

origin拟合计算Kcat Km值 横坐标为底物浓度&#xff0c;纵坐标为反应速率 全选X 与Y坐标数据&#xff0c;然后选择菜单栏Analysis: Fitting: Nonlinear Curve Fit&#xff1a;Open Dialog 在Setting&#xff1a;Function Selection页面内的Category选择Pharmacology, Functi…