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

本文主要是介绍vue+Nodejs+Koa搭建前后端系统(三)--koa-generator项目优化修改,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

  • 计算机系统为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或IPv6netmask: '',//指定IPv4或IPv6网络掩码family: 'IPv6',//值为IPv4或IPv6之一mac: '',//指定网络接口的MAC地址internal: false,//如果接口为环回一则为true,否则为falsecidr: '',//用于指定分配的IPv4或IPv6地址以及CIDR表示法中的路由前缀。如果网络掩码无效,则将其设置为nullscopeid: 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 pipereturn val;}if (port >= 0) {// port numberreturn 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()方法做成插件:新建 /plugins/ 目录,用于存放插件文件。同时可以把添加命名空间的代码也移到这个插件中,而不用分散到各个路由文件中。

/plugins/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("./plugins/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 几个方便的打包工具

这篇关于vue+Nodejs+Koa搭建前后端系统(三)--koa-generator项目优化修改的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/357580

相关文章

vue基于ElementUI动态设置表格高度的3种方法

《vue基于ElementUI动态设置表格高度的3种方法》ElementUI+vue动态设置表格高度的几种方法,抛砖引玉,还有其它方法动态设置表格高度,大家可以开动脑筋... 方法一、css + js的形式这个方法需要在表格外层设置一个div,原理是将表格的高度设置成外层div的高度,所以外层的div需要

5分钟获取deepseek api并搭建简易问答应用

《5分钟获取deepseekapi并搭建简易问答应用》本文主要介绍了5分钟获取deepseekapi并搭建简易问答应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需... 目录1、获取api2、获取base_url和chat_model3、配置模型参数方法一:终端中临时将加

IDEA运行spring项目时,控制台未出现的解决方案

《IDEA运行spring项目时,控制台未出现的解决方案》文章总结了在使用IDEA运行代码时,控制台未出现的问题和解决方案,问题可能是由于点击图标或重启IDEA后控制台仍未显示,解决方案提供了解决方法... 目录问题分析解决方案总结问题js使用IDEA,点击运行按钮,运行结束,但控制台未出现http://

解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题

《解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题》文章详细描述了在使用lombok的@Data注解标注实体类时遇到编译无误但运行时报错的问题,分析... 目录问题分析问题解决方案步骤一步骤二步骤三总结问题使用lombok注解@Data标注实体类,编译时

C语言小项目实战之通讯录功能

《C语言小项目实战之通讯录功能》:本文主要介绍如何设计和实现一个简单的通讯录管理系统,包括联系人信息的存储、增加、删除、查找、修改和排序等功能,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录功能介绍:添加联系人模块显示联系人模块删除联系人模块查找联系人模块修改联系人模块排序联系人模块源代码如下

SpringBoot项目中Maven剔除无用Jar引用的最佳实践

《SpringBoot项目中Maven剔除无用Jar引用的最佳实践》在SpringBoot项目开发中,Maven是最常用的构建工具之一,通过Maven,我们可以轻松地管理项目所需的依赖,而,... 目录1、引言2、Maven 依赖管理的基础概念2.1 什么是 Maven 依赖2.2 Maven 的依赖传递机

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

详解如何在React中执行条件渲染

《详解如何在React中执行条件渲染》在现代Web开发中,React作为一种流行的JavaScript库,为开发者提供了一种高效构建用户界面的方式,条件渲染是React中的一个关键概念,本文将深入探讨... 目录引言什么是条件渲染?基础示例使用逻辑与运算符(&&)使用条件语句列表中的条件渲染总结引言在现代

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI