当前位置:   article > 正文

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

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

前言

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

修改package.json配置

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

npm run dev
  • 1

可能会报错

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

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

"dev": "nodemon bin/www",
  • 1

修改之后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");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

命令行颜色输出

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

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

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

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

麻!拆开来看:
%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
  • 1

/bin/www.js:

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

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
  • 1

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

declare module "colors-console";
  • 1

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

0x1B + "[" + <params> + <fn>
  • 1
  • <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
  • 1

即可。
在这里插入图片描述
其中的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;
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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

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
	    }
	  ]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

设置服务器端口

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

var port = normalizePort(process.env.PORT || "3000");
var server = http.createServer(app.callback());
server.listen(port);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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'
  • 1

删除process.env的属性:

delete process.env.PORT
  • 1

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

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

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

{
  "name": "server2",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "set PORT=5252 && nodemon bin/www localhost"
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

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

命令行参数

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

{
	"scripts": {
    	"start": "node bin/www --param1 --param2=5152"
  	}
}
  • 1
  • 2
  • 3
  • 4
  • 5

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

console.log(process.argv);
  • 1

启动服务器

npm run start
  • 1

会得到如下输出:

[
  'E:\\Node\\install\\node.exe',
  'F:\\test\\vue_node\\hello-node\\server2\\bin\\www',
  '--param1',
  '--param2=5152'
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

  • 数组的第一个元素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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

或者安装Yargs插件

npm i yargs@6.0.0
  • 1

使用方法:

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

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

package.json

"scripts": {
    "start": "node bin/www"
},
  • 1
  • 2
  • 3

在终端执行:

npm run start -- --port=5152
  • 1

相当于执行:

node bin/www --port=5152
  • 1

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

"scripts": {
    "start": "node bin/www --port=5153"
},
  • 1
  • 2
  • 3

在终端执行:

npm run start -- --port=5152
  • 1

相当于执行:

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

process.argv的值为:

[
  'E:\\Node\\install\\node.exe',
  'F:\\test\\vue_node\\hello-node\\server2\\bin\\www',
  '--port=5153',
  '--port=5152'
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

yargs插件的返回值为:

{
	port: [ 5153, 5152 ]
}
  • 1
  • 2
  • 3

修改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());
        }
      });
    }
  });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 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'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 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'
    
    • 1
    • 2
    • 3
    • 4
  • 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系统返回反斜杠 \ )
    
    • 1
    • 2
  • __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";
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

自定义插件

可以把 路由目录自动加载 的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);
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

app.js

const load = require("./plugins/loadRoutes.js");
//加载全部路由
load(app);
  • 1
  • 2
  • 3

webpack打包

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

npm i @vercel/ncc
  • 1

打包命令:

ncc build app.js -m -o dist
  • 1
  • -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();
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

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

有个小插曲:我在百度查 打包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"
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

/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);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

新增koa.d.ts文件

declare module "colors-console";
  • 1

新增/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);
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

参考资料:
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 几个方便的打包工具

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/107817
推荐阅读
相关标签
  

闽ICP备14008679号