作一個跑通先後端的`豆瓣租房`移動端webApp

1、前言

最近學了react,一直想作一個項目,沒有什麼好的主意。由於本身也要租房住,就想到了租房App這個idea,參考豆瓣租房小程序,着手了這樣一個簡陋的先後端項目😄。
👉在線demo點擊這裏
👉項目源碼點擊這裏,你能夠下載在本地運行,若是對你有幫助能夠點一下star哈😁css

// 你可使用npm或yarn
yarn install
運行你的數據庫 // 必須!!!
yarn server // 運行服務器, 鏈接的數據庫在server目錄下的config.js裏配置
yarn start // 運行項目
複製代碼

2、前端

  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. 在結合二、3兩點時,因爲要添加配置項,但我不想在項目中run eject彈出,因而用了react-app-rewired改寫配置,這樣就不用彈出命令了。👉 github地址
  5. 權限路由。思路是根據遍歷路由配置表,須要權限的走權限路由,不須要的走原來的路由。具體可看項目中的router部分。
  6. 圖標採用iconfont SVG處理
    iconfont
    具體用法查看官方地址

遇到的問題:

  • (未解決)在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
    search_placeholder
    search_placeholder
    上圖咱們能夠看到是寬度的問題,正常應該爲110px,而錯誤的時候則才80px暫時解決方法:移除該路由懶加載,直接加載😏

項目優化:

  1. 路由懶加載,方案: react-loadable,添加loading提示
  2. 圖片懶加載,方案:lazyload
    • 封裝成一個組件👉 具體代碼
    • 這裏須要說明在你的網站上加載豆瓣的圖片都是403的,所以咱們須要用到下面這個網站來加載圖片 點擊這裏,使用方法 https://images.weserv.nl/?url=+圖片原來的地址, 詳細參考上一步代碼中的連接
  3. ajax視狀況添加loading提示,添加CSS3動畫,使交互更加友好。

3、後端

  • 採用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同樣的方式取到頁面的元素。官方文檔:👉點擊這裏前端

整個爬取的流程:node

  • 初始化的時候判斷是否大於最大存儲的數據長度(此項目中設置了數據庫最多儲存5000條數據),若是超過,則執行刪除,反之跳過。
  • 開啓一個定時任務,天天的0.00am開始爬取=>爬取列表頁面=>存入數據庫=>若是失敗,不會爬取該條tid
  • 的詳情頁,反之繼續爬取詳情頁。
  • 爬蟲提取信息用到了一些正則表達式,提取房租、聯繫方式、房型、所在地區等等。具體代碼:👉點擊這裏。其中參考了👉這篇文章中的一些正則表達式。

注意:豆瓣會限制一個時段內Ip的訪問次數,所以須要咱們作一些調整。react

  • 列表頁面每一頁、 詳情頁每一條數據的爬取的間隔時間保證是不一樣的。(定時器+隨機數時間)(貌似沒什麼卵用?)
// 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以下jquery

db.Houses.insertMany([your array data])
複製代碼

3.3 寫接口(路由)

須要注意的是部分路由(須要用戶登陸後才能夠訪問的接口)header中須要傳遞token才能訪問,所以添加路由中間件校驗經過校驗後才容許訪問。詳細代碼查看這裏。關鍵代碼以下👇ios

const jwt = require('jsonwebtoken');
const token = ctx.header['x-token'];
if(token){
	解析token獲得用戶信息
	進入下一個中間件
}else {
	返回錯誤須要傳遞token
}
複製代碼

4、數據庫mogodb相關

4.1 修改數據庫相關結構

開始設計數據庫的時候,設置價格字段prices是數組,後以爲字符串就能夠了。因而在原數據庫的基礎上修改數據格式字段名prices=>pricenginx

  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來存儲git

db.houses.insert({"tid": NumberInt(666)})
複製代碼

4.3 遇到的問題

爬蟲爬取貼子的時候,會爬到相同的貼子,而咱們是不須要這些重複的。這裏的問題是在插入重複值的時候,出現錯誤以後不會繼續插入剩下的數據,這是很坑的一點。下面是解決方法:github

  • 先設置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.web

大意就是指定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)
複製代碼

5、部署相關(跨域處理)

  • 開發階段 可在項目中的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;
}
複製代碼

6、git相關

有時候提交了錯誤的代碼又想回退版本,就須要回退遠程git倉庫的代碼,再從新提交。 👉更多用法參考這裏

git_reflog

git reflog // 查看提交列表, 如我須要撤回到第二條提交記錄,也就是紅線下的那條
git reset --soft 3a2a12d // 這裏的參數--soft表示保留本地修改記錄, --hard 表明保存本地的記錄,若是是--hard 則會清空本地修改記錄,也就是你修改的都沒有了!!切記!!!
git push -f //強制推送到遠程分支
複製代碼
相關文章
相關標籤/搜索