• 实现一个自定义的vue脚手架


    开发背景

    博客很久没有更新了, 今天更新一个好玩的,等我将vue3的东西彻底搞明白我会更新一个vue3的系列,到时候会更新稍微勤一点,在使用vuecli的时候发现他的脚手架很有意思,用了几年了,但是一直没有好好研究过这个东西是怎么实现的,所以今天就好好的记录一下这玩意怎么实现的,记录的过程中有什么注意事项我也会标明的,当然我会尽可能的写的明白一些,因为这个东西有一些地方不那么好理解,当一个东西不太好理解的时候文章书写的顺序就显的很重要,因为开始的时候如果你就很迷茫,那么你看下去的看动力也就没有了,所以我也是尽可能的从最简单的开始写,相信只要按照文章一点点的来,应该都是没问题的,我文章的顺序就是我自己开发这个功能的顺序,如果有大神觉得我哪里写的有什么问题的话,也可以下方留言,我看到了都会补充说明并表示感谢。文章看起来觉得比较费劲的可以直接移步我的github,源码奉上 源码

    先看效果:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述

    实现过程中可能存在的问题
    • 如何获取用户的输入
    • 如果根据用户的输入进行相应的操作
    • 如何自定一个packag.json文件
    • 如何发布到npm
    初始化一个package.json
    npm init 或者 npm init -y
    
    • 1
    初始化一个测试文件
    #!/usr/bin/env node
    console.log("function is success")
    
    • 1
    • 2

    特殊说明: #!/usr/bin/env node 这里是必须要加的,如果你实在不理解,那么你可以先这样写,另外就是这行代码必须是第一行,他的前面不可以有任何代码 当然这里也可以解释一下,你可以简单的理解为增加这一行是为了指定用node执行脚本文件。如果想深入理解这个东西,可以看一下大神的介绍Shebang 希望可以有点帮助

    运行该文件
    node index.js
    
    • 1
    将node index.js 替换为自定义命令
    比如:wlm

    我们的目的是,当我们输入 wlm的时候 执行的是node index.js这句话即可

    配置package.json
    {
      "name": "wlm-cli",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "",
      "license": "ISC",
      "bin": {
        "wlm":"index.mjs"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    bin命令 说明

    “bin”: {
    “wlm”:“index.js”
    }

    这里的bin就是我们需要使用的终端命令,后面的文件就是我们指定的mjs文件,当然这个文件你可以自己封装,只要是一个入口文件就可以,vue-cli源码这里指向的就是一个bin文件

    发布该命令
    npm link  // 将bin脚本下的命令进行发布到本地,本地就可以使用bin脚本中的命令
    
    • 1
    效果
    wlm
    function is success
    
    • 1
    • 2
    注意事项

    需要被执行的文件需要使用#!/usr/bin/env node 进行声明 否则是不生效的,该命令是映射到全局的,所以你在任何地方都是可以直接进行使用该命令的,就和你使用vue-cli是一样的效果

    移除link
    npm unlink  // 注意这里必须在项目文件中执行,不可以全局执行 
    
    • 1

    实现过程

    脚本命令参数设计
    wlm --help 查看使用帮助
    wlm -V |--version 查看工具版本号
    wlm list  列出所有可用模板
    wlm inti   基于制定模板进行项目初始化 <> 代表的是必填项
    
    • 1
    • 2
    • 3
    • 4

    下面的操作都是服务于上面👆这段参数设计,首先是我们已经将wlm的命令执行到本地,npm link已经完成了我们需要的第一步,后面就是这么在我们输入wlm之后的命令进行操作,首先我们需要将wlm后面输入的操作捕捉到,其实node提供给我们了原始的操作方式,比如下面

    log(process.argv) // 我全局声明了 const log = console.log;  所以这里直接使用了log
    输入:wlm iii
    leimingwei@leimingweideMBP vue-cli-xa % wlm iii
    [
      '/Users/leimingwei/.nvm/versions/node/v16.13.2/bin/node',
      '/Users/leimingwei/.nvm/versions/node/v16.13.2/bin/xa',
      'iii'
    ]
    error: unknown command 'iii'
    (Did you mean init?)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可以看到当我们输入命令之后,node自带的process.argv 是可以将我们输入的参数捕捉到的,但是这种写法我们是很难将用户的所有操作进行处理的,所以我们需要一个工具类来代替我们做这件事,那么这个工具就是commander

    执行用户输入的命令行操作
    yarn add commander // 安装commander工具
    import { Command } from 'commander'; //进行命令行的操作
    const program = new Command();
    
    • 1
    • 2
    • 3
    使用
    program
      .command('use') // 此处就是定义用户的输入
      .description('如何使用该cli') // 当用户输入-h 的时候提示的描述信息
      // 接受到用户输入的命令之后进行的操作
      .action(()=>{
        log(logSymbols.info, chalk.yellow('第一步:运行 wlm list'))
        log(logSymbols.info, chalk.yellow('第二步:运行 wlm init 模板名称 自定义名称'))
        log(logSymbols.info, chalk.yellow('第三步:按照步骤初始化模板即可'))
      })
      program.parse();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    效果展示:
    leimingwei@leimingweideMBP lm-cli % wlm use 
    ℹ 第一步:运行 wlm list
    ℹ 第二步:运行 wlm init 模板名称 自定义名称
    ℹ 第三步:按照步骤初始化模板即可
    
    • 1
    • 2
    • 3
    • 4

    有了上述工具之后我们可以做的事情就比较多了,首先可以自定义指令,之后可以根据自定义指令进行action的操作,action中有回调参数,可以获取到当前用户的操作结果,具体的使用说明可以看🔝上方提供的github链接中的说明文档,这里不做赘述

    说明

    该功能插件提供很多命令操作和很多选项操作,可以自己进行定义一些功能操作,比如获取到用户输入的命令进行下载或者执行一些动作都是可以的

    下载用户想要的文件

    用户的命令我们已经获取到了,这个时候我们需要进行根据用户的要求下载对应的仓库才可以,这里我们没办法直接使用git clone 下载,所以我们需要使用第三方的下载工具,download-git-repo 该工具可以帮助我们直接下载我们提供好的git下载地址

    git-repo基本使用
    yarn add downlaod-git-repo
    import download from 'download-git-repo'; 
    
    • 1
    • 2
    const downloadUrl  = '地址' 
    // PN || TN 是我的入参 你们可以直接写死自己需要的项目名字
    download(downloadUrl, PN || TN, { clone: true }, (err) => {
      const spinner = ora('模板获取中...').start(); // ora后面会说到 一个加载动画
            if (err) {
              spinner.color = 'red';
              spinner.text = `模板获取失败,请重新操作,失败原因:${err}`;
              spinner.fail()
              return
            }
            spinner.color = 'green';
            spinner.text = '模板下载成功';
            spinner.succeed()
          })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    限制用户输入操作和选择的脚本

    当我们可以知道了用户输入的命令了 ,那么之后我们要做的就是这么可以让用户有选择和询问的效果,比如:是否安装路由?是否安装pinia等等操作,还有一些是给用户选择,比如你是安装vue2还是vue3等,这个功能需要我们使用另一个工具进行实现,inquirer工具

    inquirer的基本使用
     inquirer
            .prompt([
              {
                type: 'input',
                name: 'name',
                message: "请输入项目名称"
              }, {
                type: 'input',
                name: 'description',
                message: "请输入项目简介"
              }, {
                type: 'input',
                name: 'author',
                message: "请输入作者"
              }
            ])
            .then((answers) => {
              log.log(answers)
            })
            .catch((error) => {
              if (error.isTtyError) {
                log(logSymbols.error, chalk.red(error)) // 这里的logSymbols 和 chalk 后面会说到 
              } else {
                log(logSymbols.error, chalk.yellow(error))
       }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    重写package.json文件

    当我们拿到了用户的输入,也将模板下载好了,也已经让用户自己输入了自定义的内容,那么下一步就是怎么将用户输入好的东西进行重新写入到我们下载好的项目中,当然不需要我们自己手写,需要我们引入第三方的工具handlebars 和 node内置的fs文件操作模块

    handlebars的基本使用
    yarn add handlebars
    import handlebars from 'handlebars' //模板引擎 
    import fs from 'fs' //node 内置模块 不需要单独引入
    
    • 1
    • 2
    • 3
    重写json
        const packagePath = `${PN}/package.json` //PN 文件名字
        const packageContent = fs.readFileSync(packagePath, 'utf8')
        const packageFinalValue = handlebars.compile(packageContent)(PC)
        log(chalk.green(packageFinalValue))
        fs.writeFileSync(packagePath, packageFinalValue)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    json配置

    当然只是重写还是不够的,需要我们的package.json进行配置,

    {
      "name": "{{name}}",
      "author": "{{author}}",
      "description": "{{description}}",
      "version": "1.0.0",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "license": "ISC",
      "bin": {
        "wlm": "index.js"
      },
      "{{dependencies}}": {
        "commander": "^9.4.1",
        "download-git-repo": "^3.0.2",
        "handlebars": "^4.7.7",
        "inquirer": "^9.1.4",
        "router" : "{{router}}",
        "pinia" : "{{pinia}}"
      },
      "{{devDependencies}}": {
    
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    我们需要进行重写的地方需要使用胡须模板进行变量接收参数

    美化操作

    上面提到的ora\chalk\logSymbols都是用来美化用户操作的,这里可以简单的理解为操作的界面更加舒适!

    import ora from 'ora' //添加loading效果
    import chalk from 'chalk'; // 提示文字
    import logSymbols from 'log-symbols'; //提示符号
    
    • 1
    • 2
    • 3
    具体使用

    具体美化怎么使用的这里不做太多的介绍了,各自的官网已经写的很明白了,开头我已经将源码地址提供出来, 觉得我的文档写的太乱的可以直接用我写的源码也可以,因为一段时间没有写了,所以写的有点乱,所以这次我也是破天荒的直接提供所有的源码给你们,目的是不挨骂!

    如何发布

    发布npm

    源码

  • 相关阅读:
    php 常用的接口和函数
    Mysql行级锁
    Python回归预测建模实战-线性回归预测房价
    【前端5*】表格-表单1(弹窗在父组件)父子组件调用 vue element-ui
    一个人 三个月 干了二十万
    用C++实现一个日期类
    svelte组件:svelte3自定义桌面PC端对话框组件svelte-layer
    动态代理jdk和cglib
    机器学习评估指标(Metrics)
    systemverilog 中的 `define ---带参数的宏函数--macro function
  • 原文地址:https://blog.csdn.net/qq_41485414/article/details/128013515