vue+Nodejs+Koa搭建前后端系统(三)--koa-generator项目优化修改

news2024/11/13 17:52:25

前言

  • 计算机系统为Windows 10 专业版

修改package.json配置

原package.json文件中scripts字段的配置字段为:
koa-generator生成的项目中package.json文件的
在终端运行

npm run dev

可能会报错

‘.’ 不是内部或外部命令,也不是可运行的程序或批处理文件。

解决方法就是修改package.json文件中dev指令:

"dev": "nodemon bin/www",

修改之后package.json文件中scripts字段的配置字段为:
在这里插入图片描述

修改/bin/www.js文件

能不能像vue-cli启动前端项目时在控制台(终端)输出服务器地址和启动时间:
在这里插入图片描述

服务器启动时间输出

使用console.time()console.timeEnd()组合即可输出代码运行时间(计算是的他俩之间同步代码的运行时间,点这里查看使用方法)

var app = require("../app");
var debug = require("debug")("demo:server");
var http = require("http");
console.time("Time");
/**
......
此处省略好多字
*/
console.timeEnd("Time");

命令行颜色输出

使用console.log()可以输出带颜色的文字,甚至可以加上一点点小样式。点这里查看console家族的趣事。

console.log("给你点%c颜色", "color: yellow; ");

其中 %c 之后的字符就会应用color: yellow;样式。
但是这个只是在浏览器控制台才会起效果,而node的console输出是在命令行(终端)。问题不大,可以这样写:

console.log("%s\x1B[31m%s\x1B[0m", "给你点", "颜色");

麻!拆开来看:
%s:字符串占位符,输出时会将console.log剩下的参数匹配替换进来(相当于console.log('给你点\x1B[31m颜色\x1B[0m')
\x1B[31m:ANSI转义码(ANSI escape code) 它将被终端拦截并指示它切换到红色。
\x1B[0m:ANSI转义码(ANSI escape code)表示重置终端颜色,使其在此之后不再继续成为所选颜色。
在这里插入图片描述也可以安装colors-console插件

npm i -D colors-console

/bin/www.js:

const colors = require('colors-console')
console.log('给你点' + colors('red', '颜色'))

ANSI转义码对照表:

ANSI转义码colors-console插件对应参数描述
\x1B[0m重置样式
\x1B[1mbright字体颜色:亮色(应该是粗体)
\x1B[2mgrey字体颜色:灰色
\x1B[3mitalic斜体
\x1B[4munderline下划线
\x1B[7mreverse反向
\x1B[8mhidden隐藏
\x1B[30mblack字体颜色:黑色
\x1B[31mred字体颜色:红色
\x1B[32mgreen字体颜色:绿色
\x1B[33myellow字体颜色:黄色
\x1B[34mblue字体颜色:蓝色
\x1B[35mmagenta字体颜色:品红
\x1B[36mcyan字体颜色: 青色
\x1B[37mwhite字体颜色:白色
\x1B[40mblackBG字体背景:黑色
\x1B[41mredBG字体背景:红色
\x1B[42mgreenBG字体背景:绿色
\x1B[43myellowBG字体背景:黄色
\x1B[44mblueBG字体背景:蓝色
\x1B[45mmagentaBG字体背景:品红
\x1B[46mcyanBG字体背景:青色
\x1B[47mwhiteBG字体背景:白色

如果在引入插件有灰色三个小点的提示,那不是错误,不影响运行,只是提示没找到声明文件(.d.ts)。
在这里插入图片描述如果看着不得劲,在终端尝试安装其声明文件(.d.ts)

 npm i --save-dev @types/colors-console

若没有找到该声明文件,则可以在根目录新建一个koa.d.ts(koa只是文件名,随便取名)。添加如下声明代码:

declare module "colors-console";

ANSI转义码(ansi escape code):
ANSI是国际标准,所以(应该是)兼容所有平台。
以下是原文摘抄,原文在这里
\x1B表示ACSII执行escape命令,也可写作\033。
原文图片
在这里插入图片描述
\x1B加上 [ ,他们一起组成的部分通常被称为CSI (Control Sequence Introducer)。
ansi escape code的语法:

0x1B + "[" + <params> + <fn>
  • <params> 由0个或者多个数字组成,是函数的参数,多个参数之间由分号进行分割。
  • <fn> 是一个字母,是ansi escape code需要调用的函数名。

因此: \x1b[0;1;34m 可以理解为 m(0, 1, 34); 同样,\x1b[A 可以理解为: A()

<fn>可用的函数 :

函数名描述
A将光标向上移动n行
B将光标向下移动n行
C将光标向前移动n个字符
D将光标向后移动n个字符
E将光标向下移到到n行的行首
F将光标向上移到到n行的行首
G将光标移动到当前行中的第n列
H将光标移动到第m行,第n列(以左上为起点)
J清除部分屏幕。0、1、2和3有各种特定的功能
KClear part of the line. 0, 1, and 2 have various specific functions
S将窗口向上滚动到n行
T将窗口向下滚动到n行
s保存当前光标位置以供u函数使用
u将光标设置回s函数最后保存的位置
f和G函数一样
m设置图形模式(SGR)

m函数参数说明:

参数值描述
0重置:关闭所有属性
1粗体
3斜体
4下划线
30–37从0-7的基本颜色板中设置文本颜色,即前景色
38;5;n从256色调色板中将文本颜色设置为n,例如:\x1b[38;5;34m
38;2;r;g;b将文本颜色设置为rgb,例如:\x1b[38;2;255;255;0m
40–47从0-7的基本颜色板中设置背景色
48;5;n从256色调色板中将背景色设置为n,例如:\x1b[48;5;34m
48;2;r;g;b将背景色设置为rgb,例如:\x1b[48;2;255;255;0m
90–97从0-7的明亮调色板中设置文本颜色
100–107从0-7的明亮调色板中设置背景色

本地服务器和局域网服务器

本地服务器地址就是localhost(或者127.0.0.1),只有本机能够访问。
局域网服务器地址是本机的IP(IPv4),只有连接此局域网的设备可以访问。
vue-cli的Network就是用的该IP:
在这里插入图片描述

查看本机IP地址:
有两种方法查看-图形化界面查看和命令行查看
图形化界面查看步骤:桌面-网络(鼠标右击)-属性-已连接的网路(鼠标左击)- 详细信息
在这里插入图片描述

在这里插入图片描述在这里插入图片描述
在这里插入图片描述图形化界面查看的缺点是:不同版本的Window系统查看方式可能略微不同。
所以推荐命令行查看本机IP地址:
在终端(cmd)输入如下指令:

ipconfig

即可。
在这里插入图片描述
其中的IPv4地址即是以后node服务器的局域网地址。

node获取本机的IP地址(IPv4)

获取本机的IP地址需要用到node的os模块,具体代码如下:

var os = require("os");
function getIPAdress() {
  var interfaces = os.networkInterfaces();
  for (var devName in interfaces) {
    var iface = interfaces[devName];
    for (var i = 0; i < iface.length; i++) {
      var alias = iface[i];
      if (
        alias.family === "IPv4" &&
        alias.address !== "127.0.0.1" &&
        !alias.internal
      ) {
        return alias.address;
      }
    }
  }
}

此方法只适用本地开发使用,打包发布到服务器后会失效。

os.networkInterfaces()的返回值格式为:

{
	'以太网 2': [
	    {
	      address: '',//用于指定分配的网络地址,即IPv4或IPv6
	      netmask: '',//指定IPv4或IPv6网络掩码
	      family: 'IPv6',//值为IPv4或IPv6之一
	      mac: '',//指定网络接口的MAC地址
	      internal: false,//如果接口为环回一则为true,否则为false
	      cidr: '',//用于指定分配的IPv4或IPv6地址以及CIDR表示法中的路由前缀。如果网络掩码无效,则将其设置为null
	      scopeid: 4//指定IPv6的作用域ID
	    }
	  ]
}

设置服务器端口

var app = require("../app");
var http = require("http");

var port = normalizePort(process.env.PORT || "3000");
var server = http.createServer(app.callback());
server.listen(port);

normalizePort(process.env.PORT || "3000");这段代码就是获取端口号:先从环境变量中取,没有的话,默认3000。
normalizePort()是koa-generator中引入的,他的作用就是规范化端口的值,其定义是:

/**
 * Normalize a port into a number, string, or false.
 */
function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

server.listen(port);用于监听端口,也可以这样写server.listen(port,host);
port是端口,host是主机ip地址。

  • 如果port省略或是0,系统会随意分配一个,在server的listening事件触发后能被通过server.address().port检索到。
  • 如果host省略,若IPv6可用,服务器将会接收基于IPv6地址(默认host为 :: 双冒号,server.address().address返回的也是该值)的连接,否则接收基于IPv4地址(默认host为0.0.0.0,server.address().address返回的也是该值)的连接。并且127.0.0.1也可连接。若host有指定地址,那么只该指定的host可连接。
    所以server.listen(port)相当于server.listen(port,'::')server.listen(port,'0.0.0.0')

process.env属性返回一个包含用户环境信息的对象。
修改环境变量的方法有两种,第一种是在运行中修改:
设置或新增process.env的属性:

process.env.PORT = 3000;//设置的值会自动转成字符串'3000'

删除process.env的属性:

delete process.env.PORT

注意:在Windows系统下,环境变量是不区分大小写的:

process.env.TEST === process.env.test;//true

第二种是在package.json中设置命令:

{
  "name": "server2",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "set PORT=5252 && nodemon bin/www localhost"
  }
}

这两种方式都是临时修改环境变量,当程序关闭变量也都会还原。

若要永久,需要在操作系统中设置,比如window系统:
此电脑(鼠标右击)- 属性 - 高级系统设置 - 环境变量
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

命令行参数

-process.argv返回node启动时的命令行参数数组。
比如:
修改package.json中的start命令:

{
	"scripts": {
    	"start": "node bin/www --param1 --param2=5152"
  	}
}

在/bin/www.js中打印命令行参数:

console.log(process.argv);

启动服务器

npm run start

会得到如下输出:

[
  'E:\\Node\\install\\node.exe',
  'F:\\test\\vue_node\\hello-node\\server2\\bin\\www',
  '--param1',
  '--param2=5152'
]

命令行参数约定是以 --(两个破折号) 作为前缀。

  • 数组的第一个元素process.argv[0]——返回启动Node.js进程的可执行文件所在的绝对路径
  • 第二个元素process.argv[1]——为当前执行的JavaScript文件路径
  • 剩余的元素为其他命令行参数,注意是字符串。也就是说命令行参数是从数组的第二个元素开始的。

(nodemon指令同理,因为其也是执行node指令)

解析命令行参数可以手写:

function getProcessArgv(){
	const argvs = process.argv.splice(2);
	const r = {};
	argvs.forEach((item)=>{
		const v = item.split('=');
		const key = v[0].replace(/^-+/gim,"");
		const value = v[1] || true;
		r[key] = value;
	});
	return r;
}

或者安装Yargs插件

npm i yargs@6.0.0

使用方法:

//node指令: node bin/www --name=xiaoyang --age=18
var argv = require('yargs').argv;
console.log(argv.name,argv.age);//xiaoyang  18

注意!使用npm run指令时,使用参数需要加--,这是npm传递参数给script的方法。
比如:

package.json

"scripts": {
    "start": "node bin/www"
},

在终端执行:

npm run start -- --port=5152

相当于执行:

node bin/www --port=5152

如果npm run 中的参数与package.json中相应指令中的参数名一样,不会覆盖。
比如:

"scripts": {
    "start": "node bin/www --port=5153"
},

在终端执行:

npm run start -- --port=5152

相当于执行:

node bin/www --port=5153 --port=5152

process.argv的值为:

[
  'E:\\Node\\install\\node.exe',
  'F:\\test\\vue_node\\hello-node\\server2\\bin\\www',
  '--port=5153',
  '--port=5152'
]

yargs插件的返回值为:

{
	port: [ 5153, 5152 ]
}

修改app.js文件

路由目录自动加载

实现此功能需要遍历路由目录,需要加载node的fs模块path模块
遍历路由目录:

const Koa = require("koa");
const app = new Koa();
var fs = require("fs");
var path = require("path");

//要遍历的文件夹所在的路径
var routesDir = path.resolve("routes/");
//根据文件路径读取文件,返回文件列表
function loadRoutes(dirPath) {
  fs.readdir(dirPath, { withFileTypes: true }, function (err, files) {
    if (err) {
      console.warn(err, "读取文件夹错误!");
    } else {
      //遍历读取到的文件列表
      files.forEach(function (dirent) {
        const currentPath = path.join(dirPath, dirent.name);
        if (dirent.isDirectory()) {
          //文件夹
          loadRoutes(currentPath);
        } else if (dirent.isFile()) {
          //文件
          const relativePath = path.relative(__dirname, currentPath);
          const router = require(relativePath);
          app.use(router.routes(), router.allowedMethods());
        }
      });
    }
  });
}
  • path.resolve([…paths])
    该方法将路径或路径片段的序列解析为绝对路径。
    给定的路径序列从右到左处理,每个后续的 path 会被追加到前面,直到构建绝对路径。
    如果在处理完所有给定的 path 片段之后,还没有生成绝对路径,则使用当前工作目录(__dirname);如果没有传入 path 片段,则 path.resolve() 将返回当前工作目录的绝对路径(相当于__dirname)。

    path.resolve('/foo','/bar', './baz');
    // 返回: '/bar/baz'
    
    path.resolve('/foo/bar', './baz');
    // 返回: '/foo/bar/baz'
    
    path.resolve('/foo/bar', '/tmp/file/');
    // 返回: '/tmp/file'
    
    path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
    // 如果当前工作目录是 F:/test/vue_node/hello-node/server2,
    // 则返回 'F:/test/vue_node/hello-node/server2/wwwroot/static_files/gif/image.gif'
    
  • path.join([…paths])
    使用特定于平台的分隔符作为定界符将所有给定的 path 片段连接在一起,然后返回规范化生成的路径。如果连接的路径字符串是零长度字符串,则将返回 ‘.’,表示当前工作目录。返回的路径类型与其第一个参数的路径类型一致:第一个参数为绝对路径,则返回绝对路径;第一个参数为相对路径,则返回相对路径。

    path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
    // 返回: '/foo/bar/baz/asdf'
    path.join('foo', 'bar', 'baz/asdf', 'quux', '..');
    // 返回: 'foo/bar/baz/asdf'
    
  • path.relative(from, to)
    path.relative() 方法根据当前工作目录返回从 from 到 to 的相对路径。 如果 from 和 to 都解析为相同的路径(在分别调用 path.resolve() 之后),则返回零长度字符串。
    如果零长度字符串作为 from 或 to 传入,则将使用当前工作目录而不是零长度字符串。

    path.relative('C:\\orandea\\test\\aaa', 'C:\\orandea\\impl\\bbb');
    // 返回: '..\\..\\impl\bbb'(Window系统返回反斜杠 \ )
    
  • __dirname
    当前模块的目录名。
    例如,在 F:/test/vue_node/hello-node/server2/app.js 中使用__dirname,其值则为 F:/test/vue_node/hello-node/server2

  • __filename
    当前模块的目录名。
    例如,在 F:/test/vue_node/hello-node/server2/app.js 中使用__filename,其值则为 F:/test/vue_node/hello-node/server2/app.js

  • fs.readdir(path[, options], callback)
    读取目录的内容。
    path:读取的目录路径(绝对路径或相对路径)、Buffe类或URL类:<string> | <Buffer> | <URL>。
    callback:回调函数,有两个参数 (err, files),其中 files 是目录中文件或目录信息的数组(<string>[] | <Buffer>[] | <fs.Dirent>[]),默认返回的是文件或目录名称数组。
    options:【可选】,<Object>

    • encoding:指定用于传给回调的文件名的字符编码,默认’utf-8。如果 encoding 设置为 ‘buffer’,则回调函数返回的files将作为 <Buffer> 对象数组传入。
    • withFileTypes :默认false。若为true,则回调函数返回的files将作为<fs.Dirent>对象数组传入。
  • <fs.Dirent>对象

    • isDirectory():如果\ <fs.Dirent> 对象描述文件系统目录,则返回 true。
    • isFile():如果 <fs.Dirent> 对象描述常规文件,则返回 true。
    • name:此 <fs.Dirent> 对象引用的文件名或目录名。若为目录则返目录名(比如routes),若为文件则返文件名(有后缀,比如index.js)

路由命名空间

由于路由是可以分几个文件的(/routes/),因此不可避免会碰到不同路由文件有相同的路由地址,这会照成路由冲突,解决办法就是使用命名空间:以/routes/ 为根目录,路由文件的绝对路径就是该文件中所有路由的命名空间。
比如:/routes/index.js 的命名空间是 /index。/routes/login/index.js 的命名空间是 /login/index。
然后用路由的prefix()方法设置路由命名空间:

/routes/index.js:

const router = require("koa-router")();
router.prefix("/index");
//请求路径为:/index
router.get("/", async (ctx, next) => {
  ctx.body = "koa2";
});
//请求路径为:/index/string
router.get("/string", async (ctx, next) => {
  ctx.body = "koa2 string";
});

自定义中间件

可以把 路由目录自动加载 的loadRoutes()方法做成中间件:新建/middleware/目录,用于存放中间件文件。同时可以把添加命名空间的代码也移到这个中间件中,而不用分散到各个路由文件中。

/middleware/loadRoutes.js

var fs = require("fs");
var path = require("path");
//要遍历的文件夹所在的路径
var routesDir = path.resolve("routes/");
//根据文件路径读取文件,返回文件列表
function loadRoutes(app, dirPath) {
  fs.readdir(dirPath, { withFileTypes: true }, function (err, files) {
    if (err) {
      console.warn(err, "读取文件夹错误!");
    } else {
      //遍历读取到的文件列表
      files.forEach(function (dirent) {
        const currentPath = path.join(dirPath, dirent.name);
        if (dirent.isDirectory()) {
          //文件夹
          loadRoutes(app, currentPath);
        } else if (dirent.isFile()) {
          //文件
          const relativePath = path.relative(__dirname, currentPath);
          const router = require(relativePath);
          //命名空间
          const routerNamespace = path
            .relative(routesDir, currentPath)
            .replace(/\.js$/, "");
          router.prefix("/" + routerNamespace); //Note: prefix always should start from / otherwise it won't work.
          app.use(router.routes(), router.allowedMethods());
        }
      });
    }
  });
}

module.exports = function (app) {
  loadRoutes(app, routesDir);
};

app.js

const load = require("./middleware/loadRoutes.js");
//加载全部路由
load(app);

webpack打包

一般情况node代码不需要打包,直接放到node服务器,即可运行。如果你的代码需要保护,防止别人滥用你的代码,可以考虑webpack打包,以增加别人滥改你代码的成本。
可以使用@vercel/ncc插件打包:
安装:

npm i @vercel/ncc

打包命令:

ncc build app.js -m -o dist
  • -m #混淆、压缩代码
  • -o #指定输出目录(这里根目录下的dist/)
  • app.js 项目入口文件(在 koa框架中是 app.js)

打包之后生成dist目录:

在这里插入图片描述

修改服务器入口文件(\bin\www.js)项目的引入:
var app = require("../app");修改为var app = require("../dist/index.js");即可。
你可能会遇到这种情况,明明写了路由,但是在请求时却是Not Found。原因可能是:

router.get("/", async (ctx, next) => {
  await ctx.render("index", {
    title: "Hello Koa 2!",
  });
   next();
});

ctx.render会解析pug文件为html,但是我们打包的时候并没有加载Pug解析器,所以代码会报错,从而Not Found。
不用纠结,因为本项目是前后端分离,后端只负责返回数据就好:

router.get("/", async (ctx, next) => {
  ctx.body = "Hello Koa 2!";
  next();
});

有个小插曲:我在百度查 打包node项目 时,看到了有@vercel/ncc、@zeit/ncc、pkg (前两个是webpack打包成单文件,pkg是编译成二进制文件)。于是就挑了一个,过几天写这篇文章时,忘了安装的是哪个了,只记得是全局安装,项目中的package.json文件无迹可查,还好有npm list -g --depth 0

修改的文件总览

package.json文件

{
  "name": "server2",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "start": "node bin/www",
    "dev": "nodemon bin/www --port=5152",
    "prd": "pm2 start bin/www",
    "build": "ncc build app.js -m -o dist",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "debug": "^4.1.1",
    "koa": "^2.7.0",
    "koa-bodyparser": "^4.2.1",
    "koa-convert": "^1.2.0",
    "koa-json": "^2.0.2",
    "koa-logger": "^3.2.0",
    "koa-onerror": "^4.1.0",
    "koa-router": "^7.4.0",
    "koa-static": "^5.0.0",
    "koa-views": "^6.2.0",
    "pug": "^2.0.3",
    "yargs": "^6.0.0"
  },
  "devDependencies": {
    "colors-console": "^1.0.3",
    "nodemon": "^1.19.1"
  }
}

/bin/www.js文件

var app = require("../app");
var debug = require("debug")("demo:server");
var http = require("http");
var colors = require("colors-console");
var os = require("os");
var argv = require("yargs").argv;

console.time("Time");
//命令行参数:--port
if (argv.port && /^\d+$/.test(argv.port)) {
  process.env.PORT = argv.port;
}
var port = normalizePort(process.env.PORT || "3000");
var server = http.createServer(app.callback());
server.listen(port);
server.on("error", onError);
server.on("listening", onListening);

//获取本机IPv4
function getIPAdress() {
  var interfaces = os.networkInterfaces();
  for (var devName in interfaces) {
    var iface = interfaces[devName];
    for (var i = 0; i < iface.length; i++) {
      var alias = iface[i];
      if (
        alias.family === "IPv4" &&
        alias.address !== "127.0.0.1" &&
        !alias.internal
      ) {
        return alias.address;
      }
    }
  }
}
//规范化接口格式
function normalizePort(val) {
  var port = parseInt(val, 10);
  if (isNaN(port)) {
    return val;
  }
  if (port >= 0) {
    return port;
  }
  return false;
}

function onError(error) {
  if (error.syscall !== "listen") {
    throw error;
  }
  var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;
  switch (error.code) {
    case "EACCES":
      console.error(bind + " requires elevated privileges");
      process.exit(1);
      break;
    case "EADDRINUSE":
      console.error(bind + " is already in use");
      process.exit(1);
      break;
    default:
      throw error;
  }
}
//
function onListening() {
  console.log("Node running at:");
  console.log("- Local:  ", colors("cyan", `http://localhost:${port}/`));
  console.log("- Network:", colors("cyan", `http://${getIPAdress()}:${port}/`));
  console.timeEnd("Time");
  debug("Listening on " + bind);
}

新增koa.d.ts文件

declare module "colors-console";

新增/middleware/loadRoutes.js文件

var fs = require("fs");
var path = require("path");
const defaultOptions = {
  extname: [".js"], //要加载的文件扩展名,非此扩展名不加载
  root: "routes/", //要遍历的路由文件的根目录
  //不添加命名空间的文件或目录,相对于root的路径,比如['login/'],表示/routes/login/ 下的所有文件
  prefixIgnore: ["main.js"],
};
//要遍历的文件夹所在的路径
const routesDir = path.resolve(defaultOptions.root);
//根据文件路径读取文件,返回文件列表
function loadRoutes(app, dirPath) {
  fs.readdir(dirPath, { withFileTypes: true }, function (err, files) {
    if (err) {
      console.warn(err, "读取文件夹错误!");
    } else {
      //遍历读取到的文件列表
      files.forEach(function (dirent) {
        const currentPath = path.join(dirPath, dirent.name);
        if (dirent.isDirectory()) {
          //文件夹
          loadRoutes(app, currentPath);
        } else if (dirent.isFile()) {
          //文件
          const relativePath = path.relative(__dirname, currentPath);
          const extname = path.extname(relativePath); //文件扩展名
          if (defaultOptions.extname.includes(extname)) {
            const router = require(relativePath);
            const extnameReg = new RegExp(defaultOptions.extname.join("|"));
            const prefixIgnore = defaultOptions.prefixIgnore.some((s) => {
              const ignoeSrc = path.resolve(defaultOptions.root, s);
              return currentPath.indexOf(ignoeSrc) === 0;
            });
            if (!prefixIgnore) {
              //命名空间
              const routerNamespace = path
                .relative(routesDir, currentPath)
                .replace(extnameReg, "");
              router.prefix("/" + routerNamespace); //Note: prefix always should start from / otherwise it won't work.
            }
            app.use(router.routes(), router.allowedMethods());
          }
        }
      });
    }
  });
}

module.exports = function (app, opt = {}) {
  Object.assign(defaultOptions, opt);
  loadRoutes(app, routesDir);
};

app.js文件

const Koa = require("koa");
const app = new Koa();
const json = require("koa-json");
const onerror = require("koa-onerror");
const bodyparser = require("koa-bodyparser");
const logger = require("koa-logger");
const load = require("./middleware/loadRoutes.js");

onerror(app);
// middlewares
app.use(
  bodyparser({
    enableTypes: ["json", "form", "text"],
  })
);
app.use(json());
app.use(logger());
app.use(require("koa-static")(__dirname + "/public"));

// logger
app.use(async (ctx, next) => {
  const start = new Date();
  await next();
  const ms = new Date() - start;
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});
//加载路由
load(app);

// error-handling
app.on("error", (err, ctx) => {
  console.error("server error", err, ctx);
});

module.exports = app;

参考资料:
CSDN:nodejs遍历文件夹下所有文件
百度:查看电脑IP地址的CMD命令是多少?老王教你如何使用,很简单
知乎:node.js express模板初学遇到.address().address为"::"为什么?
CSDN:node.js获取计算机本地ip
CSDN:node环境实现console输出不同颜色
CSDN:ANSI转义代码(ansi escape code)
博客园:nodejs 修改端口号 process.env.PORT(window环境下)
ncc - koa 后台源码加密打包工具 @vercel/ncc - webpack node打包更正规
nodejs 几个方便的打包工具

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/467310.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Apache Zeppelin系列教程第一篇——安装和使用

一、Apache Zeppelin 介绍 Apache Zeppelin是一种开源的Web笔记本类型交互式数据分析工具&#xff0c;它提供了基于浏览器的界面&#xff0c;允许数据工程师和科学家通过各种语言和工具&#xff0c;如Scala, Python, SQL, R,等等&#xff0c;交互式地进行数据分析、可视化以及…

Word控件Spire.Doc 【脚注】字体(2): 在C#, VB.NET中设置Word字体

Spire.Doc for .NET 是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

执行kubectl命令失败server was refused问题解决

在宿主机上执行kubectl 命令时&#xff0c;出现如下错误 [rootroot ~] kubectl get namespaces The connection to the server xxx.xx.xx.xx:6443 was refused - did you specify the right host or port? 然后再网上找寻解决方案&#xff0c;发现各种各样的解决方案都存在。…

API淘宝数据接口

如果你想在自己的应用中使用淘宝的数据&#xff0c;那么对接淘宝数据接口是必不可少的一步。本文将介绍如何对接API淘宝数据接口&#xff0c;以便你能够顺利获取和使用淘宝的数据。 步骤一&#xff1a;​​获取App Key和App Secret​​ 首先&#xff0c;在淘宝开放平台申请AP…

深入浅出:CAN通信之CCP协议

CCP(CAN Calibration Protocol) CAN标定协议(用于标定系统与ECU之间的通信) CCP协议在应用层&#xff0c;使用CAN的数据帧来传输命令 CRO数据帧(主设备想从设备发送) CRO报文&#xff1a;CCP报文帧格式为CMD CTR DATA&#xff0c;即Command、Counter、Data&#xff0c;其中…

opencv cuda版本windows编译

目录 1. 编译准备2. 编译3. 遇到的问题及解决方案3.1 boostdesc_bgm.i,vgg_generated_48.i等文件的缺失3.2 fatal error: features2d/test/test_detectors_regression.impl.hpp: 没有那个文件或目录 1. 编译准备 编译工具是cmakevisual studio2022&#xff0c;首先安装这两个工…

6个优化策略,助你降低K8S成本

Kubernetes 早已成为容器编排引擎的事实标准&#xff0c;而随着 Kubernetes 环境的复杂性持续增长&#xff0c;成本也在不断攀升。CNCF 发布的调查报告《Kubernetes 的 FinOps》显示&#xff0c;68%的受访者表示 Kubernetes 开销正在上涨&#xff0c;并且一半的人所在的组织经历…

WebSocket 详解

WebSocket WebSocket 是一种支持双向通讯网络通信协议。意思就是服务器可以主动向客户端推送信息&#xff0c;客户端也可以主动向服务器发送信息属于服务器推送技术的一种. 特点&#xff1a; &#xff08;1&#xff09;建立在 TCP 协议之上&#xff0c;服务器端的实现比较容…

【MATLAB数据处理实用案例详解(13)】——利用Elman网络实现上证股市开盘价预测

目录 一、问题描述二、Elman网络预测上证股市开盘价原理三、算法步骤3.1 加载数据3.2 构造样本集3.3 划分训练集和测试集3.4 创建Elman神经网络3.5 网络训练3.6 测试 四、结果展示 一、问题描述 选择2005年6月30日至2006年12月1日的上证开盘价进行预测分析。数据保存在elm_sto…

制冷暖通工业互联网平台服务商

制冷暖通工业互联网平台服务商可以提供以下服务&#xff1a; 系统架构设计&#xff1a;根据客户需求&#xff0c;制定适合的系统架构和技术路线&#xff0c;提供全面的技术咨询和方案设计服务。 平台开发和定制化&#xff1a;根据客户需求和系统设计&#xff0c;进行平台开发和…

【go】三色标记-垃圾回收机制

垃圾回收原因 &#xff1a; 垃圾回收是一种内存管理技术&#xff0c;它的主要目的是自动管理程序中的内存分配和释放&#xff0c;以减少内存泄漏和野指针等问题 赋值器与回收器&#xff1a; 赋值器&#xff08;Mutator&#xff09;是指程序中的执行部分&#xff0c;负责创建…

c++11 标准模板(STL)(std::priority_queue)(五)

适配一个容器以提供优先级队列 std::priority_queue 定义于头文件 <queue> template< class T, class Container std::vector<T>, class Compare std::less<typename Container::value_type> > class priority_queue; priority_queu…

docker 安装应用程序

此篇文章以 mysql 安装为例&#xff1a; 一、这里我们拉取官方的最新版本的镜像 docker pull mysql:latest二、使用以下命令来查看是否已安装了 mysql docker images在上图中可以看到我们已经安装了最新版本&#xff08;latest&#xff09;的 mysql 镜像。 三、运行容器 安…

一天学完UCOS III(上)

一、UCOS系统中的一些概念 1.UCOS任务状态 2.任务控制块&#xff08;OS_TCB&#xff09;&#xff1a;保存任务信息结构体 3.任务堆栈&#xff08;CPU_STK&#xff09;&#xff1a;创建任务时传把任务堆栈的参树传入 4.任务优先级&#xff1a;高优先级任务可以抢占低优先级任务…

NDK OpenGL渲染画面效果

NDK系列之OpenGL渲染画面效果技术实战&#xff0c;本节主要是通过OpenGL Java库&#xff08;谷歌对OpenGL C库做了JIN封装&#xff0c;核心实现还是在Native层&#xff09;&#xff0c;实现页面渲染&#xff0c;自定义渲染特效。 实现效果&#xff1a; 实现逻辑&#xff1a; 1…

如何学习FPGA?莱迪斯深力科电子LCMXO2-4000HC-4TG144C MachXO2系列 FPGA可编程逻辑简介

如何学习FPGA&#xff1f;莱迪斯深力科电子LCMXO2-4000HC-4TG144C MachXO2系列 FPGA可编程逻辑简介 FPGA是英文Field&#xff0d;Programmable Gate Array的缩写&#xff0c;即现场可编程门阵列&#xff0c;它是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物。它是作…

【高分论文密码】大尺度空间模拟预测与数字制图

大尺度空间模拟预测和数字制图技术和不确定性分析广泛应用于高分SCI论文之中&#xff0c;号称高分论文密码。大尺度模拟技术可以从不同时空尺度阐明农业生态环境领域的内在机理和时空变化规律&#xff0c;又可以为复杂的机理过程模型大尺度模拟提供技术基础。在本次培训中&…

查询优化之单表查询

建表 CREATE TABLE IF NOT EXISTS article ( id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, author_id INT(10) UNSIGNED NOT NULL, category_id INT(10) UNSIGNED NOT NULL, views INT(10) UNSIGNED NOT NULL, comments INT(10) UNSIGNED NOT NULL, title VARBI…

Ajax和Json综合案例

1. 查询所有 创建brand.html,使用axios发送请求&#xff0c;其中查询一般采用get的请求方式 <script src"js/axios-0.18.0.js"></script><script>//1. 当页面加载完成后&#xff0c;发送ajax请求window.onload function () {//2. 发送ajax请求axi…

星辰天合亮相 2023 国际金融展,软件定义存储信创方案备受瞩目

4 月 25 日&#xff0c;为期三天的 2023 中国国际金融展在北京首钢会展中心开幕&#xff0c;本届展会以“荟萃金融科技成果&#xff0c;展现数字金融力量&#xff0c;谱写金融服务中国式现代化新篇章”为主题&#xff0c;全面展示金融业为完善科技创新体系、加快实现高水平科技…