沪江CCtalk视频地址:https://www.cctalk.com/v/15114923889450

规范与部署

懒人推动社会进步。

本篇中,我们会讲述三个知识点

  • 定制书写规范
  • 开发环境运行
  • 如何部署运行

定制书写规范

文中所说的书写规范,仅供参考,非项目必需。

随着 Node 流行,JavaScript 编码规范已经相当成熟,社区也产生了各种各样的编码规范。但是在这里,我们要做的不是『限制空格的数量』,也不是『要不要加分号』。我们想要说的规范,是项目结构的规范。

目前我们的项目结构如下:

├─ controller/          // 用于解析用户的输入,处理后返回相应的结果
├─ service/ // 用于编写业务逻辑层,比如连接数据库,调用第三方接口等
├─ errorPage/ // http 请求错误时候,对应的错误响应页面
├─ logs/ // 项目运用中产生的日志数据
├─ middleware/ // 中间件集中地,用于编写中间件,并集中调用
│ ├─ mi-http-error/
│ ├─ mi-log/
│ ├─ mi-send/
│ └── index.js
├─ public/ // 用于放置静态资源
├─ views/ // 用于放置模板文件,返回客户端的视图层
├─ router.js // 配置 URL 路由规则
└─ app.js // 用于自定义启动时的初始化工作,比如启动 https,调用中间件,启动路由等

当架构师准备好项目结构后,开发人员只需要修改业务层面的代码即可,比如当我们增加一个业务场景时候,我们大概需要修改三个地方:

  1. service/ 目录下新建文件,处理逻辑层的业务代码,并返回给 controller
  2. controller/ 目录下新建文件,简单处理下请求数据后,传递给 service
  3. 修改路由文件 router.js,增加路由对应的处理器

随着业务量的增大,我们就会发现有一个重复性的操作——『不断的 require 文件,不断的解析文件中的函数』。当业务量达到一定程度时候,可能一个文件里面要额外引入十几个外部文件:

const controller1 = require('...')
const controller2 = require('...')
const controller3 = require('...')
const controller4 = require('...')
...
app.get('/fn1', controller1.fn1() )
app.get('/fn2', controller2.fn2() )
app.get('/fn3', controller3.fn3() )
app.get('/fn4', controller4.fn4() )

单是起名字就已经够头疼的!

所以,我们要做的事情就是,约定代码结构规范,省去这些头疼的事情,比如 router.js

// const router = require('koa-router')()
// const HomeController = require('./controller/home')
// module.exports = (app) => {
// router.get( '/', HomeController.index )
// router.get('/home', HomeController.home)
// router.get('/home/:id/:name', HomeController.homeParams)
// router.get('/user', HomeController.login)
// router.post('/user/register', HomeController.register)
// app.use(router.routes())
// .use(router.allowedMethods())
// }
const router = require('koa-router')()
module.exports = (app) => {
router.get( '/', app.controller.home.index )
router.get('/home', app.controller.home.home)
router.get('/home/:id/:name', app.controller.home.homeParams)
router.get('/user', app.controller.home.login)
router.post('/user/register', app.controller.home.register)
app.use(router.routes())
.use(router.allowedMethods())
}

聪明的同学可能已经发现了,app.controller.home.index 其实就是 cotroller/home.js 中的 index 函数。

设计思路

实现思路很简单,当应用程序启动时候,读取指定目录下的 js 文件,以文件名作为属性名,挂载在实例 app 上,然后把文件中的接口函数,扩展到文件对象上。

一般有两种方式入手,一种是程序启动时候去执行,另外一种是请求过来时候再去读取。

而在传统书写方式中,项目启动时候会根据 require 加载指定目录文件,然后缓存起来,其思路与第一种方式一致。如果以中间件的方式,在请求过来时候再去读取,则第一次读取肯定会相对慢一起。综合考虑,我们采用了第一种方式:程序启动时候读取。

代码实现

新建目录文件 middleware/mi-rule/index.js, 实现代码如下:

const Path = require("path");
const fs = require('fs');
module.exports = function (opts) {
let { app, rules = []} = opts // 如果参数缺少实例 app,则抛出错误
if (!app) {
throw new Error("the app params is necessary!")
}
// 提取出 app 实例对象中的属性名
const appKeys = Object.keys(app)
rules.forEach((item) => {
let { path, name} = item
// 如果 app 实例中已经存在了传入过来的属性名,则抛出错误
if (appKeys.includes(name)) {
throw new Error(`the name of ${name} already exists!`)
}
let content = {};
//读取指定文件夹下(dir)的所有文件并遍历
fs.readdirSync(path).forEach(filename => {
//取出文件的后缀
let extname = Path.extname(filename);
//只处理js文件
if (extname === '.js') {
//将文件名中去掉后缀
let name = Path.basename(filename, extname);
//读取文件中的内容并赋值绑定
content[name] = require(Path.join(path, filename));
}
});
app[name] = content
})
}

opts 是参数对象,里面包含了实例 app,用来挂载指定的目录文件。rules 是我们指定的目录规则。

用法如下,修改 middleware/index.js

// 引入规则中件间
const miRule = require('./mi-rule') module.exports = (app) => {
/**
* 在接口的开头调用
* 指定 controller 文件夹下的 js 文件,挂载在 app.controller 属性
* 指定 service 文件夹下的 js 文件,挂载在 app.service 属性
*/
miRule({
app,
rules: [
{
path: path.join(__dirname, '../controller'),
name: 'controller'
},
{
path: path.join(__dirname, '../service'),
name: 'service'
}
]
}) // 以下代码省略
}

业务代码应用

1. 修改 router.js

const router = require('koa-router')()
module.exports = (app) => {
router.get( '/', app.controller.home.index )
router.get('/home', app.controller.home.home)
router.get('/home/:id/:name', app.controller.home.homeParams)
router.get('/user', app.controller.home.login)
router.post('/user/register', app.controller.home.register)
app.use(router.routes()).use(router.allowedMethods())
}

2. 修改 controller/home.js

module.exports = {
index: async(ctx, next) => {
await ctx.render("home/index", {title: "iKcamp欢迎您"})
},
home: async(ctx, next) => {
ctx.response.body = '<h1>HOME page</h1>'
},
homeParams: async(ctx, next) => {
ctx.response.body = '<h1>HOME page /:id/:name</h1>'
},
login: async(ctx, next) => {
await ctx.render('home/login', {
btnName: 'GoGoGo'
})
},
register: async(ctx, next) => {
// 解构出 app 实例对象
const { app } = ctx let params = ctx.request.body
let name = params.name
let password = params.password // 留意 service 层的调用方式
let res = await app.service.home.register(name,password)
if(res.status == "-1"){
await ctx.render("home/login", res.data)
}else{
ctx.state.title = "个人中心"
await ctx.render("home/success", res.data)
}
}
}

项目中引入这个结构规范,并不是必须的,毕竟大家的想法不一样。iKcamp 团队在提出此想法时候,也是有不少分歧。提出这样一个思路,仅供大家参考。

开发环境运行

作为后端代码语言,开发环境中每次修改文件,都需要手动的重启应用,不能像前端浏览器那样清爽。为了减轻手工重启的成本,我们建议采用 nodemon 来代替 node 以启动应用。当代码发生变化时候,nodemon 会帮我们自动重启。

全局安装 nodemon

npm i nodemon -g

本地项目中也需要安装:

npm i nodemon -S

更多细节用法,请查阅官方文档

部署运行

线上部署运行的话,方法也有很多,我们推荐使用 pm2

pm2 是一个带有负载均衡功能的Node应用的进程管理器。

安装方法与 nodemon 相似,需要全局安装:

npm i pm2 -g

运行方法:

pm2 start app.js

更多细节用法,请查阅官方文档

推荐: 翻译项目Master的自述:

1. 干货|人人都是翻译项目的Master

2. iKcamp出品微信小程序教学共5章16小节汇总(含视频)

iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 规范与部署的更多相关文章

  1. iKcamp团队制作|基于Koa2搭建Node.js实战项目教学(含视频)☞ 环境准备

    安装搭建项目的开发环境 视频地址:https://www.cctalk.com/v/15114357764004 文章 Koa 起手 - 环境准备 由于 koa2 已经开始使用 async/await ...

  2. iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 错误处理

    沪江CCtalk视频地址:https://www.cctalk.com/v/15114923887518 处理错误请求 爱能遮掩一切过错. 当我们在访问一个站点的时候,如果访问的地址不存在(404), ...

  3. iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 记录日志

    沪江CCtalk视频地址:https://www.cctalk.com/v/15114923883523 log 日志中间件 最困难的事情就是认识自己. 在一个真实的项目中,开发只是整个投入的一小部分 ...

  4. iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 解析JSON

    视频地址:https://www.cctalk.com/v/15114923886141 JSON 数据 我颠倒了整个世界,只为摆正你的倒影. 前面的文章中,我们已经完成了项目中常见的问题,比如 路由 ...

  5. iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 处理静态资源

    视频地址:https://www.cctalk.com/v/15114923882788 处理静态资源 无非花开花落,静静. 指定静态资源目录 这里我们使用第三方中间件: koa-static 安装并 ...

  6. iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 视图Nunjucks

    视频地址:https://www.cctalk.com/v/15114923888328 视图 Nunjucks 彩虹是上帝和人类立的约,上帝不会再用洪水灭人. 客户端和服务端之间相互通信,传递的数据 ...

  7. iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 代码分层

    视频地址:https://www.cctalk.com/v/15114923889408 文章 在前面几节中,我们已经实现了项目中的几个常见操作:启动服务器.路由中间件.Get 和 Post 形式的请 ...

  8. iKcamp团队制作|基于Koa2搭建Node.js实战(含视频)☞ 中间件用法

    中间件用法--讲解 Koa2 中间件的用法及如何开发中间件

  9. iKcamp团队制作|基于Koa2搭建Node.js实战(含视频)☞ 路由koa-router

    路由koa-router--MVC 中重要的环节:Url 处理器

随机推荐

  1. 使用JMeter代理服务器录制APP脚本

    重点:证书的安装,需要将Jmeter安装目录下证书传送到手机,使用手机安装(不要用QQ传送给手机,手机提示无法安装,可使用网盘方式传送,可成功安装证书) (出现该错误时,需安装证书) 简单的配置教程如 ...

  2. Django 博客项目01 数据库设计与验证码校验+Ajax登录

    数据库设计 from django.db import models from django.contrib.auth.models import AbstractUser class UserInf ...

  3. 全选,反选,获取值ajax提交

    //必须先加载jquery //加载 弹出框插件 artdialog http://www.planeart.cn/demo/artDialog/ /****全选反选*开始**/ $(document ...

  4. windows下隐藏磁盘分区

    在一定情况下有的人会想隐藏掉部分分区,比如双系统的情况 有两种方式 方法1: 删除盘符,适合在双系统的情况下隐藏掉另外一个系统相关的分区 请注意是删除盘符 不是删除分区 此电脑右键管理 点击磁盘管理 ...

  5. windows任务计划程序 坑

  6. onload属性使用方法

    onload事件属性是页面的图片文字等全部加载完毕后执行的事件 window.onload=fun1;function fun1(){    document.getElementsByTagName ...

  7. http 各个状态返回值

    code 定义在 org.apache.http.HttpStatus 转载来自于:http://desert3.iteye.com/blog/1136548 502 Bad Gateway:tomc ...

  8. Unreal Engine 4(虚幻UE4)GameplayAbilities 插件入门教程(六)GameplayEffect的级别设置

    本节的内容不难,权当是复习.如果没有完成前面的教程,请前往学习. 第一步:用一个csv文件表示级别数据,下图中的Hurt随级别1~7表示其损伤值在1~7级别时分别是-7,-14,-20等.写好之后关闭 ...

  9. dubbo 官方参考手册~备案(防止哪天阿里一生气把dubbo给删除了)

          首页  ||  下载  ||  用户指南  ||  开发者指南  ||  管理员指南  ||  培训文档  ||  常见问题解答  ||  发布记录  ||  发展路线  ||  社区 E ...

  10. SDL播放音频的时候发现SDL_OpenAudioDevice打开一直失败

    1:在使用SDL播放音频的时候发现SDL_OpenAudioDevice打开一直失败,导致SDL不能进入回调函数. 使用SDL_GetError()打印错误提示XAudio2: XAudio2Crea ...