您的位置:首页 > 技术中心 > 前端框架 >

为什么需要脚手架?详解node构建脚手架的步骤

时间:2023-04-10 22:38

为什么需要脚手架?怎么搭建脚手架?下面本篇文章介绍一下node构建脚手架的步骤,希望对大家有所帮助!

1 为什么需要脚手架

  • 根据交互动态生成项目结构和配置文件等。
  • 用户通过命令交互的方式下载不同的模版
  • 经过模版引擎渲染定制项目模版
  • 模版变动,只需更新模版即可,不需要用户更新脚手架 【相关教程推荐:nodejs视频教程、编程教学】

2 构建步骤

  • 新建mycli文件夹(可自定义文件名),下方新建bin文件,bin文件新建index.js,这个index.js就是入口文件,index.js文件头部加入#!/usr/bin/env node代码

  • 生成package.json文件,此时会有个bin配置对象,key值即为全局脚手架名称,value是入口文件bin文件的index.js路径。

    npm init -y    npm install

  • 将脚手架全局命令链接到全局,终端打印mycli即链接成功。

    //命令可以将一个任意位置的npm包链接到全局执行环境,从而在任意位置使用命令行都可以直接运行该npm包。npm link
  • 安装依赖

  npm install commander inquirer@8.2.5 download-git-repo chalk@4.1.2 ora@5.4.1 figlet handlebars
  • commander:命令行工具,有了它我们就可以读取命令行命令,知道用户想要做什么了
  • inquirer: 交互式命令行工具,给用户提供一个漂亮的界面和提出问题流的方式
  • download-git-repo:下载远程模板工具,负责下载远程仓库的模板项目
  • chalk:颜色插件,用来修改命令行输出样式,通过颜色区分 info、error 日志,清晰直观
  • ora:用于显示加载中的效果,类似于前端页面的 loading 效果,像下载模板这种耗时的操作,有了 loading 效果可以提示用户正在进行中,请耐心等待
  • figlet :镂空字体样式

注意:下方代码都放在bin文件index.js进行调试

2.1 commander.js概述

commander.js是一个工具,用来构建node的命令行程序,使得能够使用自定义指令在全局命令行运行node脚本。本来我们只能在脚本所在文件的根目录里通过node xxx.js运行脚本,通过commander构建命令行程序后,就能在任意一个目录里,比如桌面,比如用户目录,直接输入自定义的那个指令,就能直接运行脚本,更加简便。

#!/usr/bin/env node//就是解决了不同的用户node路径不同的问题,可以让系统动态的去查找node来执行你的脚本文件。//node.js内置了对命令行操作的支持,在 package.json 中的 bin 字段可以定义命令名和关联的执行文件。const program = require("commander")program.version('1.1.0')function getFramwork (val) {  console.log(val);}const myhelp = function (program) {  program.option('-f --framwork <framwork>', '设置框架', getFramwork)}const createProgress = function (program) {  program.command('create <progress> [other...]')    .alias('crt')    .description('创建项目')    .action((progress, arg) => {      console.log(progress, arg);    })}myhelp(program);createProgress(program);program.parse(process.argv)// 补充.parse()// 作用就是解析,参数就是要解析的字符串,一般使用时参数就是用process.argv,就是用户输入参数

执行全局命令mycli即可输出所有命令~~

image.png

2.2 download-git-repo

  • download(repository, destination, options, callback)
  • repository:下载地址
  • destination:下载路径
  • options:配置项 {clone:true}
  • callback:下载后的回调
#!/usr/bin/env nodeconst download = require('download-git-repo');download('direct:https://gitlab.com/flippidippi/download-git-repo-fixture.git', "xxx", { clone: true }, (err) => {  console.log(err ? 'Error' : 'Success')})

执行mycli即可看到文件下生成一个xxx文件

image.png

2.3 Inquirer(命令交互)

inquirer 是一个常用的交互式终端用户界面集合。 简单来说 inquirer 是可以让我们很方便的做各种终端交互行为的一个库。

inquirer 主要提供了三个方法方便我们注册问题

  • prompt(questions) => promise该方法就是 终端交互的核心方法,运行 prompt 方法即告诉终端启动 交互式命令界面。 - prompt 方法需要传入一个 questions 数组, questions 数组包含对象形式的各个 question. question 的具体结构字段含义在后文介绍。 - prompt 方法的返回值是一个 promise 对象,promise.then 接收的返回值是 answers 对象,answers 对象包含前面所有问题回答的数据结果。
#!/usr/bin/env nodeconst inquirer = require("inquirer")function getUsername() {  return inquirer    .prompt([      {        type: "input",        name: "progress",        message: "请输入项目名称",        default: "progress",        filter(input) {          return input.trim()        },        validate(input) {          return input.length > 0        },      },    ])    .then((answer) => {      console.log(answer)    })}function getFramework() {  return inquirer    .prompt([      {        type: "list",        name: "framework",        choices: [          "express",          new inquirer.Separator(),          "koa",          new inquirer.Separator(),          "egg",        ],        message: "请选择你所使用的框架",      },    ])    .then((answer) => {      console.log(answer)    })}function getSelect() {  return inquirer    .prompt([      {        type: "checkbox",        name: "userndasde",        choices: [          { name: "pr", disabled: true },          { name: "oa", checked: true },          "gg",        ],        message: "需要的验证格式",        // default: ["oa"],      },    ])    .then((answer) => {      console.log(answer)    })}async function init() {  await getSelect()  await getUsername()  await getFramework()}init()

2.4 ora and chalk(美化)

在用户输入答案之后,开始下载模板,这时候使用 ora 来提示用户正在下载中。

注意:注意版本不同引入方式不同,这里用ora(版本5.4.1) ,chalk(版本4.1.2)

const ora = require("ora")const chalk = require("chalk")const spinner = ora("Loading unicorns").start()spinner.text = chalk.blue("下载中~~~~~~")setTimeout(() => {  spinner.succeed(chalk.red("下载成功!"))  spinner.fail("下载失败!")  spinner.warn("警告!")}, 2000)

2.5 figlet(镂空文字)

镂空文字调试器地址:地址

figlet旨在完全实现JavaScript中的FIGfont规范。它可以在浏览器和Node.js中工作。

用法

figlet.text( description,{options},callback(err,data){}) 这个是异步的会被

参数

  • description:需要格式化的字符串

  • options:参数配置

    • Font:字体,Default value:Standard
    • horizontalLayout:布局,Default value:default; Values:{default,full,fitted};
    • verticalLayout:垂直布局, Default value:default; Values:{defalut,full,fitted,controlled smushing,universal smushing};
    • Width:宽度;
    • whitespaceBreak:换行(Boolean); Default value:false
  • callback(err,data):回调

const figlet = require("figlet")const chalk = require("chalk")//简单函数function handleAsync(params) {  const JAVASCRIPT = figlet.textSync(    "NODEJS",    {      font: "big",      horizontalLayout: "fitted",      verticalLayout: "controlled smushing",      width: 600,      whitespaceBreak: true,    },    function (err, data) {      if (err) {        console.log("Something went wrong...")        console.dir(err)        return      }      console.log(data)    }  )  console.log(chalk.blue.bold(JAVASCRIPT))}handleAsync()

image.png

总结

创建一个完整的脚手架

目录结构:

  • bin/index.js
#!/usr/bin/env nodeconsole.log("adas");require("../lib/commander/index.js")
  • lib/commonder/index.js
const program = require("commander")const init = require('../inquirer/index');const downloadFun = require("../core/download.js");program.version('1.1.0')function getFramwork (val) {  console.log(val);}const myhelp = function (program) {  program.option('-f --framwork <framork> [other...]', '设置框架', getFramwork)}const createProgress = function (program) {  program.command('create <progress> [other...]')    .alias('crt')    .description('创建项目')    .action((progress, arg) => {      init();    })}const downloadUrl = function (program) {  program.command('download <url> [...other]')    .description('下载内容')    .action((url, ...args) => {      console.log(args);      downloadFun(url, args[1].args[1])    })}myhelp(program);downloadUrl(program);createProgress(program)program.parse(process.argv)
  • lib/core/action.js (package.json重写)
const fs = require('fs');const path = require("path");const handlebars = require("handlebars");  function modifyPackageJson (options) {    let downloadPath = options.projectName;    const packagePath = path.join(downloadPath, 'package.json');    console.log(packagePath, "packagePath");        //判断是否存在package.json文件    if (fs.existsSync(packagePath)) {      let content = fs.readFileSync(packagePath).toString();            //判断是否选择了eslint      if (options.isIslint) {        let targetContent = JSON.parse(content);        content = JSON.stringify(targetContent);        targetContent.dependencies.eslint = "^1.0.0";        console.log("content", content);      }            //写入模板      const template = handlebars.compile(content);      const param = { name: options.projectName };      const result = template(param);            //重新写入package.json文件      fs.writeFileSync(packagePath, result);      console.log('modify package.json complate');    } else {      throw new Error('no package.json');    }  }  module.exports = modifyPackageJson
  • lib/core/download.js
  const download = require('download-git-repo');  const ora = require("ora");  const chalk = require("chalk");  const figlet = require("figlet");  const modifyPackageJson = require("./action")    function handleAsync (params) {    const JAVASCRIPT = figlet.textSync('JAVASCRIPT', {      font: 'big',      horizontalLayout: 'fitted',      verticalLayout: 'controlled smushing',      width: 600,      whitespaceBreak: true    }, function (err, data) {      if (err) {        console.log('Something went wrong...');        console.dir(err);        return;      }      console.log(data);    });    console.log(chalk.blue.bold(JAVASCRIPT));  }    const downloadFun = (url, option) => {    const spinner = ora("Loading unicorns").start()    spinner.text = chalk.blue("下载中");        download(url, option.projectName, { clone: true }, function (err) {      if (err) {        spinner.fail("下载失败!");        handleAsync()      } else {        spinner.succeed(chalk.red("下载成功!"))        console.log(chalk.blue(`cd ${option.projectName}`))        console.log(chalk.red("npm install"))        console.log(chalk.yellow(`npm run dev`))        modifyPackageJson(option)        handleAsync()      }    })  }  module.exports = downloadFun;
  • inquire/index.js 注意frameworkConfig写自己的gitlab仓库地址
  const inquirer = require("inquirer");  const downloadFun = require("../core/download.js");  const frameworkConfig = {    front: "https://gitlab.com/flippidippi/download-git-repo-fixture.git",    manager: "https://gitlab.com/flippidippi/download-git-repo-fixture.git"  }  const config = {};    function getFramework () {    return inquirer.prompt([      {        type: 'list',        name: 'framework',        choices: ["front", "manager"],        message: "请选择你所使用的框架"      }    ]).then((answer) => {      return answer.framework;    })  }    function getProjectName () {    return inquirer.prompt([      {        type: 'input',        name: 'projectName',        message: '项目名称',        filter (input) {          return input.trim();        },      }    ]).then((answer) => {      console.log(answer, "FDsfs");      return answer.projectName;    })  }    function getIsEslint () {    return inquirer.prompt([      {        type: 'confirm',        name: 'isIslint',        message: '是否使用eslint校验格式?'      }    ]).then((answer) => {      return answer.isIslint;    })  }    async function init () {    config.projectName = await getProjectName();    config.framework = await getFramework();    config.isIslint = await getIsEslint();    let url = config.framework == "front" ? frameworkConfig.front : frameworkConfig.manager;    downloadFun("direct:" + url, config);  }  module.exports = init;

更多node相关知识,请访问:nodejs 教程!

以上就是为什么需要脚手架?详解node构建脚手架的步骤的详细内容,更多请关注Gxl网其它相关文章!

热门排行

今日推荐

热门手游