做一个跑通前后端的`豆瓣租房`移动端webApp

一、前言

最近学了react,一直想做一个项目,没有什么好的主意。因为自己也要租房住,就想到了租房App这个idea,参考豆瓣租房小程序,着手了这样一个简陋的前后端项目?。
?在线demo点击这里
?项目源码点击这里,你可以下载在本地运行,如果对你有帮助可以点一下star哈?

// 你可以使用npm或yarn
yarn install
运行你的数据库 // 必须!!!
yarn server // 运行服务器, 连接的数据库在server目录下的config.js里配置
yarn start // 运行项目
复制代码

二、前端

  1. 项目技术栈react+react-router+react-redux,用create-react-app脚手架生成。UI方面采用Ant Design Mobile?官方地址
  2. css方案是css-in-js,采用style-jsx ,? github地址,可参考掘金上的一篇文章?点击这里
  3. 由于是移动端,避免不了适配问题,采用vm/vh适配,具体同样可以参考掘金的?这篇文章
  4. 在结合2、3两点时,由于要添加配置项,但我不想在项目中run eject弹出,于是用了react-app-rewired改写配置,这样就不用弹出命令了。? github地址
  5. 权限路由。思路是根据遍历路由配置表,需要权限的走权限路由,不需要的走原来的路由。具体可看项目中的router部分。
  6. 图标采用iconfont SVG处理
    具体用法查看官方地址
遇到的问题:
  • (未解决)在dev开发环境下修改scss中的css,不会实时编译更新
  • (未解决)IOS下通过focus事件不能唤起键盘,安卓下可以。为了有个较好的用户体验,我在登录,搜索页面打开时,让输入框自动focus唤起键盘,经IOS真机实践,只能触发focus事件,但是不会唤起键盘,安卓正常。查阅资料后是IOS做的限制,(IOS还有和音频、视频不能自动播放的限制)。需要用户主动点击输入框后,才可以唤起键盘。下一次重新打开就能自动唤起键盘了,很坑的一点?!目前无解?
  • (解决)从首页列表点击详情时候,返回到首页,会重新请求加载,并且滚动位置丢失,用户体验十分不好。这里为了学习redux我用redux(也可用react的新context api)解决,因此在路由方面也用了react-redux-router,但已不维护,改为connect-react-routergithub点这里。思路是首次获取房源列表,然后存入redux中,下次打开的时候,从redux中获取。
  • (解决)热加载后不能保存redux中的状态。解决方法:在store中,添加以下代码, 详细看这里
if (module.hot) {
// Reload reducers
    module.hot.accept('./reducers', () => {
        store.replaceReducer(connectRouter(history)(rootReducer));
    });
}
复制代码
  • (解决??) 用了react-loadble加载项目中的搜索页面,会有搜索inputplaceholder显示不全的问题,初次打开会有问题,第二次打开没有问题,如下图。 在dev环境下不能重现,生产环境下会有问题。该组件为Ant Design MobilesearchBar
    上图我们可以看到是宽度的问题,正常应该为110px,而错误的时候则才80px暂时解决方法:移除该路由懒加载,直接加载?
项目优化:
  1. 路由懒加载,方案: react-loadable,添加loading提示
  2. 图片懒加载,方案:lazyload
    • 封装成一个组件? 具体代码
    • 这里需要说明在你的网站上加载豆瓣的图片都是403的,因此我们需要用到下面这个网站来加载图片 点击这里,使用方法 https://images.weserv.nl/?url=+图片原来的地址, 详细参考上一步代码中的链接
  3. ajax视情况添加loading提示,添加CSS3动画,使交互更加友好。

三、后端

  • 采用koa2+koa-router+mongodb+jsonWebToken。最主要的是需要注意异步和异常处理的问题。
  • 数据库方面用了Mongoose来操作。Mongoose是在node.js异步环境下对mongodb进行便捷操作的对象模型工具。更多详细说明请看官方文档:?点击这里
3.1 爬取豆瓣小组数据
  • 用到的http库是axios
  • 定时任务库,node-schedule。github:?点击这里
  • 爬虫库cheerio,它的用法十分简单。
const cheerio = require('cheerio')
const $ = cheerio.load('<h2 class="title">Hello world</h2>')
复制代码

这里我们就可以通过$(selector),像jquery一样的方式取到页面的元素。官方文档:?点击这里

整个爬取的流程:

  • 初始化的时候判断是否大于最大存储的数据长度(此项目中设置了数据库最多储存5000条数据),如果超过,则执行删除,反之跳过。
  • 开启一个定时任务,每天的0.00am开始爬取=>爬取列表页面=>存入数据库=>如果失败,不会爬取该条tid
  • 的详情页,反之继续爬取详情页。
  • 爬虫提取信息用到了一些正则表达式,提取房租、联系方式、房型、所在地区等等。具体代码:?点击这里。其中参考了?这篇文章中的一些正则表达式。

注意:豆瓣会限制一个时段内Ip的访问次数,因此需要我们做一些调整。

  • 列表页面每一页、 详情页每一条数据的爬取的间隔时间保证是不同的。(定时器+随机数时间)(貌似没什么卵用?)
// sleep
function sleep(time = 0) {
 return new Promise(resolve => {
   setTimeout(resolve, time);
 });
}
 // 更新数据库函数
 async updateTopic(tid, resolve, reject) {
   // 睡眠
   await sleep(Math.ceil(Math.random() * 50 * 1000) + 5000);
   // 开始更新
   await this.fetchDetail(tid).then(houseInfo => {...});
 }
复制代码
  • 改变请求头的user-agent。项目中是有个user-agent列表?查看代码,每次请求都带上随机中的一个。
3.2 存入数据库

这里我是一次性插入多条数据,用到的api如下

db.Houses.insertMany([your array data])
复制代码
3.3 写接口(路由)

需要注意的是部分路由(需要用户登录后才可以访问的接口)header中需要传递token才能访问,因此添加路由中间件校验通过校验后才允许访问。详细代码查看这里。关键代码如下?

const jwt = require('jsonwebtoken');
const token = ctx.header['x-token'];
if(token){
	解析token得到用户信息
	进入下一个中间件
}else {
	返回错误需要传递token
}
复制代码

四、数据库mogodb相关

4.1 修改数据库相关结构

开始设计数据库的时候,设置价格字段prices是数组,后觉得字符串就可以了。于是在原数据库的基础上修改数据格式字段名prices=>price

  1. 批量更新某个字段
db.getCollection('houses').find().forEach(function(item){
    db.getCollection('houses').update({_id:item._id},{$set:{prices: ''+item.prices}})
 })
复制代码
  1. 更改字段名
// 如将字段"prices"改为"price"
db.getCollection('houses').update({},{$rename:{'prices':'price'}}, false, true)
复制代码
4.2 附上一些api.
  1. 数据库复制。如复制douban-house数据库到douban-test
// db.copyDatabase(<from_dbname>, <to_dbname>, <from_hostname>)
 db.copyDatabase('douban-house', 'douban-test')
复制代码
  1. 查找数据库中数组长度大/小于n的数据
// 大于 exists=1 小于exists=0
db.getCollection('houses').find({'imgs.n':{'$exists':1}})
复制代码
  1. 查找数据库中某个字段不为null的数据
// $ne=> not equal
db.getCollection('houses').find({'contact':{$ne:null}})
复制代码
  1. 查找数据库中多条某个字段的数据
db.getCollection('houses').find({'tid':{$in:['这里是数组','例如id1','2']}})
复制代码

另外:插入字段数字Number Int类型的数据会存储为Double类型,会带有小数点,例如存的是10,存进数据库之后会变成10.0,可以用NumberInt或者NumberLong来存储

db.houses.insert({"tid": NumberInt(666)})
复制代码
4.3 遇到的问题

爬虫爬取贴子的时候,会爬到相同的贴子,而我们是不需要这些重复的。这里的问题是在插入重复值的时候,出现错误之后不会继续插入剩下的数据,这是很坑的一点。下面是解决方法:

  • 先设置mongodb的唯一索引值,在设置的时候也遇到不少的坑,查了很多资料,总结相关的api
    const housesSchema = new mongoose.Schema({
    	tid: String, //我这里设置的唯一索引是每条贴子的id号
    	...省略
    })
    housesSchema.index({ tid: 1 }, { unique: true });
    复制代码
  • 这里设置好之后,当插入重复的tid时,数据库会返回错误,不插入该条数据。特别需要说的大坑是插入的api无论是insert还是insertMany, 他们的api如下
db.collection.insert(
  <document or array of documents>,
  {
    writeConcern: <document>,
    ordered: <boolean>
  }
)
复制代码
这里需要注意的是`ordered`这个参数, 这是一个可选参数,官方解释如下
复制代码

Optional. A boolean specifying whether the mongod instance should perform an ordered or unordered insert. Defaults to true.

大意就是指定mongod实例是否应执行有序插入。默认为```true```。
**重点是:**当有序插入的时候,如果出现了错误,程序会停下当前的插入,不执行插入剩余的数据。只有当无序插入,也就是设置了```ordered: false```,当出现错误之后,才会把剩下的继续插入。官方说明如下:

> Excluding Write Concern errors, ordered operations stop after an error, while unordered operations continue to process any remaining write operations in the queue.

官方文档链接:?[点击这里](http://docs.mongodb.com/manual/reference/method/db.collection.insertMany)
复制代码

五、部署相关(跨域处理)

  • 开发阶段 可在项目中的package.json中添加proxy字段, 这里假设http://localhost:3003就是我们的后台服务器, http://localhost:3000是react开发时候的服务器 如:在项目中访问http://localhost:3000/api/house/125048127就会代理到http://localhost:3003/api/house/125048127, 就没有跨域问题了
  "proxy": {
    "/api": {
      "target": "http://localhost:3003"
    }
  }
复制代码
location  /api/ {
   proxy_pass   http://localhost:3003;
}
复制代码

六、git相关

有时候提交了错误的代码又想回退版本,就需要回退远程git仓库的代码,再重新提交。 ?更多用法参考这里

git reflog // 查看提交列表, 如我需要撤回到第二条提交记录,也就是红线下的那条
git reset --soft 3a2a12d // 这里的参数--soft表示保留本地修改记录, --hard 代表保存本地的记录,如果是--hard 则会清空本地修改记录,也就是你修改的都没有了!!切记!!!
git push -f //强制推送到远程分支
复制代码

转载于:https://juejin.im/post/5bb87626e51d450e6d011433

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值