上一节,我们将了NodeJS的安装,server的创建、debugger等,想要满足开发,我们还是要接着往下看,那么,我们直奔主题!
项目实战(原生环境搭建)
依赖包
-
cross-env
:提供兼容Win,Mac,Linux不同系统的环境变量 -
nodemon
:监控NodeJS文件变化,实现自动编译,不用每次修改文件后手动重启服务
可通过下面代码进行安装
yarn add nodemon cross-env --save-dev
创建目录结构
- 执行
npm init -y
,创建一个初始化的npm环境,用来将来安装一些依赖包,生成package.json文件 - 项目目录下创建'bin'文件夹,一般存放可执行文件,并在其下层创建'www.js'文件,在这里我们写一些
Server
相关的代码,即server
层 - 在项目目录下创建app.js,在这里我们用来写我们的业务代码。
写代码
//www.js
const http = require('http')
const PORT = 8000
const serverHandel = require('../app')
const server = http.createServer(serverHandel)
server.listen(PORT)
console.log('success')
//app.js
const serveHandel = (req, res) => {
//设置返回格式为JSON
res.setHeader('Content-type', 'application/json')
const resData = {
name:'张三',
age: 18,
env: process.env.NODE_ENV
}
res.end(
JSON.stringify(resData)
)
}
module.exports = serveHandel
//在安装完以上'nodemon 和cross-env'后,需要配置一下node指令,和主文件,如下
"main": "bin/www.js",
"scripts": {
"dev": "cross-env NODE_ENV=dev nodemon ./bin/www.js",
"prd": "cross-env NODE_ENV=production nodemon ./bin/www.js"
},
上面我将server
代码,业务代码抽离开,使得项目更加易于维护,我们可以在app.js里面进行各种配置和解析,并且可以将不同的方法相互引用,而www.js只放心做server端的事。
到这里,一个基础的NodeJS项目环境就搭建完成了,是不是很简单呢
开发
路由初始化
首先需要注意
- 接口,是通过路由实现的
上面我们搭建了基本的开发环境,也将server层代码和业务代码分离,但毕竟业务是复杂的我们不能把业务代码都写在app.js里面,所以这里仍然需要做个拆分,一般我们采用的方案是在“bin”同级新建“src”文件夹,其中的每个人间代表一类接口,然后我们再通过模块化将其引入到app.js中,这样,我们就可以不同的业务代码写在不同文件件里,一些公共的配置也可以在app.js里面进行全局配置,这样的代码将拆分的更加简单
一般的,我们需要先将接口调通,再去处理其具体数据,下面我将改造后的app.js和其中一个业务代码模块贴出来,同学们也可以跟着这个节奏进行扩展
//app.js
const handleBlogRouter = require('./src/router/blog')
const handleUserRouter = require('./src/router/user')
const serveHandel = (req, res) => {
//设置返回格式为JSON
res.setHeader('Content-type', 'application/json')
//设置path
const url = req.url
req.path = url.split('?')[0]
//处理blog路由
const blogData = handleBlogRouter(req, res)
if(blogData) {
res.end(
JSON.stringify(blogData)
)
return
}
//处理user路由
const userData = handleUserRouter(req,res)
if(userData) {
res.end(
JSON.stringify(userData)
)
return
}
//未命中路由返回404
res.writeHead(404, {"Content-type": "text/plain"})
res.write("404 Not Found\n")
res.end()
}
module.exports = serveHandel
//其中一个业务代码
const handleBlogRouter = (req, res) => {
const method = req.method //GET POST
//实现接口
//获取博客列表
if (method === 'GET' && req.path === '/api/blog/list') {
return {
msg: '这是获取博客列表的接口'
}
}
//获取博客详情
if (method === 'GET' && req.path === '/api/blog/detail') {
return {
msg: '这是获取博客详情的接口'
}
}
//新建一篇博客
if (method === 'POST' && req.path === '/api/blog/new') {
return {
msg: '这是新建博客的接口'
}
}
//更新一篇博客
if (method === 'POST' && req.path === '/api/blog/update') {
return {
msg: '这是更新博客的接口'
}
}
//删除一篇博客
if (method === 'POST' && req.path === '/api/blog/del') {
return {
msg: '这是删除博客的接口'
}
}
}
module.exports = handleBlogRouter
这样我们就创建了不同的接口,并且可以通过浏览器或postman对其进行测试。
读到这里,我想做一个总结:
我们把不同业务代码拆分到src对应的不同文件夹下,然后通过nodejs模块化将其引入到app.js,即入口文件中,并在这里做一些全局性的配置,如响应头的设置,path的配置等,当我们调用某接口时,如:'/api/blog/detail',就会命中业务代码中的某一个逻辑,并返回对应的对象(这里我们为了先将接口测通,先返回简单对象),并最终将其作为接口响应返回,假如不能命中任何逻辑代码,则响应为404。
到这里,我们就完成了这个项目的路由初始化,下面我们就可以开发对应的路由(接口)了
路由开发
上面讲到了基础环境的搭建,Server层和业务层的分离,以及路由的初始化等,到此为止,我们已经具备了开发接口的能力(不涉及登录,不涉及数据库,只做接口的数据格式返回)
这里介绍几个概念:
- 数据模型:一种面向对象的变成思想,用到对象继承等一些知识,我们将在后面使用数据模型来定义将来要输出的数据结构,使我们接口所输出的数据达到一个格式上的一致性,或者是易于维护的特点。
- controller: MVC设计模式中的C,在这里,我们将用来专心的处理数据,只做和数据有关的事情,包括一些计算等等的操作。
这样,我们的项目暂时可以分为以下四层:
-
www.js:用来写
Server
逻辑,只关心Server的逻辑 - app.js: 做一些基本配置,如参数的解析规则,不同接口的处理方法等,不涉及业务代码,可以看做是Server层和业务层的桥梁
- router: 处理路由接口的逻辑,在这里我们只关心路由的都逻辑,包括匹配到路由后返回什么格式的数据等(在这里我们将用到数据模型,用来统一返回格式)
- controller:在这里,我们只关心数据,我们可以在这里做具体的数据处理,处理参数等。
将应用分层,然后通过nodejs模块化,将各个模块关联起来,这就行程了一个成熟应用的雏形,我们可以在此基础上开发各种借口,下面我将贴出一个获取博客详情的各层代码
- Server层
和上面基础路由的搭建一样,这里不做说明
- app层
和上面基础路由的搭建一样,只加入了query解析的代码,因为这属于公共的逻辑代码
+++
req.query = querystring.parse(url.split('?')[1])
...
- router层
我们在上面搭建了基础的路由逻辑,即命中某一个路由,返回对应的数据,因为上面只是做初始化,使用了一些简单的“傻白甜”数据,这里我们将其具体化。
const { getList } = require('../controller/blog')
const { SuccessModel, ErrorModel } = require('../model/resModel')
if (method === 'GET' && req.path === '/api/blog/list') {
const author = req.query.author || ''
const keyword = req.query.keyword || ''
const listData = getList(author,keyword)
return new SuccessModel(listData)
}
可以看到,这里我们将具体的数据处理方法getList
,抽离出去,并引入了数据模型用来规范返回的数据格式。
- 数据模型
//我们将其建在router同级目录下的model文件夹下
class BaseModel {
constructor(data, message) {
if(typeof data === 'string') {
this.message = data
data = null
message = null
}
if(data) {
this.data = data
}
if (message) {
this.message = message
}
}
}
class SuccessModel extends BaseModel {
constructor(data, message) {
super(data,message)
this.errno = 0
}
}
class ErrorModel extends BaseModel {
constructor(data, message) {
super(data, message)
this.errno = -1
}
}
module.exports = {
SuccessModel,
ErrorModel
}
这就是数据模型,可以看出,这里用到了JS继承的一些东西,就是定义一个数据结构
- controller 层
//我们将其建在router同级目录下的controller文件夹下
const getList = (author, keyword) => {
// 先返回假数据(格式是正确的)
return [
{
id: 1,
title: '标题A',
content: '内容A',
createTime: 1586310687741,
author: '张三'
},
{
id: 2,
title: '标题B',
content: '内容B',
createTime: 1586310687741,
author: '李四'
}
]
}
module.exports = {
getList
}
可以看到,这里我们只针对传入参数做响应的逻辑,并输出对应的参数,这里再次说明controller只关心数据的处理
于是我们访问http://localhost:8000/api/blog/list?author=zhangsan&keyword=A试试吧,是不是返回了我们想要的数据结构呢。
小结:个人认为,这一部分十分重要,我们将应用分层,不同的层写不同的代码,将各个层之间串联起来,就是一个完整的应用。好了,自己在你的编辑器上捋一捋这样的逻辑吧!
网友评论