青训营Pro自定义脚手架从1到∞ - 自动化思维搭建koa脚手架

Posted YK菌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了青训营Pro自定义脚手架从1到∞ - 自动化思维搭建koa脚手架相关的知识,希望对你有一定的参考价值。

嗨!~ 大家好,我是YK菌 🐷 ,一个微系前端 ✨,爱思考,爱总结,爱记录,爱分享 🏹,欢迎关注我呀 😘 ~ [微信号: yk2012yk2012,微信公众号:ykyk2012]

本文相关代码:youth_camp_ykjun: 青训营&开课吧 实战课程代码仓库 - Gitee.com

一、前言

今天来复习&总结青训营实训营第二天的内容,在上次课程中我们从0到1创建了一个vue的脚手架,今天我们跟着崔老师@阿崔cxr使用自动化思维来搭建一个koa脚手架

PS:本博文是崔老师 前端工程化 课程的第一部分:初始化,后面我会尽力更新完~ 包括:webpack原理、rollup原理、实现mini-vite、自动测试等

二、分析vue-cli

  1. 使用指令创建一个项目
vue create hello-world
  1. 提供选择 单选/多选

  1. 安装依赖

总结一下步骤就是:① 输入指令 ② 选择配置 ③ 创建文件 ④ 安装依赖

那么我们就按照这个步骤来写我们的脚手架

三、起步

接下来我们就要自己实现一个脚手架,有了上节课的基础知识,今天的内容可以让你更上一层楼。

首先创建我们脚手架的项目目录 learn-koa-setup

然后初始化项目npm init -y

将项目模块化规范改成ESM,即在package.json中添加"type": "module",

创建好index.js,在这里编写脚手架要做的事情

import fs from "fs";

// 核心:自动化思维【先想手动创建一个项目的基本步骤】

// 1. 创建了文件夹(项目名)
fs.mkdirSync("ykyk");

// 2. 创建了index.js
fs.writeFileSync("./ykyk/index.js", "index");

// 3. 创建了package.json
fs.writeFileSync("./ykyk/package.json", "package");

// 4. 安装依赖
// TODO package -> npm

我们测试一下

node index.js

文件夹和文件已经创建,也填充了基本内容

我们每次执行这个命令,都要手动删除ykyk这个文件夹,然后再新建,比较麻烦,我们用指令让他自动执行,在package.json中添加指令(我这里使用的是windows删除文件夹的指令)

"scripts": 
  "test": "rd /s ykyk && node index.js"
,

此时就可以直接使用 npm test 命令来完成删除文件夹,然后执行index.js文件了!

四、完善

上面的步骤虽然很easy,但是已经是大体的方向已经明确了,接下来要做的就是不断完善这里面的具体细节。

注意:我们这里使用小步骤的方式写代码。一点一点的完善,一次只完成一个很小的功能,然后进行测试。功能没问题再往下写,这样保证我们在编写代码的时候及时知道哪里出了错,及时修正。

我们这里用到了很多根路径,我们将他封装起来

function getRootPath() 
  return "./ykyk";

就可以这样使用了

// 1. 创建了文件夹(项目名)
fs.mkdirSync(getRootPath());

// 2. 创建了index.js
fs.writeFileSync(getRootPath()+"/index.js", "index");

// 3. 创建了package.json
fs.writeFileSync(getRootPath()+"/package.json", "package");

1. 生成index.js内容

① 生成模板

我们创建一个模板 indexTemplate.js

先将模板内容写死,然后导出创建模板的函数

export function createIndexTemplate() 
  return `
    const Koa = require("koa");
    const app = new Koa();
    app.listen(8000, () => 
      console.log("open server localhost:8000");
    );
  `;

在index.js中引入,并使用

import  createIndexTemplate  from "./indexTemplate.js";

fs.writeFileSync(getRootPath() + "/index.js", createIndexTemplate());

最后执行指令,查看效果

② 动态生成模板

接下来我们就要让模板的内容可以根据不同的设置,来呈现不同的代码

这里我们用到一个第三方库 ejs 高效的嵌入式 JavaScript 模板引擎

安装 npm i ejs

创建文件 template/index.ejs

const Koa = require("koa");

const app = new Koa();

app.listen(8000, () => 
  console.log("open server localhost:8000");
);

此时在 indexTemplate.js 就应该这样引入和使用

import ejs from "ejs";
import fs from "fs";

export function createIndexTemplate() 
  // 读取ejs模板
  const template = fs.readFileSync("./template/index.ejs", "utf-8");
  // 交给ejs来渲染
  const code = ejs.render(template);
  // 返回渲染后的模板
  return code;

此时再测试一下,发现没有问题,然后就要进行条件渲染了

修改index.ejs文件【根据文档中给出的语法】

const Koa = require("koa");

<% if (router)  %>
const Router = require("koa-router");
<%  %>

const app = new Koa();

<% if (router)  %>
const router = new Router();
router.get("/", (ctx) => 
ctx.body = "hello YK菌";
);
app.use(router.routes());
<%  %>

app.listen(8000, () => 
console.log("open server localhost:8000");
);

通过给渲染函数传递参数来决定是否渲染响应的内容

const code = ejs.render(template, 
  router: true,
);

③ 模拟用户输入的数据

其实这个参数应该是由用户来选择的,但是我们是小步骤的方式编写代码,所以我们先将用户的输入写死。

// 先把配置中间件数据写死
const inputConfig = 
  middleware: 
    router: true,
  ,
;

// 将配置文件传入创建模板函数
fs.writeFileSync(getRootPath() + "/index.js", createIndexTemplate(inputConfig));

indexTemplate.js中这样使用即可

export function createIndexTemplate(inputConfig) 
  // 读取ejs模板
  const template = fs.readFileSync("./template/index.ejs", "utf-8");
  // 交给ejs来渲染
  const code = ejs.render(template, 
    router: inputConfig.middleware.router,
  );
  // 返回渲染后的模板
  return code;

④ 拓展完整选项

我们把四个选项补齐

// 程序的input
const inputConfig = 
  middleware: 
    router: true,
    static: true,
    views: true,
    body: true
  ,
;

indexTemplate.js

import ejs from "ejs";
import fs from "fs";

export function createIndexTemplate(inputConfig) 
  // 读取ejs模板
  const template = fs.readFileSync("./template/index.ejs", "utf-8");
  // 交给ejs来渲染
  const code = ejs.render(template, 
    router: inputConfig.middleware.router,
    static: inputConfig.middleware.static,
    views: inputConfig.middleware.views,
    body: inputConfig.middleware.body
  );
  // 返回渲染后的模板
  return code;

template/index.ejs

const Koa = require("koa");
<% if (router)  %>
const Router = require("koa-router");
<%  %>
<% if (static)  %>
const serve = require("koa-static");
<%  %>
<% if (views)  %>
const views = require("koa-views");
<%  %>
<% if (body)  %>
const body = require("koa-body");
<%  %>
const app = new Koa();

<% if (router)  %>
const router = new Router();
router.get("/", (ctx) => 
ctx.body = "hello YK菌";
);
app.use(router.routes());
<%  %>
<% if (static)  %>
app.use(serve(__dirname + "/static"));
<%  %>
<% if (views)  %>
app.use(
views(__dirname + "/views", 
extension: "pug",
)
);
<%  %>
<% if (body)  %>
app.use(
body(
multipart: true,
)
);
<%  %>

app.listen(8000, () => 
console.log("open server localhost:8000");
);

测试我们的代码功能 npm test

此时创建出的ykyk项目中的index.js就是根据选项生成的完整的代码了

显示 ykyk/index.js 中的内容符合我们的预期

const Koa = require("koa");

const Router = require("koa-router");

const serve = require("koa-static");

const views = require("koa-views");

const body = require("koa-body");

const app = new Koa();

const router = new Router();
router.get("/", (ctx) => 
  ctx.body = "hello YK菌";
);
app.use(router.routes());

app.use(serve(__dirname + "/static"));

app.use(
  views(__dirname + "/views", 
    extension: "pug",
  )
);

app.use(
  body(
    multipart: true,
  )
);

app.listen(8000, () => 
  console.log("open server localhost:8000");
);

2. 生成package.json内容

简单使用ejs生成了index.js模板,我们再基于数据生成package.json,和上面的步骤基本一样

① 生成模板

首先创建 packageTemplate.js

import ejs from "ejs";
import fs from "fs";

export function createPackageTemplate(inputConfig) 
  // 读取ejs模板
  const template = fs.readFileSync("./template/package.ejs", "utf-8");
  // 交给ejs来渲染
  const code = ejs.render(template, 
    router: inputConfig.middleware.router,
    static: inputConfig.middleware.static,
    views: inputConfig.middleware.views,
    body: inputConfig.middleware.body,
  );
  // 返回渲染后的模板
  return code;

然后创建一个模板 template/package.ejs


  "name": "koa-setup-ykyk",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": 
  "test": "echo \\"Error: no test specified\\" && exit 1",
  "build": "rm -rf hei && node index.js"
  ,
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": 
    "koa": "^2.13.1"
    <% if (router)  %>
    ,"koa-router": "^10.1.1"
    <%  %>
    <% if (static)  %>
    ,"koa-static": "^5.0.0"
    <%  %>
    <% if (views)  %>
    ,"koa-views": "^7.0.1"
    ,"pug": "^3.0.2"
    <%  %>
    <% if (body)  %>
    ,"koa-body": "^4.1.3"
    <%  %>
  

这里有一个小技巧,就是把可选包的前面加上逗号,这样就可以保证这里的格式没有问题了~

② 获取项目名称

模拟用户输入packageName

// 程序的input
const inputConfig = 
  packageName: 'yk_demo',
  middleware: 
    router: true,
    ...
  ,
;

在这里引入即可

...
  // 交给ejs来渲染
  const code = ejs.render(template, 
    packageName: inputConfig.packageName,
    ...
  );
...

最后根据ejs的语法在template/package.ejd中这样写就可以

"name": "<%= packageName %>",

最后我们测试一下npm test,可以看到生成的package.json符合我们的预期


  "name": "yk_demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": 
    "test": "echo \\"Error: no test specified\\" && exit 1",
    "build": "rm -rf hei && node index.js"
  ,
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": 
    "koa": "^2.13.1",
    "koa-router": "^10.1.1",
    "koa-static": "^5.0.0",
    "koa-views": "^7.0.1",
    "pug": "^3.0.2",
    "koa-body": "^4.1.3"
  

相同的写法,我们可以把index.js中的端口号也设置成可供用户自己选择的模式,具体的可以看我代码仓库中的代码
youth_camp_ykjun: 青训营&开课吧 实战课程代码仓库 - Gitee.com

3. 获取用户输入

接下来我们要做的就是获取真实的用户输入,这里我们使用 inquirer 这个库(A collection of common interactive command line user interfaces.) 网址:inquirer - npm (npmjs.com)

首先安装这个库 npm i inquirer

创建question/index.js获取用户输入

import inquirer from "inquirer";

export function question() 
  return inquirer.prompt([
    // 获取用户输入的项目名称
     type: "input", name: "packageName", message: "set package name" ,
    // 获取用户输入的端口号,默认值为8080
    
      type: "number",
      name: "port",
      message: "set port number",
      default: () => 8080,
    ,
    // 多选,获取用户选择的中间件
    
      type: "checkbox",
      name: "middleware",
      choices: [
         name: "koaRouter" ,
         name: "koaStatic" ,
         name: "koaViews" ,
         name: "koaBody" ,
      ],
    ,
  ]);

然后将模拟的数据替换成真实用户输入的数据

import  question  from "./question/index.js";

const answer = await question();

// 程序的input
const inputConfig = 
  packageName: answer.packageName,
  port: answer.port,
  middleware: 
    router: answer.middleware.indexOf("koaRouter") !== -1,
    static: answer.middleware.indexOf("koaStatic") !== -1,
    views: answer.middleware

以上是关于青训营Pro自定义脚手架从1到∞ - 自动化思维搭建koa脚手架的主要内容,如果未能解决你的问题,请参考以下文章

青训营Pro️从0到1实现一个自己的前端约定路由项目脚手架️ 工具~

青训营Pro️从0到1实现一个自己的前端约定路由项目脚手架️ 工具~

青训营Pro 前端框架设计理念 - Vue3动机 - 手写实现mini-vue

青训营Pro 前端框架设计理念 - Vue3动机 - 手写实现mini-vue

青训营Node.js基础 - 异步编程四种解决方案

青训营Node.js基础 - 异步编程四种解决方案