2

为啥要选用node

  1. 不需要学习额外的语言特性;
  2. 扩展网络和数据库的知识面;
  3. 一个字,快!

安装环境快,一键式无脑安装,不用担心各种繁琐配置;
启动服务快,只需要短短几行代码就能生成一个web服务器;
上手更快。

思路

通过下述步骤对使用node开发web服务有一个大概的认识
  1. 安装数据库,这里选用mongodb;
  2. 定义数据模式,即数据的结构和行为;
  3. 定义数据模型,对数据库进行操作需要通过该模型;
  4. 定义路由,在该路由路径被访问时,触发对应的函数,进行数据库操作。

其实很简单,就三步。通过一些代码层面的优化,甚至只需要我们实现第二步定义好我们所需要的数据结构之后就能生成restful风格的api。

restful风格的api?

宁静的数据接口?什么鬼?
  • 先说api,其实是一个很广的命题。这里我们理解的api是打通前后端的交互的数据接口。而在有一些语义下,它可以是你封装的一个工具函数方法。
  • restful风格我们可以这么理解
  1. 设计路径:在第2、3步定义好数据结构后,通过路径去访问该数据资源。
  2. 定义http动词:访问资源后,我们需要什么样的操作

我们耳熟能详的增删改查其实就对应着 post、delete、put、get这四个http动词。
例如:get /api/user/ => 查看/api/user这个路径下所有用户信息的资源

尝试开发

其实开发web服务,大部分操作就是围绕着操作数据库,而操作数据库肯定有额外的学习成本。毕竟这是很多前端er没接触过的。

就我个人而言,学习一个新的知识点,查阅官方文档无疑是最好的办法。

假设已经安装完了mongodb,我们要操作数据库还需要另一个东西:mongoose,翻译过来是猫鼬,又是一个不知道什么鸟东西,在这儿可以简单的理解,通过它可以更加方便的操作数据库。

其实官方的例子特别的好,跟着猫鼬一起云养猫
还记得前面将的思路和步骤嘛,首先我们要使用node的一个框架express生成项目模板

  1. 设计路径:在routes文件夹创建一个cat.js文件,这个就是我们要访问的资源;
  2. 定义数据模型:定义数据结构、生成数据模型;
  3. 定义路由:使用express框架的路由功能,根据http动词触发对应函数操作数据库;
const express = require('express');
const router = express.Router();
const mongoose = require("mongoose")

//猫的模式:假设每一只猫都有名字和颜色的属性。
const catSchema = mongoose.Schema({
  name: String,
  color:String
})
//猫的模型:通过传入猫的模式,我们有了一个模型,然后就可以根据该模型生成好多猫了!
const CatModel = mongoose.model('Cat', catSchema)

router.get('/cat', function (req, res, next) {
  //后来猫越来越多,就可以通过模型来查找小猫
  CatModel.find((err, allCats) => res.json(allCats))
});

router.post('/cat', ((req, res, next) => {
  //假设我们要增加一只小猫,猫咪的信息通过post请求的请求体传输到服务端。
  const catInfo = req.body //假如请求体内容为{name:'小白',color:'黑色'}
  //根据猫的模型生成的小猫就有了  名字为小白,颜色为黑色的属性。
  const lititleCat = new CatModel(catInfo)
  //小猫在调用save方法后保存到数据库
  lititleCat.save((err, saved) => res.json(saved))
}))

router.delete('/cat', ((req, res, next) => {
  const { id } = req.body
  //根据传入的猫的id,删除该猫的信息
  CatModel.findByIdAndRemove(id, (err, removed) => res.json(removed))
}))

router.put('/cat', ((req, res, next) => {
  const { id } = req.body
  //根据传入的猫的id,更新猫的信息
  CatModel.findByIdAndUpdate(id, { ...req.body }, { new: true }, (err, updated) => res.json(updated))
}))

这个时候启动服务,其实未生效,这是因为我们写了一大堆,并没有触发,需要在入口文件app.js引入我们的路由文件。

//引入猫的路由
const catRouter = require('./routes/cat');
//app就是我们的服务器,当服务器资源路径 /api被调用时,就会映射到我们猫的路由
app.use('/api', catRouter);
// localhoset:3000/api/cat  即是我们要请求猫的资源的 路径,最后通过http动词,触发对应的数据库操作,我们就生成了第一个符合restful风格的api。

进阶

  1. 假设我们要将猫的信息和它的饲养员的信息绑定在一起,就需要联表
  2. mongoose的语法充斥着各种回调,就需要async await来优化

接下来修改一下上面生成猫的例子

const express = require('express');
const router = express.Router();
const mongoose = require("mongoose")
const mongoose = require('mongoose')

+ const feederSchema = mongoose.Schema({
+  feederName: String,
+  cat: {
+    type: mongoose.Schema.Types.ObjectId,//联表查询必须这样的格式来存储对应表的_id
+    ref: 'Cat'//联表关系的表名,注意是生成模型的类,与模型名区分开
+  }
+ })

//猫的模式:假设每一只猫都有名字和颜色的属性。
const catSchema = mongoose.Schema({
  name: String,
  color:String
})

+ const feederModel = mongoose.model("Feeder", feederSchema)
//猫的模型:通过传入猫的模式,我们有了一个模型,然后就可以根据该模型生成好多猫了!
const CatModel = mongoose.model('Cat', catSchema)

router.get('/cat',  (req, res, next)=> {
  //后来猫越来越多,就可以通过模型来查找小猫
  CatModel.find((err, allCats) => res.json(allCats))
});

router.post('/cat', async((req, res, next) => {
   //假设我们要增加一只小猫,猫咪的信息通过post请求的请求体传输到服务端。
+  const { name, color, feederName } = req.body //假如请求体内容为{name:'小白',color:'黑色',feederName:"张三"}
+  const catInfo = { name, color } //猫的信息
+  const feederInfo = { feederName }  //饲养员信息
   //根据猫的模型生成的小猫就有了  名字为小白,颜色为黑色的属性。
   const lititleCat = new CatModel(catInfo)
   //小猫在调用save方法后保存到数据库
+  const { _id } = await lititleCat.save()
   //将小猫的id与用户的猫关联起来
+  const newFeeder = new feederModel({ ...feederInfo, cat: _id })
   //保存用户信息
+  await newFeeder.save()
   //通过populate()可以查询联表的属性,exec()可以更好的追踪堆栈,让查询的函数返回的是一个完整的promise对象
+  const data = await feederModel.find().populate("cat").exec()
+  res.json(data)
  
}))

router.delete('/cat', (req, res, next) => {
  const { id } = req.body
  //根据传入的猫的id,删除该猫的信息
  CatModel.findByIdAndRemove(id, (err, removed) => res.json(removed))
})

router.put('/cat', (req, res, next) => {
  const { id } = req.body
  //根据传入的猫的id,更新猫的信息
  CatModel.findByIdAndUpdate(id, { ...req.body }, { new: true }, (err, updated) => res.json(updated))
})

可以下载postman测试一下代码
image

优化

当你使用postman测试了几次代码之后你会发现每次post请求都新增重复数据,哪怕内容是没有变化的,mongoose会为保存时的数据添加唯一的_id标识。

按照正常的逻辑应该是

  1. 当没有这条数据时,插入一个新数据;
  2. 当数据存在时,根据新的内容更新它;
  3. 而判断该数据是否存在,我们需要一个作为唯一标识的查询条件

所以我们可以使用findOneAndUpdate这个api插入或更新我们的数据,修改post请求的代码

router.post('/cat', async((req, res, next) => {
  //假设我们要增加一只小猫,猫咪的信息通过post请求的请求体传输到服务端。
  const { name, color, feederName } = req.body //假如请求体内容为{name:'小白',color:'黑色',feederName:"张三"}
  const catInfo = { name, color } //猫的信息
  const feederInfo = { feederName }  //饲养员信息
  //这里根据猫的名称作为插入或更新的查询标识
+ const {_id} = await CatModel.findOneAndUpdate({name},catInfo,{ upsert: true, new: true, setDefaultsOnInsert: true }).exec()
  //这里根据饲养员的名称作为插入或更新的查询标识
+ await feederModel.findOneAndUpdate({feederName},{...feederInfo,cat:_id},{ upsert: true, new: true, setDefaultsOnInsert: true })
  //通过populate()可以查询联表的属性,exec()可以更好的追踪堆栈,让查询的函数返回的是一个完整的promise对象
  const data = await feederModel.find().populate("cat").exec()
  res.json(data)
 
}))

总结

当然,这些都是node最基础的操作,但是这些特别简单的东西,扩展了很多知识,通过断点调试你可以更加深刻地了解到http请求通信过程中的数据传输、数据库模型的操作等等,甚至你还会开始思考如何使用node做更多的东西。
例如:
快速的接口mock,在拟定好协议之后使用node开发数据接口,提高接口联调的效率;
甚至搭一个可视化的mock数据的平台,通过可编辑表格操作去生成数据接口;
又或者你可以新建一个自己的网站,使用node搭建自己的服务器等等等。

最后贴上自己的github地址:
https://github.com/kangjs7854...
臭不要脸的求个star,万分感谢~


康AOXRg
7 声望0 粉丝