中在文末提到了复杂的异步交互流程,那么我本文主要就来实现从前端到后台的交互。
插曲——Mongoose在创建MongoDB的Collection时的命名策略
这个问题我不得不说在最前面,因为作为初学者我们向后端扩展的过程中对数据库的一些细节可能并不会注意到,而mongoose在对collection创建时的命名策略竟然在大多的基础教程中都没有提到,我们业余时间本来就不多,然后一个小小的问题就够我喝一壶的了。。
说重点:
1、定义好schema后,将schema发布为model
var ArticlesModel=mongoose.model("Articles",ArticlesSchema);
这同时是在数据库中创建了一个表叫'article',即在model()方法中的第一个命名的参数,这个是不区分大小写的,不区分大小写。
2、另外,如果对于文章详情页还需定义一个表,那就一定不要再取名叫做'article',
这是因为mongoose为我们创建collection的时候,会自动都转化为复数形式,毕竟这个更像是集合。所以我们对模型命名的时候,一定要考虑到单复数形式。即不要妄想建立单复数的同时存在的两个表。
假设BookSchema已经定义,如果我们发布model
var Book=mongoose.model("Book",BookSchema);
查找存入该表的数据可以这样:
db.books.find()
//或者这样
db.model('Book').find()
进入主题——异步交互
上文也提到过,每一条评论我分成了主回复Comment和回复列表ReplyList(越到后面越能感觉到最初整个文档的数据结构设计对组件的拆分和数据的操作影响之大,所以对一个复杂的应用,一定要慎重设计好state树),也就是组件Comment和ReplyList中都分别要有点赞和评论功能,既然功能相同,那么肯定是代码能复用才最好。代码的优化是个渐进的过程,我先实现在一个组件中实现点赞功能,最后再考虑抽成一个函数(成功不易复制,但是失败可以避免,所以我记录下的更多是自己的探索过程)。
点赞的逻辑并不复杂:如果当前尚未点赞(即状态praise:false),则点赞后,点赞数量(praise_count)+1,praise置为true,取消赞类似。
constructor(props){
super(props);
this.state={
praise_count:props.comment.praise_count,
praise:props.comment.praise
}
}
clickHeart(e) {
const { comment } = this.props;
let praiseCount = this.state.praise_count,
isPraise;
let self = this;
if (this.state.praise) {
praiseCount -= 1;
isPraise = false;
} else {
praiseCount += 1;
isPraise = true;
}
//fetch请求后边可以考虑抽离成一个action
fetch('/post_praise', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
articleId: 1,
//articleId其实也是需要从父组件一层层传递下来的,现在为省事儿就没改
id: comment.id,
praise_count: praiseCount,
praise: isPraise
})
})
.then(response => {
if (response.status == 200) {
response.json().then(function(json) {
self.setState({
praise_count: json.praise_count,
praise: json.praise
})
})
}
})
}
//render中相关代码:
let praiseClass=classNames({
'iconfont icon-praise':true,
'active':this.state.praise
})
<span className={praiseClass} onClick={this.clickHeart.bind(this)}></span>
<span>{this.state.praise_count}</span>
post请求中,客户端请求接口时必须指名请求头类型 Content-Type=application/json,这样当我们post上来的数据为{praise:true}
的时候req.body.praise就能取到值,如果没有正确声明请求头,会报undefined.
post 提交数据后,后端更新数据库中对应键值对,然后返回给前端修改后的数据,那么前端怎么更新视图呢?就是利用react提供的setState(),所以我们就需要配合后边的更新将点赞相关的这两个变量praise_count和praise设为了this.state中的变量。
后端的代码又是如何写的呢?
router.post('/post_praise',function(req,res){
var data=req.body;
//可以看出数据结构很直观的会影响到数据的查找,这儿的update就稍微复杂了些
comments.update({'articleId':data.articleId,'comments.id':data.id},
{'comments.$.praise_count':data.praise_count,
'comments.$.praise':data.praise},
function(err,results){
if (err) {
console.log('error message',err);
return;
}
//将返回数据作为update()的回调函数
res.send({
praise_count:data.praise_count,
praise:data.praise
});
})
})
另外需要注意一点:express需要依赖 bodyParser 包来解析请求体,在4.0版本之后(我使用的是最新的4.14.0版本)需要独立安装npm install body-parser
,然后在启动文件中需要配置
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
//body-parser 解析json格式数据
app.use(bodyParser.json({limit: '1mb'}));
//此项必须在 bodyParser.json 下面,为参数编码
app.use(bodyParser.urlencoded({
extended: true
}));
功能的抽离后边慢慢写吧,在这儿我先抛砖引玉。完整大型应用的开源项目,大家也可以推荐下啊,实在感觉网上大多实例应用的工具不少但实现也都较为简单不太能满足日益增长的需求。像复杂应用中如何设计数据结构、更好的整合/规范代码、前后端的配合等等问题都还需要慢慢摸索。
【参考文章】
https://github.com/reactjs/redux/issues/291
express 解析post方式下的json参数
React 源码剖析系列 - 解密 setState