搭建一个Koa后端项目脚手架的方法步骤
一、背景
结合当前的node比较火的三大框架,express、koa、egg。笔者之前用的express,后面发现回调把我搞死了,实在太无奈了。终于有一天去尝试了koa来进行开发,发现实在太舒服了。es6语法支持很多,同步模式也很到位,但是在学习koa的过程中,发现基本的基础知识都了解了,也按照官方的文档走了一遍,但发现好像无从下手。感觉开发过程中,分层不太明显,业务逻辑简单还好,一多麻烦就来了。查看了资料后,有一个koa的脚手架叫做 koa-generator
,马上尝试后发现不是我想要的模板。看来github已经有2年没有维护了,koa2一些新特性也没有加上,感觉有点快落伍了。于是结合其他人的模式,也避免后面自己过多的重复造*。编写一个koa项目的初始模板。这个模板主要的功能集成了logger、router、jwt、mongoose、redis、pm2等模块,还有部分的中间件集合,该模板对于简单的后台项目来说基本够用了,没有考虑高并发处理,后期会继续完善。对于初学者来说,可以快速的新项目开发,在开始之前先好好看下面的解读。
二、目录结构
下面的目录是该模板基础目录结构,后面的章节会对每一个目录的配置进行介绍,让大家在开发中对项目的结构比较清晰,出了问题容易定位。
├─.gitignore // 忽略文件配置 ├─app.js // 应用入口 ├─config.js // 公共配置文件 ├─ecosystem.config.js // pm2配置文件 ├─package.json // 依赖文件配置 ├─readme.md // readme.md文档 ├─routes // 路由 | ├─private.js // 校验接口 | └public.js // 公开接口 ├─models // 数据库配置及模型 | ├─index.js // 数据库配置 | └user.js // 用户的schema文件 ├─middlewares // 中间件 | ├─cors.js // 跨域中间件 | ├─jwt.js // jwt中间件 | ├─logger.js // 日志打印中间件 | └response.js // 响应及异常处理中间件 ├─logs // 日志目录 | ├─koa-template.log | └koa-template.log-2019-05-28 ├─lib // 工具库 | ├─error.js // 异常处理 | └mongodb.js // mongodb配置 ├─controllers // 操作数据库及业务逻辑 | ├─index.js // 配置 | ├─login.js // 登录 | └test.js // 测试 ├─bin // 启动目录 | └www // 启动文件配置
bin文件
bin文件目录中,只有一个文件,即为www,因为我们后端的项目基本上是在linux上进行运行的,其实我们不必去担心文件的后缀是什么,只需知道该文件是可执行文件还是不可执行文件就行了。这个文件有什么用呢?其实我们这个文件是用来部署的时候可以启动我们一整个后端程序,也就是我们前端中的集成的运行环境。我们的运行、关闭、重启都在这文件进行即可。基本代码如下:
#!/usr/bin/env node /** * module dependencies. */ const app = require('../app') const http = require('http') const config = require('../config') /** * get port from environment and store in express. */ const port = normalizeport(process.env.port || config.port) // app.set('port', port); /** * create http server. */ const server = http.createserver(app.callback()) /** * listen on provided port, on all network interfaces. */ server.listen(port) server.on('error', onerror) server.on('listening', onlistening) /** * normalize a port into a number, string, or false. */ function normalizeport(val) { const port = parseint(val, 10) if (isnan(port)) { // named pipe return val } if (port >= 0) { // port number return port } return false } /** * event listener for http server "error" event. */ function onerror(error) { if (error.syscall !== 'listen') { throw error } const bind = typeof port === 'string' ? 'pipe ' + port : 'port ' + port // handle specific listen errors with friendly messages 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 } } /** * event listener for http server "listening" event. */ function onlistening() { const addr = server.address() const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port console.log('listening on ' + bind) }
相信用过koa-generator对这个代码斌并不陌生,这其实就是他里面的代码,express项目的www文件也基本差不多。还是希望大家可以把这里面的代码过一遍,它的基本思路就是利用了node.js中的http模块,让http暴露你的端口并进行监听,这个端口是在配置文件config.js中引入的。
app.js
先简单的看一下代码
'use strict' const koa = require('koa') const bodyparser = require('koa-bodyparser')() const staticcache = require('koa-static-cache') const config = require('./config') const publicrouter = require('./routes/public') const privaterouter = require('./routes/private') const { loggermiddleware } = require('./middlewares/logger') const { errorhandler, responsehandler } = require('./middlewares/response') const app = new koa() // logger app.use(loggermiddleware) // error handler app.use(errorhandler) // global middlewares app.use(bodyparser) app.use(staticcache(config.publicdir)) // routes app.use(publicrouter.routes(), publicrouter.allowedmethods()) app.use(privaterouter.routes(), privaterouter.allowedmethods()) // response app.use(responsehandler) module.exports = app
这个文件中,我们可以看到较多的中间件,中间件的执行顺序是从外到内,再从内到外,也就是洋葱模式。如果还不大了解中间的小伙伴可以去查找相关资料。中间件的执行过程是依靠 app.use()
进行传递的,你可以简单的理解为自己编写的函数,依次去执行即可。每一个中间件会在app调用是传入2个参数,分别为: ctx
和 next
ctx:
koa context 将 node 的 request 和 response 对象封装在一个单独的对象里面,其为编写 web 应用和 api 提供了很多有用的方法。
这些操作在http服务器开发中经常使用,因此其被添加在上下文这一层,而不是更高层框架中,因此将迫使中间件需要重新实现这些常用方法。
next:
下一个中间件函数,也就是每一个中间件如果要往下走必须写上这个,否则无法执行。
可以理解为前端的vue-router中的路由守卫中的next(), 执行下一步或者进行传参。
middlewares
在这个项目主要用到了几个中间件,一个是 logger.js
、 response.js
和 jwt.js
等其他中间件。
logger.js
大家可以想一下,如果我们项目在开发中,或者上线了,我们要看我们执行的日志或者请求的参数以及报错等信息,如果没有再每一个请求中体现出来,那么遇到问题我们会很难定位到是前端的问题还是后端。而logger这个中间件就是用来对这些情况进行处理的,原来的koa模板中,只是简单的进行log的打印而已,这个中间件是用了log4js模块进行封装的。详细使用方法查看官方文档,这个中间件会在控制台或者日志中打印出固定的格式,http请求方法、返回状态、请求url、ip地址、请求时间等,而且我们也可以很好的利用log4js中的配置,来打印出自定义的日志。可以代替 console.log()
使用,在使用这个中间件的时候,必须放在第一个中间件,才能保证所以的请求及操作会先经过logger进行记录再到下一个中间件。其代码如下:
'use strict' const fs = require('fs') const path = require('path') const log4js = require('log4js') const config = require('../config') const logsdir = path.parse(config.logpath).dir if (!fs.existssync(logsdir)) { fs.mkdirsync(logsdir) } log4js.configure({ appenders: { console: { type: 'console' }, datefile: { type: 'datefile', filename: config.logpath, pattern: '-yyyy-mm-dd' } }, categories: { default: { appenders: ['console', 'datefile'], level: 'info' } } }) const logger = log4js.getlogger('[default]') const loggermiddleware = async (ctx, next) => { const start = new date() await next() const ms = new date() - start const remoteaddress = ctx.headers['x-forwarded-for'] || ctx.ip || ctx.ips || (ctx.socket && (ctx.socket.remoteaddress || (ctx.socket.socket && ctx.socket.socket.remoteaddress))) let logtext = `${ctx.method} ${ctx.status} ${ctx.url} 请求参数: ${json.stringify(ctx.request.body)} 响应参数: ${json.stringify(ctx.body)} - ${remoteaddress} - ${ms}ms` logger.info(logtext) } module.exports = { logger, loggermiddleware }
response.js
这个中间件主要是用来对返回前端的响应进行处理,基础的koa模板中,我们可以用 ctx.body
进行返回前端,但是发现有些东西经常重复写,还不如提出来进行封装,而且还不用担心返回的格式会不一致。 先看看代码:
'use strict' const { logger } = require('./logger') // 这个middleware用于将ctx.result中的内容最终回传给客户端 // 回传的格式遵循这样的格式:{ code: 0, msg: any data: any } const responsehandler = (ctx) => { if (ctx.result !== undefined) { ctx.type = 'json' ctx.body = { code: 200, msg: ctx.msg || '', data: ctx.result } } } // 这个middleware处理在其它middleware中出现的异常 // 并将异常消息回传给客户端:{ code: '错误代码', msg: '错误信息' } const errorhandler = (ctx, next) => { return next().catch(err => { if (err.code == null) { logger.error(err.stack) } ctx.body = { code: err.code || -1, data: null, msg: err.message.trim() } ctx.status = 200 // 保证返回状态是 200, 这样前端不会抛出异常 return promise.resolve() }) } module.exports = { responsehandler, errorhandler }
代码的后面会暴露出 responsehandler
和 errorhandler
, responsehandler
正确响应,我们在业务中,只需要对 ctx.result
进行写入即可。这个中间件可以放在所有中间件的最后面,这样可以保证前面中间件都需要经过它,再返回前端。 errorhandler
错误响应,这个主要是用来进行出错或者异常的捕获,可以返回响应给前端,要不前端会出现一直padding的状态直到超时。
jwt.js
'use strict' const koajwt = require('koa-jwt') const jwt = require('jsonwebtoken') const config = require('../config') const jwtmiddleware = koajwt({ secret: config.secret }) module.exports = function (ctx, next) { // 将 token 中的数据解密后存到 ctx 中 try { if (typeof ctx.request.headers.authorization === 'string') { const token = ctx.request.headers.authorization.slice(7) ctx.jwtdata = jwt.verify(token, config.secret) } else { throw {code: 401, message: 'no authorization'} } } catch (err) { throw {code: 401, message: err.message} } next() }
其实这个中间件是对 koa-jwt
进行封装的,jwt我们用来生成token,用来判断用户的唯一性,每次登录后返回前端,前端每一个需要鉴权的api都需要进行token验证,我们利用了 koa-jwt
进行token的生成,但是怎样才能在每一个接口中获取到token解析后的用户呢。这个中间件就起到很大的关键作用。会结合在需要鉴权的 router
中,验证通过后保存信息到ctx中,可以供全局使用。
cors.js
在前后端接口请求中,由于浏览器的限制,会出现跨域的情况。常用的跨域方案有:
1、jsonp跨域
2、nginx反向代理
3、服务器端修改heade
4、document.domain
5、window.name
6、postmessage
7、后台配置运行跨域
koa中如何设置跨域
先看看koa中如何设置跨域,cors具体的实现过程,具体的详细介绍,已经在代码中进行注释了。先看一下原生的配置,后面直接使用中间件即可,不过还是需要了解一下具体实现方式,万一出了问题,能快熟的排查。
app.use(async (ctx, next) => { // 允许来自所有域名请求 ctx.set("access-control-allow-origin", "*"); // 这样就能只允许 http://localhost:8080 这个域名的请求了 // ctx.set("access-control-allow-origin", "http://localhost:8080"); // 设置所允许的http请求方法 ctx.set("access-control-allow-methods", "options, get, put, post, delete"); // 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段. ctx.set("access-control-allow-headers", "x-requested-with, accept, origin, content-type"); // 服务器收到请求以后,检查了origin、access-control-request-method和access-control-request-headers字段以后,确认允许跨源请求,就可以做出回应。 // content-type表示具体请求中的媒体类型信息 ctx.set("content-type", "application/json;charset=utf-8"); // 该字段可选。它的值是一个布尔值,表示是否允许发送cookie。默认情况下,cookie不包括在cors请求之中。 // 当设置成允许请求携带cookie时,需要保证"access-control-allow-origin"是服务器有的域名,而不能是"*"; ctx.set("access-control-allow-credentials", true); // 该字段可选,用来指定本次预检请求的有效期,单位为秒。 // 当请求方法是put或delete等特殊方法或者content-type字段的类型是application/json时,服务器会提前发送一次请求进行验证 // 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证 ctx.set("access-control-max-age", 300); /* cors请求时,xmlhttprequest对象的getresponseheader()方法只能拿到6个基本字段: cache-control、 content-language、 content-type、 expires、 last-modified、 pragma。 */ // 需要获取其他字段时,使用access-control-expose-headers, // getresponseheader('mydata')可以返回我们所需的值 //https://www.rails365.net/articles/cors-jin-jie-expose-headers-wu ctx.set("access-control-expose-headers", "mydata"); await next(); })
相对用得较多是的大神封装好得koa-cors中间件,可以自行查看npm上得文档,在这个项目中用的就是koa-cors的中间件,基本的配置写在cors.js里面了,再通过中间件进行引用。注意要写在router前面,避免在没有进行跨域配置前就去请求接口。
app.js中的引用,记得安装引入koa-cors
// cors app.use(cors(corshandler))
'use strict' const corshandler = { origin: function (ctx) { if (ctx.url === '/test') { // 这里可以配置不运行跨域的接口地址 return false; } return '*'; }, exposeheaders: ['www-authenticate', 'server-authorization'], maxage: 5, credentials: true, allowmethods: ['get', 'post', 'delete'], allowheaders: ['content-type', 'authorization', 'accept'], } module.exports = { corshandler }
koa-helmet 中间件
koa-helmet 可以帮助你的 app 抵御一些比较常见的安全 web 安全隐患,它其实是将 9 个安全中间件集中到了一起,做了合并,大部分都是对于 http header 的操作,下图为默认开启的功能。
在项目中使用先安装该中间件, npm i koa-helmet --save
,该项目中直接引用默认配置即可,如果有需要,可以看官方文档自己进行配置。
const helmet = require("koa-helmet") // helmet app.use(helmet())
其他中间件
koa的中间件可以说很多大神给我们做好了*,我们直接可以拿来用就行,例如: bodyparser
、 koa-session
、将将中间件转换成koa2可以使用的中间件 koa-convert
、ejs模板使用 koa-ejs
,大家根据自己需要进行引用,由于是基础模板,暂时没有加上过多中间件,减少体积。
lib文件
这个文件夹主要是用来做存放工具类的文件夹,一些全局的工具处理文件可以放到这边来,目前这个项目中只有2个文件, error.js
和 mongodb.js
error.js
中主要是在中间件中抛出异常,由于前面我们已经加入了异常捕获的中间件,在中间件操作过程中,如果有错误,我们可以直接抛出异常,这个方法就是为了方便我们配置所用的。文件中的方法是 codederror
方法继承了error, forbiddenerror
和 invalidqueryerror
是继承了 codederror
,记得在使用的时候得实例化一下该构造函数。如果小伙伴对es6的继承还不熟悉,可以先看一下文档再来看该工具类。
'use strict' class codederror extends error { constructor (message = '未知错误', code = -1) { super(message) this.code = code } } module.exports = { codederror, /** * 拒绝访问构造函数 */ forbiddenerror: class forbiddenerror extends codederror { constructor (message = '拒绝访问') { super(message, 403) } }, /** * 无效的参数构造函数 */ invalidqueryerror: class invalidqueryerror extends codederror { constructor (message = '无效的参数') { super(message, 400) } } }
mongodb.js
文件是对mongodb的链接配置,后续在models中会讲到。
models文件
该项目中是使用 mongoose
对 mongodb
对数据库进行操作, mongoose
语法简单,需要过多的学习成本。按照官方文档的配置以及api操作,即可对 mongobd
进行灵活性存储。 mongoose
的配置包括三大部分: connect
、 models
和 schema
connect
:用于创建数据库连接及监听
schema
:schema主要用于定义mongodb中集合collection里文档document的结构,可以理解为mongoose对表结构的定义(不仅仅可以定义文档的结构和属性,还可以定义文档的实例方法、静态模型方法、复合索引等),每个schema会映射到mongodb中的一个collection,schema不具备操作数据库的能力,简单理解是对字段的定义,操作数据库必须按照这些字段进行,否在会报错。
models
: model是由schema编译而成的假想(fancy)构造器,具有抽象属性和行为。model的每一个实例(instance)就是一个document,document可以保存到数据库和对数据库进行操作。简单说就是model是由schema生成的模型,可以对数据库的操作。
在我们项目中,我们把它全局集合在models文件中进行配置。 index.js
文件里面操作了 connect
、 models
这两个步骤。先看代码:
const fs = require('fs'); const path = require('path'); const mongoose = require('mongoose'); //引用mongoose模块 const config = require('../config') const { logger } = require('../middlewares/logger') let url = "mongodb://" + config.mongodb.host + ":" + config.mongodb.port + "/" + config.mongodb.database; var mongo = mongoose.createconnection(url); //创建一个数据库连接 let db = { mongoose: mongoose, mongo: mongo, models: {} }; // 错误 mongo.on('error', function (err) { logger.error(new error(err)); }); // 开启 mongo.once('open', function () { logger.info("mongo is opened"); }); // 整合models文件下的其他js文件 fs.readdirsync(__dirname) .filter(function (file) { return (file.indexof(".") !== 0) && (file !== "index.js"); }).foreach(function (file) { var modelfile = require(path.join(__dirname, file)); var schema = new mongoose.schema(modelfile.schema); db.models[modelfile.name] = mongo.model(modelfile.name, schema, modelfile.name); }); // 根据name选择model db.getmodel = function (name) { return this.models[name]; }; module.exports = db;
代码中的链接部分一看基本就明白了,可是 models
部分怎么看不出所以然。其实是模块化开发的一部分,这里是为了整合models文件下的其他js文件,方便开发者使用,不用每写一个文件就要进行引入和导出。
初始情况下, models
引入只需 mongoose.model('名称', schema);
并将其暴露出去,即可对数据库进行操作。
fs.readdirsync(__dirname) .filter(function (file) { return (file.indexof(".") !== 0) && (file !== "index.js"); }).foreach(function (file) { var modelfile = require(path.join(__dirname, file)); var schema = new mongoose.schema(modelfile.schema); db.models[modelfile.name] = mongo.model(modelfile.name, schema, modelfile.name); });
在这个文件内,我们做了这样一件事:读取 models 目录下所有文件名不为 index.js 且以 .js 为后缀名的文件,使用 require 进行引用,并将其整合为一个 schema对象后再引入到models并且暴露出去给操作数据库。这样子做的好处是,在项目越来越庞大以后,如果我们需要添加新的 schema ,只需直接在 models 目录下新建 .js 文件即可,则不用再进行引入的关系操作
由于有了上一步的操作,我们后面直接新增一个schema的配置文件即可。index.js会自动的引入并暴露出model
'use strict' module.exports = { name: "user", schema: { uuid: string, // uuid username: string, // 用户名 password: string, // 密码 } };
我们使用时可以这样操作,
const user = require('../models/index').getmodel('user') const user = await user.findone({username: username})
pm2配置
pm2是可以用于生产环境的nodejs的进程管理工具,并且它内置一个负载均衡。它不仅可以保证服务不会中断一直在线,并且提供0秒reload功能,还有其他一系列进程管理、监控功能。并且使用起来非常简单。pm2的官方文档已经进行详细的配置说明,在这里就不进行一一简述,主要讲的时我的koa项目怎样配合pm2进行相关管理或者说部署。pm2常用命令需要用的时候可以进行查看,没必要去背,用多就熟悉了。也可以结合在package.json里面,用自定义命令运行。我们在 package.json
的 script
配置和初始化文件 ecosystem.config.js
进行了多环境运行的配置,我们可以根据需要进行切换环境。
package.json
文件添加如下:
"scripts": { "start": "node ./bin/www", "dev": "pm2 start ecosystem.config.js --env dev", "test": "pm2 start ecosystem.config.js --env test", "pro": "pm2 start ecosystem.config.js --env pro", "logs": "pm2 logs", "stop": "pm2 stop ecosystem.config.js" },
其中的
npm run start: 直接跑www文件,可用于调试
npm run dev: 开发环境
npm run test:测试环境
npm run pro:生产环境
npm run logs: 查看pm2的日志
npm run stop: 停止pm2服务
新增 ecosystem.config.js
文件:
module.exports = { apps : [{ name: 'api', script: './bin/www', // options reference: https://pm2.io/doc/en/runtime/reference/ecosystem-file/ args: 'one two', instances: 1, autorestart: true, watch: true, ignore_watch: [ // 不用监听的文件 'node_modules', 'logs' ], max_memory_restart: '1g', env_pro: { "node_env": "production", "remote_addr": "" }, env_dev: { "node_env": "development", "remote_addr": "" }, env_test: { "node_env": "test", "remote_addr": "" } }] };
这个文件主要是对pm2的基本配置,不用每次都进行配置,直接在文件进行改动即可。我们需要关注的是可以在 env
中,增加我们需要的环境及变量即可,文件中的 watch
属性是可以配置监听文件改动后,自动重启项目,比较好用。如果想忽略某一个文件夹的变动可以 ignore_watch
,更多的配置如果有兴趣的小伙伴可以查看官方文档的文档说明。
路由配置
该目录下存放路由基本配置,有 private
和 public
两个文件,引入路由后,我们对其前缀做了处理, router.prefix('/api')
在每一个请求的时候都需要带上这个前缀,抽出来也是为了服务目录的改变,可以直接更改即可,做了全局的操作。每一个路由都必须暴露出去,这样在app.js文件中使用该中间件。 publicrouter.allowedmethods()
根据 ctx.status
设置 response
响应头
// routes app.use(publicrouter.routes(), publicrouter.allowedmethods()) app.use(privaterouter.routes(), privaterouter.allowedmethods())
private
:该文件下的路由是需要通过jwt验证的,才能进行访问。前面我们做了jwt的中间件,我们直接引入即可 router.use(jwtmiddleware)
记得要放在请求路由的前面,才能保证每次都经过它。
'use strict' const router = require('koa-router') const controllers = require('../controllers') const jwtmiddleware = require('../middlewares/jwt') const router = new router() router.prefix('/api') router.use(jwtmiddleware) router.get('/test', controllers.test.test) module.exports = router
public
:该文件与上面相反,主要用来不进行登录的校验,也就是我们常用的登录、注册等不需要验证的接口。
'use strict' const router = require('koa-router') const controllers = require('../controllers') const router = new router() router.prefix('/api') router.post('/login', controllers.login.login) module.exports = router
为什么我们没在这里处理业务逻辑呢?其实这里是遵循了mvc的思想,进行了分离。把数据库的操作放到了controllers文件中。这如果我们接口一多,不会显示得特别混乱。下面我们就来讲这个文件。
controllers文件
为了让整个项目更为模块化,该目录下主要是处理对应的路由的回调函数,一般我们不会在router文件中去操作数据库和逻辑操作等步骤,这里采用 routes 和 controller 分开,在方便代码的查看同时,也方便代码的维护和开发。
index.js文件:
该文件与models中的index.js文件中的集合该目录下的文件类似,这里是将其他文件导出统一到index暴露出去。
'use strict' const fs = require('fs') const files = fs.readdirsync(__dirname).filter(file => file !== 'index.js') const controllers = {} for (const file of files) { if (file.tolowercase().endswith('js')) { const controller = require(`./${file}`) controllers[`${file.replace(/\.js/, '')}`] = controller } } module.exports = controllers
其他文件的编写可以按照下面基本框架进行,在这里会用到前面封装好的业务,例如数据库操作、响应、jwt等操作。大家可以认真看以下代码分析一下。
'use strict' const jwt = require('jsonwebtoken') const config = require('../config') const user = require('../models/index').getmodel('user') const login = {} login.login = async (ctx, next) => { const {username, password} = ctx.request.body const user = await user.findone({username: username}) if (!user) { ctx.result = '' ctx.msg = '用户不存在' } else { ctx.result = jwt.sign({ data: user._id, // 设置 token 过期时间 exp: math.floor(date.now() / 1000) + (60 * 60), // 60 seconds * 60 minutes = 1 hour }, config.secret) } return next() } module.exports = login
config.js文件
该文件主要用来存放全局的配置,如果一个项目中没有全局的配置,那么一个地方改动牵动的其他地方很多,这样很不利于工作效率,在开发过程中,我们一般会把常用的都放在这个文件,例如:数据库参数,端口,密钥,全局变量等。看自己的需求适当的更改。该文件将变量进行了暴露,引用时进行require即可。
'use strict' const path = require('path') module.exports = { port: '3001', secret: 'secret', publicdir: path.resolve(__dirname, './public'), logpath: path.resolve(__dirname, './logs/koa-template.log'), mongodb: { database: 'mall', username: 'root', password: 'root', host: '127.0.0.1', port: 27017 } }
package.json文件
每个nodejs项目的根目录下面,一般都会有一个package.json文件。该文件可以由npm init生成,定义了项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。 package.json文件内部就是一个json对象,该对象的每一个成员就是当前项目的一项设置。我们也可在里面配置我们的 npm run xxx
的命令,大家可以根据需求进行配置。这是这项目需要用到的package.json文件。
{ "name": "koa-template", "version": "0.1.0", "author": "bayi", "private": true, "scripts": { "start": "node ./bin/www", "dev": "pm2 start ecosystem.config.js --env dev", "test": "pm2 start ecosystem.config.js --env test", "pro": "pm2 start ecosystem.config.js --env pro", "logs": "pm2 logs", "stop": "pm2 stop ecosystem.config.js" }, "dependencies": { "koa": "^2.6.2", "koa-bodyparser": "^4.2.1", "koa-helmet": "^4.1.0", "koa-jwt": "^3.5.1", "koa-router": "^7.4.0", "koa-static-cache": "^5.1.2", "koa2-cors": "^2.0.6", "log4js": "^3.0.6", "mongoose": "^5.5.5" } }
三、其他
####github地址:
(star! star!star!)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 浅谈Express.js解析Post数据类型的正确姿势
下一篇: golang json性能分析详解
推荐阅读