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

想到作這個,是由於無心中在github上看到了這一個倉庫https://github.com/lunlunshiwo/ChooseCity,作的就是一個城市選擇控件,是用vue寫的,說的是阿里的一道題目,而後想一想本身閒着也是閒着,就動手用react又從新作了一遍。javascript

演示

地址:城市選擇控件css

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

總體效果以下:vue

demo

要求

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

說明

我的採用的路由形式,所以沒有作成一個具體的組件(要組件化也就是把state換成props傳值便可),可是在整個頁面中作了很小單元的拆分。另外「上次定位」的功能暫時未完善,容以後補上。java

技術棧

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

sass

腳手架最開始不支持sass,開啓sass須要以下配置:react

# 安裝依賴包
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中,以下:webpack

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

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

定位

須要定位到當前城市,採用的是百度地圖的定位,須要首先去百度地圖開放平臺上申請一個祕鑰,地址在這裏http://lbsyun.baidu.com/apiconsole/key,進去以後查看js文檔,這裏再也不贅述,能夠本身去了解。git

  • 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。

相關文章
相關標籤/搜索