【React】開發一個城市選擇控件


想到做這個,是因為無意中在github上看到了這一個倉庫https://github.com/lunlunshiwo/ChooseCity,做的就是一個城市選擇控件,是用vue寫的,說的是阿里的一道題目,然后想想自己閑着也是閑着,就動手用react又重新做了一遍。

演示

地址:城市選擇控件

github: https://github.com/Rynxiao/city-selector

整體效果如下:

demo

要求

  • 可定位到當前所在城市,可支持傳城市
  • 下次打開優先選取上次定位城市,如本次定位和上次不一樣,則取本地城市,同時展示最近選擇的城市,最近選擇的城市可配
  • 城市列表按字母分組,如B組:北京、包頭,同時左側帶A-Z導航符條,點擊對應字母定位至對應的組位置,如點擊C則定位至C組,同時彈出提示為C
  • 支持城市搜索,頁頭帶搜索框,可支持聯想功能,注意性能
  • 選擇對應城市,會將對應城市數據帶回給使用頁面
  • 支持單個頁面上同時存在多個城市組件
  • 頁面用flex布局(css)

說明

個人采用的路由形式,因此沒有做成一個具體的組件(要組件化也就是把state換成props傳值即可),但是在整個頁面中做了很小單元的拆分。另外“上次定位”的功能暫時未完善,容之后補上。

技術棧

采用的是react官網提供的腳手架create-react-app,因此整體技術是react,采用webpack進行打包構建,jest測試。同時在此基礎上新增了一些東西。

sass

腳手架最開始不支持sass,開啟sass需要如下配置:

# 安裝依賴包
npm install --save node-sass-chokidar
npm install --save npm-run-all

# 腳本中增加build-css與watch-css
# 修改start和build命令,讓其可以同時運行多個命令
"scripts": {
+    "build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/",
+    "watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive",
     "test": "react-scripts test --env=jsdom",
-    "start": "react-scripts start",
-    "build": "react-scripts build",
+    "start-js": "react-scripts start",
+    "start": "npm-run-all -p watch-css start-js",
+    "build-js": "react-scripts build",
+    "build": "npm-run-all build-css build-js"
}

# .gitignore中去除生成的css文件
src/**/*.css

react-router

npm install --save react-router-dom

安裝依賴之后,增加了一個全局入口,在src/container/index.js中,如下:

<Switch>
    <Route exact path="/" component={ App } />
    <Route path="/city" component={ City } />
</Switch>

增加兩個頁面,路由分別如上配置。

定位

需要定位到當前城市,采用的是百度地圖的定位,需要首先去百度地圖開放平台上申請一個秘鑰,地址在這里http://lbsyun.baidu.com/apiconsole/key,進去之后查看js文檔,這里不再贅述,可以自己去了解。

  • src/public/index.html中加入百度開放平台提供的腳本鏈接,填上自己的秘鑰。
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=your_ak"></script>
  • src/services/locationServices.js中加入定位代碼
async function getLocalCity() {
    return new Promise(resolve => {
        var myCity = new window.BMap.LocalCity();
        myCity.get(result => {
            resolve(result.name);
        });
    }); 
}

獲取城市數據

獲取城市的接口API,歷經千辛萬苦終於在網上找到了一個能用的【這個接口有可能隨時會掛喲😁😁😁】,但是數據格式可能不太滿意,只能自己轉化。如果不想用這個格式,你也可以自己起一個后台服務器,然后輸出你自己喜歡的格式,這里我算是偷懶了。

之前的格式是按照省份區分的:

before-format-json

格式化之后的格式是按照拼音字母來區分的:

after-format-json

設置代理

因為請求的地址域名不一致,肯定會有跨域問題,這里在package.json中設置了代理,如下:

"proxy": "http://www.msece.com"

獲取城市

// src/services/cityServices.js
async function getAllCities() {
    const json = await axios.get(CITY_API);
    return formatCites(json);
}

UI

UI方面自己沒有什么創意,所以使用了阿里的antd-mobile,可以去這里看:antd-mobile

// 安裝依賴
npm install antd-mobile --save

// 按需加載
// 1. 安裝依賴
npm install react-app-rewired --save-dev
npm install babel-plugin-import --save-dev

// 2. 在package.json中,將script中的 react-scripts 換成 react-app-rewired

// 3. 在根目錄下建立config-overrides.js,內容如下:
const { injectBabelPlugin } = require('react-app-rewired');

module.exports = function override(config, env) {
    config = injectBabelPlugin(['import', { libraryName: 'antd-mobile', style: 'css' }], config);
    return config;
};

// 4. 更改引入方式
// before
import Button from 'antd-mobile/lib/button';
// after
import { Button } from 'antd-mobile';

coding

進行了組件的拆分,主要為:

  • 頭部
  • 搜索區域
  • 需要定位的城市區域(分為最近城市和熱門城市)
  • 列表區域
  • 右側導航區域
  • 搜索彈層區域

具體可以參看src/components/city下的組件

最近選擇城市

采用的是本地localstorage進行存儲,默認最多存儲兩個,后選擇的城市會替換掉第一個,如果選擇的城市中有相同的,則不進行替換。頁面公用本地存儲,若不想公用,可以在之后區分id即可。

熱門城市

熱門城市是自己預先定義的,如果不希望預先定義,也可以參照某些API,這里算是偷懶。

導航條滑動

之前的寫過一篇文章移動端效果之IndexList,具體實現可以參看。

搜索聯動

支持中/英文搜索,中文搜索是進行了全數據遍歷,英文搜索是進行了首字符判斷,然后再進行子集遍歷。在搜索方面,使用了函數節流,如果在1秒中之內還沒有輸入完成,則必須進行一次搜索。

// src/utils/index.js
function throttle(fn, wait = 500, period = 1000) {
    let startTime = new Date().getTime();
    let timeout;
    return (...args) => {
        return new Promise(resolve => {
            const now = new Date().getTime();
            if (now - startTime >= period) {
                startTime = now;
                resolve(fn.apply(null, args));
            } else {
                timeout && clearTimeout(timeout);
                timeout = setTimeout(() => {
                    resolve(fn.apply(null, args));
                }, wait);
            }
        }); 
    }
}

// src/pages/city/City.js
const searchCity = throttle(searchCityByName);

onSearchInput = async value => {
    if (!value) {
        this.hideMenuDialog();
        return;
    }

    const { labels, city } = this.state;
    const cities = await searchCity(value, labels, city);
    this.setState({  
        searchArea: true, 
        searchCities: transformCityMenuData(cities) 
    });
}

部署方面

本來是想使用heroku來部署應用的,但是經過一番折騰之后,在heroku的日志中看到服務是已經啟動了的,但是外網訪問不了,還需要技術支持_

heroku-logs

后來只能就部署到自己的騰訊雲上面去了,案例地址為:城市選擇控件

總結

自己看到后就想寫來玩玩而已,在其中也進一步了解了測試、react-router 4的用法,以及螞蟻金服的UI庫,也不是說沒有收獲。在項目中,也經過了一系列的代碼重構,比如組件拆分、公共類庫提取等等,寫案例的同時也是在訓練自己的意識,特意分享出來,大家共勉。

最后,代碼倉庫為:https://github.com/Rynxiao/city-selector,如果覺得有點意思,多謝star。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM