前端工程化:脚手架开发

1、脚手架

1. 基础概念

  • bin文件夹:存储可执行文件
  • lib文件夹:存储程序运行所需的共同文件(共享相同代码的文件)
  • --options -o :都是算到options里
  • 全局安装@vue/cli时发生了什么?
    1. 从npm下载@vue/cli的包,安装到node下的lib文件夹中
    2. 解析package.jsonbin属性
    3. 如果有bin属性,就会在bin文件夹生成软连接,指向的是bin属性对应的值
  • package.jsonmain属性,如果当前项目作为包,main指向入口文件

2. 脚手架开发流程

  • 脚手架创建

    • 创建项目test-build
    • 初始化项目npm init -y
    • package.json中添加bin属性:"bin": {"imooc-build": "bin/imooc-build.js"},
    • 创建脚手架入口文件:bin/imooc-build.js
    • 入口文件首行添加:#!/usr/bin/env node
  • 脚手架开发

    • 分包:将复杂的系统拆分成若干个模块
    • 参数解析
  • 调试脚手架

    1. 链接本地库文件:
      • cd your-cli-dir(脚手架目录)
        • npm link:创建软链接
      • cd your-lib-dir(项目目录)
        • npm link your-lib:使用上一步创建的软链接(安装到node_modules中)
        • 添加"dependencies": {包名:版本号}
    2. 取消本地库文件:
      • cd your-lib-dir(项目目录)
        • npm unlink your-cli:删除项目node_modules中的包
      • cd your-cli-dir
        • npm unlink your-cli:删除软连接
      • 删除全局C:\Users\mashize\AppData\Roaming\npm下的记录
        • npm remove -g 脚手架名称A:删除本地的包
  • 脚手架发布

    • npm publish
  • 注意:windows下没有#!/usr/bin/env node,这个是Linux下的,可以通过bash命令行操作,cmd和shell都会报错windows script host错误

  • 开发脚手架框架

    • yargs
    • commander
    • oclif

3. Commander.js基础使用

// const { program } = require('commander') // 单例模式
const {
    
     Command } = require('commander');
const program = new Command(); // 多例模式,单独创建一个program(程序)实例

// // 单例模式--demo
// program
//   .option('--first') // 设置option,option只有boolean和string两个格式类型,只有一个参数时就是boolean,两个参数就是string
//   .option('-s, --separator <char>')

// program.parse()  // 解析程序

// const options = program.opts() // 获取options
// const limit = options.first ? 1 : undefined;
// console.log(program.args[0].split(options.separator, limit)); // program.args[0]获取所有的参数

//多例模式
program
  .name('imooc-build') // 脚手架名称
  .description('CLI description')// 脚手架描述
  .version('0.0.1'); // 脚手架版本

program
  .option('-d, --debug', 'debugging');  // 全局的option

// 具体的指令(方式一)
program
  .command('split') // 脚手架具体的命令(指令)
  .description('指令的描述') // 指令的描述
  .argument('<arguments>', '紧跟指令后面的参数') // 指令后面的参数
  .option('--first', 'option的描述') // 当前指令下的option
  .option('-s, --separator <char>', 'option的描述', ',') // option三个参数,参数1是option,参数2是描述,参数3是默认值
  .option('-a, --add [char]', 'option的描述', ',') // <char>和[char]区别,<>是必填,[]是选填
  .action((args, options)=>{
    
     // 当输入当前command命令时,执行的回调函数
    console.log(program.commands[0].optsWithGlobals()); // 当前指令下,以及全局的options
    console.log('args, options:', args, options) // args获取的是argument,options获取的是option,都是当前指令下的
  })

// addCommand 注册子命令
const service = new Command('service'); // 创建第二个指定姓名为'service'脚手架

service // 跟主命令的选项一样。注意不能连写command注册多个命令,这个是跟主命令的区别
  .command('start [port]')
  .description('子指令的描述') // 子指令的描述
  .action((port)=>{
    
    
	console.log('开启执行service start 命令', port)
  })

// 在imooc-build下注册了service的子命令(子脚手架)
program.addCommand(service);

// 使用:
// imooc-build service start 8080 
// 是执行imooc-build脚手架下的service子脚手架的start命令

program.parse() 

// opts:获取当前实例的options,比如全局program获取的就是全局options,subcommand获取的就是局部options
// optsWithGlobals:获取全部options,比如全局program获取的就是全局options,subcommand获取的是【全局+局部】options
const options = program.opts(); //获取全局的options
const globalOptions = program.optsWithGlobals(); //也可以获取全局的options

4. 命令行UI显示

  • 命令行渲染标准
    • ANSI escape code
  • 脚手架常用UI库
    • chalk:颜色渲染

      • 基本用法
      • chalk-cli使用技巧:快速生成chalk
      import chalk from "chalk";
      
      console.log(chalk.red('hello imooc'));
      console.log(chalk.red('hello imooc')+ "!"+ chalk.yellow('hello imooc!'));
      console.log(chalk.red.bgYellow.bold('hello imooc'));
      console.log(chalk.red('hello','imooc'));
      console.log(chalk.red('hello',chalk.underline('imooc!')));
      
      console.log(chalk.rgb(255,255,0).underline('hello imooc!'));
      console.log(chalk.hex('#ff0000').bold('hello imooc!'));
      console.log(chalk.hex('#ff0000')('hello', 'imooc!'));
      
      const error = (...text)=> console.log(chalk.bold.hex('#ff0000')(text))
      const warning = (...text)=> console.log(chalk.hex('#ffa500')(text))
      error('Error!')
      warning('Warning!')
      
      const customChalk = new Chalk({
              
              level:3}) // 0-3,0不支持所有颜色,1-3颜色支持越来越多
      console.log(customChalk.blue('hello'));
      
    • ora:loading状态加载

      • 基本用法
      • cli-spinners
      // node --experimental-modules .\src\ora.mjs
      import ora from 'ora'
      
      const spinner = ora().start()
      
      let percent = 0
      
      spinner.color = 'red' // loading颜色
      spinner.text = 'Loading' + percent + '%' // 显示的文本
      spinner.prefixText = "Downloading chalk" // 显示文本的前缀信息
      
      let interval = setInterval(() => {
              
              
        percent += 10
        spinner.text = 'Loading' + percent + '%'
        if (percent === 100) {
              
              
          spinner.stop() // 结束loading
          spinner.succeed("Download finish!") // 回调,显示状态
          clearInterval(interval)
        }
      }, 500);
      

5. 命令行交互

  • 相关技术点
    • 键盘输入监听(readline)
    • 命令行窗口尺寸运算
    • 清屏
    • 光标移动
    • 输出流静默
    • 输入输出流(stream)
    • 事件库(events)
    • ansi escaped code
  • 重点内容
    • readline库
    • inquirer库
// node --experimental-modules .\src\inquirer.js
import inquirer from 'inquirer';

inquirer
  .prompt([
    /* Pass your questions in here */
    {
    
    
      type:'input',
      name:'yourName', // 返回信息的key
      message:'请输入你的姓名',  // 交互信息的显示的内筒
      default:'noname', // 默认信息
      validate: function (v) {
    
     // 校验参数,只有结果为true时才会继续执行
        return typeof v === 'string';
      },
      transformer: function (v) {
    
     // 类似于提示信息,不会影响最终的answers
        return v + '(input your name)'
      },
      filter: function (v) {
    
     // 过滤,会影响最终的answers
        return 'name[' + v + ']'
      }
    },
    {
    
    
      type:'number',
      name:'num',
      message:'请输入数字',
    },
    {
    
    
      type:'confirm', // Boolean类型
      name:'choice',
      message:'your choice',
      default: false,
    },
    {
    
    
      type:'list', // Boolean类型
      name:'list',
      message:'your list',
      default: 0, // 对应的是list 的顺序(索引,从0开始)
      choices: [
        {
    
     value:1, name:'zs'},
        {
    
     value:2, name:'ls'},
      ]
    },
    {
    
    
      type:'checkbox', // Boolean类型
      name:'checkbox',
      message:'your checkbox',
      choices: [
        {
    
     value:1, name:'zs'},
        {
    
     value:2, name:'ls'},
      ]
    }
  ])
  .then((answers) => {
    
    
    // Use user feedback for... whatever!!
    console.log('获取输入结果answers:', answers)
  })
  .catch((error) => {
    
    
    if (error.isTtyError) {
    
    
      // Prompt couldn't be rendered in the current environment
    } else {
    
    
      // Something else went wrong
    }
  });

6. 多package管理

  • lerna
  • workspaces

6.1 lerna使用

  • npx lerna -v:查看lerna版本
  • npx lerna init:项目初始化
  • npx lerna create <name>:生成一个项目
  • npx lerna create --help:查看命令的帮助信息
  • lerna add <package>[@version] [--dev] [--exact] [--peer]:安装依赖
  • npx lerna link :生成软连接
  • npx lerna bootstrap:所有依赖的安装和更新
  • npx lerna publish:发布
    • package.json添加 publishConfig 属性
      "publishConfig": {
              
              
        "access": "public"
      }
      
  • 其他常用命令:https://lerna.js.org/docs/api-reference/commands

7. 通用脚手架封装

7.1 主要实现功能

(1)创建初始化项目+集成通用代码(例如:登录)
(2)快速生成base页面并配置路由
(3)集成通用代码(常用utils封装、el二次封装组件,常用页面)
(4)常用业务层功能页面的集成

8. 安装和卸载

安装:npm install @msz-cli/cli -g
卸载:npm uninstall @msz-cli/cli -g
相关命令:
创建项目模板:msz create -t project projectname
创建模块模板:msz create -t module modulename
获取物料(utils/components/pages):msz get utils/components/pages

猜你喜欢

转载自blog.csdn.net/Litt_White/article/details/130546596
今日推荐