欢迎来到我的博客

vuePress-theme-reco hbbaly    2021
欢迎来到我的博客

Choose mode

  • dark
  • auto
  • light
首页
时间轴
标签
分类
  • 前端
GitHub
author-avatar

hbbaly

31

Article

18

Tag

首页
时间轴
标签
分类
  • 前端
GitHub
  • 【vue】搭建自己的脚手架

    • 简述
      • 构建准备
        • Inquirer.js
        • commander.js
        • 其他依赖
      • 项目文件结构搭建
        • tem文件
          • tem-add
            • tem-list
              • tem-delete
                • tem-init
                  • template.json
                    • package.json
                      • 上传到npm
                        • 验证

                    【vue】搭建自己的脚手架

                    vuePress-theme-reco hbbaly    2021

                    【vue】搭建自己的脚手架


                    hbbaly 2020-03-02 Vue脚手架

                    # 简述

                    作为前端,相信大家都用过vue-cli或者create-react-app,输入命令,自动完成,接下来就会介绍怎么完成自己的脚手架。

                    # 构建准备

                    # Inquirer.js

                    地址

                    用于用户与命令行交互,做一个脚手架或者在某些时候要与用户进行交互, 需要用到它。

                    # 参数

                    • type : String 表示提问的类型,默认input,包括:input, confirm, list, rawlist, expand, checkbox, password, editor

                    • name : String 存储当前问题回答的变量

                    • message : String问题的描述

                    • default : String默认值

                    • choices : Array|Function列表选项

                    • validate : Function对用户的回答进行校验

                    • filter : Function对用户的回答进行过滤,返回处理后的值

                    • transformer : Function对用户回答的显示效果进行处理(如:修改回答的字体或背景颜色),不会影响最终的答案的内容

                    • when : Function, Boolean根据前面问题的回答,判断当前问题是否需要被回答

                    • pageSize : Number修改某些type类型下的渲染行数

                    • prefix : String修改message默认前缀

                    • suffix : String修改message默认后缀

                    • askAnswered : Boolean如果答案已经存在,则强制提示该问题

                    • loop : Boolean 启用列表循环

                    # 安装

                    npm install inquirer
                    

                    # 使用

                    const inquirer = require('inquirer');
                    const list = [
                      {
                        type: 'input',
                        message: '请输入手机号:',
                        name: 'phone',
                        validate: function(val) {
                            if(val.match(/\d{11}/g)) { // 校验位数
                                return val;
                            }
                            return "请输入11位数字";
                        }
                      },
                      {
                        type: "confirm",
                        message: "是否使用监听?",
                        name: "watch",
                        prefix: "前缀"
                      },
                      {
                        type: 'list',
                        message: '请选择一种水果:',
                        name: 'fruit',
                        choices: [
                            "Apple",
                            "Pear",
                            "Banana"
                        ],
                        filter: function (val) { // 使用filter将回答变为小写
                          return val.toLowerCase();
                        }
                      }
                    ]
                    inquirer
                      .prompt(list)
                      .then(answers => {
                        // Use user feedback for... whatever!!
                      })
                      .catch(error => {
                        if(error.isTtyError) {
                          // Prompt couldn't be rendered in the current environment
                        } else {
                          // Something else when wrong
                        }
                      });
                    

                    # commander.js

                    地址

                    完整的 node.js 命令行解决方案

                    # 安装

                    npm install commander
                    

                    # 版本号

                    const program = require('commander');
                    program
                    	.version('1.0.0')
                      .parse(process.argv);
                    

                    或者自定义flag

                    const program = require('commander');
                    program
                    	.version('1.0.0', '-v, --version')
                    	.parse(process.argv)
                    

                    # option方法

                    # 参数说明
                    • 自定义flag<必须>

                      • 一长一短的flag,中间可以逗号、竖线或空格隔开

                      • flag后面可以跟参数,<>定义必需参数,[]定义可选参数

                    • 选项的描述<可省略>:在使用-h或者--help时会显示

                    • 选项的默认值<可省略>

                    # 定义多个选项
                    const program = require('commander');
                    program
                    	.version('1.0.0')
                    	.option('-a, --add', 'add Something')
                    	.option('-u, --update', 'update Something')
                    	.option('-r, --remove', 'remove Something')
                      .parse(process.argv);
                    

                    短flag使用-,长flag使用--

                    # command

                    # 参数
                    • 自定义命令名称
                      • 名称<必须>
                      • 命令参数<可选>:
                        • <>和[]定义参数
                        • 命令的最后一个参数可以是可变的,需要在数组后面加入 ...,在命令后面传入的参数会被传入到 action 的回调函数以及 program.args 数组中
                    • 命令描述<可省略>
                    • 配置选项<可省略>:可配置noHelp、isDefault等
                    const program = require('commander');
                    program
                    	.version('1.0.0')
                    	.command('my-cli <path>')
                    	.option('-a, --add <fileName>', 'add a file')
                    	.option('-u, --update <fileName>', 'update a file')
                    	.option('-r, --remove <fileName>', 'remove a file')
                    	.action(function(path, sh) {
                        // action 定义命令的回调函数
                    		console.log(path)
                    		console.log(sh.add)
                    	})
                     
                    program.parse(process.argv);
                    

                    # description

                    const program = require('commander');
                    program
                    	.version('1.0.0')
                    	.description('It is my cli')
                      .parse(process.argv);
                    

                    # parse 方法

                    用于解析process.argv,设置options以及触发commands

                    # 其他依赖

                    chalk

                    download-git-repo

                    ora

                    也会用到这些依赖

                    # 项目文件结构搭建

                    1.建立一个文件夹 2.在建好的文件下下面执行npm init, 在package.json中添加

                    "chalk": "^1.1.3",
                    "commander": "^2.9.0",
                    "download-git-repo": "^1.1.0",
                    "inquirer": "^6.2.0",
                    "ora": "^3.0.0",
                    "request": "^2.88.0"
                    

                    npm i安装依赖

                    1. 建立bin文件夹,在bin文件夹里面分别建tem,tem-init,tem-add,tem-list,tem-delete
                    • bin
                      • tem
                      • tem-add
                      • tem-list
                      • tem-delete
                      • tem-init

                    # tem文件

                    #!/usr/bin/env node  // 表示执行程序是node
                    
                    const program = require('commander')
                    // 定义当前版本
                    // 定义使用方法
                    // 定义四个指令
                    program
                      .version(require('../package').version)
                      .usage('<command> [options]')
                      .command('add', 'add a new template')
                      .command('delete', 'delete a template')
                      .command('list', 'list all the templates')
                      .command('init', 'generate a new project from a template')
                      
                    // 解析命令行参数
                    program.parse(process.argv)
                    

                    tem定义了四个命令

                    # tem-add

                    添加模版

                    #!/usr/bin/env node
                    
                    // 交互式命令行
                    const inquirer = require('inquirer')
                    // 修改控制台字符串的样式
                    const chalk = require('chalk')
                    // node 内置文件模块
                    const fs = require('fs')
                    // 读取根目录下的 template.json
                    const tplObj = require(`${__dirname}/../template.json`)
                    
                    // 自定义交互式命令行的问题及简单的校验
                    let question = [
                      {
                        name: "name",
                        type: 'input',
                        message: "请输入模板名称",
                        validate (val) {
                          if (val === '') {
                            return 'Name is required!'
                          } else if (tplObj[val]) {
                            return 'Template has already existed!'
                          } else {
                            return true
                          }
                        }
                      },
                      {
                        name: "url",
                        type: 'input',
                        message: "请输入模板地址",
                        validate (val) {
                          if (val === '') return 'The url is required!'
                          return true
                        }
                      }
                    ]
                    inquirer
                      .prompt(question).then(answers => {
                        // answers 就是用户输入的内容,是个对象
                        let { name, url } = answers;
                        // 过滤 unicode 字符
                        tplObj[name] = url.replace(/[\u0000-\u0019]/g, '')
                        // 把模板信息写入 template.json 文件中
                        fs.writeFile(`${__dirname}/../template.json`, JSON.stringify(tplObj), 'utf-8', err => {
                          if (err) console.log(err)
                          console.log('\n')
                          console.log(chalk.green('Added successfully!\n'))
                          console.log(chalk.grey('The latest template list is: \n'))
                          console.log(tplObj)
                          console.log('\n')
                        })
                      })
                    

                    # tem-list

                    这个比较简单,获取模版

                    #!/usr/bin/env node
                    const tplObj = require(`${__dirname}/../template`)
                    console.log(tplObj)
                    

                    # tem-delete

                    删除某个模版

                    #!/usr/bin/env node
                    
                    const inquirer = require('inquirer')
                    const chalk = require('chalk')
                    const fs = require('fs')
                    const tplObj = require(`${__dirname}/../template`)
                    
                    let question = [
                      {
                        name: "name",
                        message: "请输入要删除的模板名称",
                        validate (val) {
                          if (val === '') {
                            return 'Name is required!'
                          } else if (!tplObj[val]) {
                            return 'Template does not exist!'
                          } else  {
                            return true
                          }
                        }
                      }
                    ]
                    inquirer
                      .prompt(question).then(answers => {
                        let { name } = answers;
                        delete tplObj[name]
                        // 更新 template.json 文件
                        fs.writeFile(`${__dirname}/../template.json`, JSON.stringify(tplObj), 'utf-8', err => {
                          if (err) console.log(err)
                          console.log('\n')
                          console.log(chalk.green('Deleted successfully!\n'))
                          console.log(chalk.grey('The latest template list is: \n'))
                          console.log(tplObj)
                          console.log('\n')
                        })
                      })
                    

                    # tem-init

                    初始化项目

                    #!/usr/bin/env node
                    const program = require('commander')
                    const chalk = require('chalk')
                    const ora = require('ora')
                    const download = require('download-git-repo')
                    const tplObj = require(`${__dirname}/../template`)
                    program
                      .usage('<template-name> [project-name]')
                    program.parse(process.argv)
                    
                    // 当没有输入参数的时候给个提示
                    if (program.args.length < 1) return program.help()
                    
                    // 好比 vue init webpack project-name 的命令一样,第一个参数是 webpack,第二个参数是 project-name
                    let templateName = program.args[0]
                    let projectName = program.args[1]
                    // 小小校验一下参数
                    if (!tplObj[templateName]) {
                      console.log(chalk.red('\n Template does not exit! \n '))
                      return
                    }
                    if (!projectName) {
                      console.log(chalk.red('\n Project should not be empty! \n '))
                      return
                    }
                    
                    url = tplObj[templateName]
                    console.log(chalk.white('\n Start generating... \n'))
                    // 出现加载图标
                    
                    const spinner = ora("Downloading...");
                    spinner.start();
                    
                    // 执行下载方法并传入参数
                    download (
                      url,
                      projectName,
                      err => {
                        if (err) {
                          spinner.fail();
                          console.log(chalk.red(`Generation failed. ${err}`))
                          return
                        }
                        // 结束加载图标
                        spinner.succeed();
                        console.log(chalk.green('\n Generation completed!'))
                        console.log('\n To get started')
                        console.log(`\n    cd ${projectName} \n`)
                      }
                    )
                    

                    # template.json

                    {"vue-spa-template": "hbbaly/vue-spa-template"}
                    

                    模版地址就是download-git-repo要读取的git地址,这个地址 不需要完整的, 只需要域名后面的就行,当然也可以写完整的地址,如果是公司搭建的git服务就不能简写了

                    # package.json

                    {
                      "name": "vue-cli-tem",
                      "version": "1.0.1",
                      "description": "这个是一个简单的脚手架",
                      "preferGlobal": true,
                      "bin": {
                        "tem": "./bin/tem",
                        "tem-add": "bin/tem-add",
                        "tem-delete": "bin/tem-delete",
                        "tem-list": "bin/tem-list",
                        "tem-init": "bin/tem-init"
                     },
                      "dependencies": {
                        "chalk": "^1.1.3",
                        "commander": "^2.9.0",
                        "download-git-repo": "^1.1.0",
                        "inquirer": "^6.2.0",
                        "ora": "^3.0.0",
                        "request": "^2.88.0"
                      },
                      "scripts": {
                        "test": "echo \"Error: no test specified\" && exit 1"
                      },
                      "keywords": [
                        "cli",
                        "vue",
                        "hbbaly"
                      ],
                      "author": "hbbaly",
                      "license": "MIT"
                    }
                    

                    # 上传到npm

                    在根目录建立.npmignore,这个和.gitignore一样,我们在.npmignore添加 /node_modules即可,上传到npm时候,会忽略这些文件。

                    1. 假设已经有npm账号

                    2. 执行npm login

                    3. 登陆之后,npm publish

                    # 验证

                    npm i vue-cli-item -g
                    

                    依次执行这几个命令