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

栏目: 数据库 · 发布时间: 6年前

内容简介:最近学了:point_right:在线demo点击这里:point_right:

最近学了 react ,一直想做一个项目,没有什么好的主意。因为自己也要租房住,就想到了 租房App 这个idea,参考豆瓣租房小程序,着手了这样一个简陋的前后端项目:smile:。

:point_right:在线demo点击这里

:point_right: 项目源码点击这里 ,你可以下载在本地运行,如果对你有帮助可以点一下 star 哈:grin:

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

二、前端

  1. 项目技术栈 react+react-router+react-redux ,用 create-react-app 脚手架生成。UI方面采用 Ant Design Mobile :point_right:官方地址。
  2. css 方案是css-in-js,采用 style-jsx ,:point_right: github地址 ,可参考掘金上的一篇文章:point_right:点击这里。
  3. 由于是移动端,避免不了 适配 问题,采用 vm/vh 适配,具体同样可以参考掘金的:point_right:这篇文章。
  4. 在结合 2、3 两点时,由于要添加配置项,但我不想在项目中 run eject 弹出,于是用了 react-app-rewired 改写配置,这样就不用弹出命令了。:point_right: github地址
  5. 权限路由。思路是根据遍历路由配置表,需要权限的走权限路由,不需要的走原来的路由。具体可看项目中的 router 部分。
  6. 图标采用 iconfont SVG 处理
    做一个跑通前后端的`豆瓣租房`移动端webApp
    具体用法查看官方地址。

遇到的问题:

  • (未解决)在dev开发环境下修改scss中的css,不会实时编译更新
  • (未解决)IOS下通过 focus 事件不能唤起键盘,安卓下可以 。为了有个较好的用户体验,我在 登录,搜索 页面打开时,让输入框自动 focus 唤起键盘,经 IOS 真机实践,只能触发 focus 事件,但是不会唤起键盘, 安卓 正常。查阅资料后是 IOS 做的限制,( IOS 还有和音频、视频不能自动播放的限制)。需要用户主动点击输入框后,才可以唤起键盘。下一次重新打开就能自动唤起键盘了,很坑的一点:unamused:! 目前无解
  • (解决) 从首页列表点击详情时候,返回到首页,会 重新请求加载 ,并且 滚动位置丢失 ,用户体验十分不好。这里为了学习 redux 我用 redux (也可用 react 的新 context api )解决,因此在路由方面也用了 react-redux-router ,但已不维护,改为 connect-react-router github点这里 。思路是首次获取房源列表,然后存入 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
    做一个跑通前后端的`豆瓣租房`移动端webApp
    做一个跑通前后端的`豆瓣租房`移动端webApp
    上图我们可以看到是宽度的问题,正常应该为 110px ,而错误的时候则才 80px暂时解决方法:移除该路由懒加载,直接加载:smirk:

项目优化:

  1. 路由懒加载,方案: react-loadable ,添加 loading 提示
  2. 图片懒加载,方案: lazyload
    • 封装成一个组件:point_right: 具体代码
    • 这里需要说明在你的网站上加载豆瓣的图片都是 403 的,因此我们需要用到下面这个网站来加载图片点击这里,使用方法 https://images.weserv.nl/?url=+图片原来的地址 , 详细参考上一步代码中的链接
  3. ajax 视情况添加 loading 提示,添加 CSS3 动画,使交互更加友好。

三、后端

  • 采用 koa2 + koa-router + mongodb + jsonWebToken 。最主要的是需要注意 异步和异常处理 的问题。
  • 数据库方面用了 Mongoose 来操作。 Mongoose 是在node.js异步环境下对 mongodb 进行便捷操作的对象模型工具。更多详细说明请看官方文档::point_right:点击这里

3.1 爬取豆瓣小组数据

  • 用到的 http 库是 axios
  • 定时任务库, node-schedule 。github::point_right: 点击这里
  • 爬虫库 cheerio ,它的用法十分简单。
const cheerio = require('cheerio')
const $ = cheerio.load('<h2 class="title">Hello world</h2>')
复制代码

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

整个爬取的流程:

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

注意:豆瓣会限制一个时段内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 列表:point_right: 查看代码 ,每次请求都带上随机中的一个。

3.2 存入数据库

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

db.Houses.insertMany([your array data])
复制代码

3.3 写接口(路由)

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

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.

官方文档链接::point_right:[点击这里](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"
    }
  }
复制代码
  • 线上环境 nginx 配置代理 :point_right:参考这里
location  /api/ {
   proxy_pass   http://localhost:3003;
}
复制代码

六、git相关

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

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

以上所述就是小编给大家介绍的《做一个跑通前后端的`豆瓣租房`移动端webApp》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms

Algorithms

Sanjoy Dasgupta、Christos H. Papadimitriou、Umesh Vazirani / McGraw-Hill Education / 2006-10-16 / GBP 30.99

This text, extensively class-tested over a decade at UC Berkeley and UC San Diego, explains the fundamentals of algorithms in a story line that makes the material enjoyable and easy to digest. Emphasi......一起来看看 《Algorithms》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试