4-1 脚手架命令注册 & 高性能脚手架架构设计

1 一周导读

1.1 标题

  • 基于 Commander 完成脚手架命令注册和命令执行过程开发

1.2 将收获什么

  • 如何设计高性能脚手架
  • Node 多进程开发
  • javascript 面向对象的实战技巧

1.3 主要内容

  • 图解高性能脚手架架构设计方法
  • 封装通用的 PackageCommand
  • 基于缓存 + Node 多进程实现动态命令加载和执行
  • 将业务逻辑和脚手架框架彻底解耦

1.4 附赠内容

  • Node 多进程开发进阶 -- child_process 源码分析
    • 深入 Node 源码看清 spawn/exec/execFile/fork 的本质区别,彻底搞懂 Node 多进程原理

1.5 关键词

  • 高性能/可扩展的脚手架 - 利用缓存提升脚手架性能并解耦业务逻辑
  • 面向对象 - 利用 Class 完成 javascript 面向对象编程
  • Node 多进程 - 深入 Node 多进程原理

1.6 学习方法

  • 学以致用: 将前两周中学到的知识进行实际应用(commander/Lerna 命令执行原理)
  • 知识储备: 面向对象、ES6 新特性 Classshell 脚本(macOS)bat 批处理文件等概念需要预先储备
  • 充分实践: Node 多进程是一门较为复杂的技术,需要同学们充分实践和思考

1.7 注意事项

  • Node 多进程高能预警

2 imooc-cli 脚手架命令注册

2.1 imooc-cli脚手架初始化

const commander = require('commander')
const program = new commander.Command()
function registerCommand() {
  program
    .name(Object.keys(pkg.bin)[0])
    .usage('<command> [options]')
    .version(pkg.version)

  program.parse(process.argv)
}

2.2 全局参数注册

1. 实现debug模式

function registerCommand() {
  program
    .name(Object.keys(pkg.bin)[0])
    .usage('<command> [options]')
    .version(pkg.version)
    .option('-d, --debug', '是否开启调试模式', false)

  program.on('option:debug', function() {
    process.env.LOG_LEVEL = 'verbose'
    log.level = process.env.LOG_LEVEL
    log.verbose('test')
  })

  program.parse(process.argv)
}

2. 对未知命令进行监听

function registerCommand() {
  program
    .name(Object.keys(pkg.bin)[0])
    .usage('<command> [options]')
    .version(pkg.version)

  // 对未知命令的监听
  program.on('command:*', function(obj) {
    const availableCommands = program.commands.map(cmd => cmd.name())
    console.log(colors.red('未知的命令:' + obj[0]));
    availableCommands.length 
    && console.log(colors.red('可用命令:' + availableCommands.join(', ')));
  })

  program.parse(process.argv)
}

3. 自定义帮助文档

function registerCommand() {
  program
    .name(Object.keys(pkg.bin)[0])
    .usage('<command> [options]')
    .version(pkg.version)

  if(process.argv.length < 3) {
    program.outputHelp()
    console.log();
  } else {
    program.parse(process.argv)
  }
}
未输入命令时也要打印帮助文档
function registerCommand() {
  program
    .name(Object.keys(pkg.bin)[0])
    .usage('<command> [options]')
    .version(pkg.version)

  program.parse(process.argv)
  if(program.args && program.args.length < 1) {
    program.outputHelp()
  }
}

2.3 imooc-cli脚手架命令注册

1. 简易版

function registerCommand() {
  program
    .command('init [projectName]')
    .option('-f, --force', '是否强制初始化项目')
    .action((projectName, cmdObj) => {
      console.log('init', projectName, cmdObj.force)
    })

  program.parse(process.argv)
  if(program.args && program.args.length < 1) {
    program.outputHelp()
  }
}

2. 分包版

  • commands>init>lib>index.js

lerna create init

'use strict';

function init(projectName, cmdObj) {
    console.log('init', projectName, cmdObj.force);
}
module.exports = init

commands>init: npm link

  • core>cli>package.json
"dependencies": {
  "@zmoon-cli-dev/init": "file:../../commands/init",
}

core: npm link

  • core>cli>lib>index.js
const init = require('@zmoon-cli-dev/init')
function registerCommand() {
  program
    .command('init [projectName]')
    .option('-f, --force', '是否强制初始化项目')
    .action(init)

  program.parse(process.argv)
  if(program.args && program.args.length < 1) {
    program.outputHelp()
  }
}

zmoon-cli-dev init test-project --force

3 高性能脚手架架构设计

3.1 痛点分析

  • 存在的问题:

    1. cli安装速度慢:所有 package 都集成在 cli 里,因此当命令较多时,会减慢 cli 的安装速度
    2. 灵活性差:init 命令只能使用 @zmoon-cli-dev/init 包,对于集团公司而言,每个 buinit 命令可能都各不相同,可能需要实现 init 命令动态化,如:
      • 团队A使用 @zmoon-cli-dev/init 作为初始化模块
      • 团队B使用自己开发的 @zmoon-cli-dev/my-init 作为初始化模块
      • 团队c使用自己开发的 @zmoon-cli-dev/your-init 作为初始化模块
  • 要求我们能够动态加载 init 模块,这将增加架构的复杂度,但大大提升脚手架的可扩展性,将脚手架框架和业务逻辑解耦

3.2 脚手架架构优化

  • 优化的点:

    1. 脚手架命令动态加载

    2. 脚手架命令使用缓存

    3. 脚手架命令动态加载完使用 node 多进程执行

posted on 2022-11-25 14:35  pleaseAnswer  阅读(38)  评论(0编辑  收藏  举报