Natsuha - 用Taro寫個天氣微信小程式

YanceyOfficial發表於2019-03-11

去年年底 o2 開源了 Taro,一直手癢癢沒去玩。考慮到 wx 的稽核制度,所以決定寫個工具類小程式。趕在 Taro 喜提第 2000 個 issues 之際,Natsuha 也終於上線了。原始碼全部釋出(除涉及私鑰部分,GitHub有說明),文章後面會貼出一些仍需優化的點,歡迎大家一起討論。

前言

Natsuha Weather 已開源,歡迎大家一起折騰,給個 star 也是極好的~ GitHub Repo

Scan the QR Code by WeChat

技術棧是 Taro + mobx + TypeScript,介面來自 Yahoo Weather API,當然設計也是參 (chao) 考 (xi) 的 Yahoo Weather. 介面有時會訪問失敗,尤其是晚上,我也沒辦法啊。?

功能

  • 下拉重新整理
  • 華氏溫度、攝氏溫度切換
  • 分時展示一天的天氣預報
  • 展示未來10天的天氣預報
  • 展示當前風向、風速
  • 展示日出日落、月相等資訊
  • 展示一天內的降水預報
  • 城市天氣檢索
  • 顯示檢索記錄

踩坑

小程式篇

雲開發解決 Bei An 問題

由於眾所周知的原因,wx 小程式無法呼叫未 bei an 的介面,哪怕是在開發環境。所以我們用雲開發雲函式來 “反代” 介面,下面通過一個例子說一下技術要點。

首先在根目錄的 project.config.json 檔案裡新增 "cloudfunctionRoot": "functions/",然後在根目錄建立資料夾 functions. 並點選右鍵建立一個新的雲函式,比如我們叫 getRegion

雲函式開發

因為我們的目標是通過雲函式請求一個未 bei an 的介面,所以為了更方便的處理非同步請求,我們引入 request-promise 這個庫。

通過硬碟開啟進入到這個雲函式的資料夾,然後安裝依賴:

yarn add request request-promise
複製程式碼

接下來我們在 index.js 中寫邏輯,直接上程式碼。雲函式通過 event 物件來獲取前端傳過來的引數,然後通過 Promise 物件將結果返回。這個例子中我們需要拿到region

// 雲函式入口檔案
const cloud = require('wx-server-sdk')
const rp = require('request-promise')

cloud.init()

exports.main = async (event, context) => {
  const region = event.region;
  const res = await rp({
    method: 'get',
    uri: `https://www.yahoo.com/news/_tdnews/api/resource/WeatherSearch;text=${region}`,
    json: true
  }).then((body) => {
    return {
      regionList: body
    }
  }).catch(err => {
    return err;
  })
  return res;
}
複製程式碼

接下來是前端發請求了,注意這裡不能再用 Taro.request(), 而是雲函式獨有的 wx.cloud.callFunction(), 因為我現在的 Taro 版本尚未實現 Taro.cloud.callFunction(),所以直接用 wx 打頭即可。

首先封裝一下 wx.cloud.callFunction(),其實感覺什麼卵用?:

export const httpClient = (url: string, data: any) => new Promise((resolve, reject): void => {
  wx.cloud.callFunction({
    name: url,
    data,
  }).then(res => {
    resolve(res.result);
  }).catch(e => {
    reject(e)
  });
});
複製程式碼

然後我們在 store 裡面寫邏輯,這樣基本上就解決了資料請求的坑。

  public getRegion = (text: string) => {
    httpClient('getRegion', {
        region: encodeURI(text),
      })
      .then((res: any) => {
        runInAction(() => {
          if (res.regionList) {
            this.regionList = res.regionList;
          }
        });
      })
      .catch(() => {
        setToast(toastTxt.cityFail);
      });
  };
複製程式碼

?題外話:因為當前版本尚未實現 Taro.cloud.callFunction(),所以 lint 會報錯,雖然不影響使用,大家有什麼好的方法,可以說一下。

地理資訊授權問題

在這個專案裡,我們需要通過小程式拿到的經緯度來反查城市資訊,而小程式獲取經緯度需要使用者授權。這裡有個坑,當使用者拒絕授權後,小程式預設詢問授權的 dialog 在一段時間內不會重複彈出,所以我們必須手動將使用者引導到授權頁面。

以前小程式有個介面叫做 wx.openSetting(),但 tx 把它廢掉了,現在只能讓使用者點選一個特定的按鈕。

為此我做了一個 modal,這裡貼出關鍵程式碼。

<Button openType='openSetting' onOpenSetting={() => this.onOpenSetting()}>
  OK
</Button>
複製程式碼

首先我們必須給按鈕宣告 openType='openSetting',這樣當使用者點選了之後就會跳轉到設定頁面。

其次,我們需要在使用者離開授權頁面時,也就是點選了左上角那個返回按鈕時,再次去檢查一下使用者的授權情況。所以我們要新增 onOpenSetting={() => this.onOpenSetting(),不得不吐槽這個事件命名,明明應該叫做 onLeaveSetting才合理。

onOpenSetting() 方法中我們再次執行判斷使用者是否授權的方法,未授權的話接著彈 modal,否則放行請求相應的資料介面。

文字有些累,直接看圖。

授權圖解

無法用傳統方式清空文字框文字

當使用者關閉搜尋 dialog 時,文字框的文字應當被清空,所以一開始寫成下面的這樣,即點選關閉按鈕時將 inputValue = '' ,然而發現不行。

<Input
  type='text'
  value={inputValue}
  placeholder='Enter City or ZIP code'
  onInput={e => handleInputTextChange(e)}
/>

<Button onClick={() => hideSearchDialog()}>Close</Button>
複製程式碼

查了一下官方文件,必須將 InputButton 包裹在一個 Form 下,且要給關閉按鈕加上 formType='reset',最後給 Form 新增 onReset 事件指向關閉 dialog 的方法。

<Form onReset={() => hideSearchDialog()}>
  <Input
    className={styles.input}
    type='text'
    placeholder='Enter City or ZIP code'
    onInput={e => handleInputTextChange(e)}
  />
  <Button formType='reset'>Close</Button>
</Form>;
複製程式碼

Taro篇

大多是編譯問題和它 webpack 配的問題,相應的我都提了 issue,有興趣的話可以跟進。

Taro 編譯會忽略模版兩個之間的空格

舉個例子,<Text>day - night</Text>,可以正常編譯,頁面可以正常看到 day - night,但是假如是變數,就會被編譯成 day- night,注意,空格被吃掉了。

const day = 'day'
const night = 'night'

<Text>{day} - {night}</Text>
複製程式碼

我提了個 issue #2261,然並沒人鳥我,有興趣可以跟進一下。

ts 不能識別wx

因為用到了雲開發,而 Taro 現階段還沒有Taro.cloud(...),所以在使用原生的wx.cloud(...)時, ts 肯定會報錯。

css module 等靜態檔案 找不到路徑

一開始用的import來引入靜態檔案,但報“找不到路徑”,可以看下圖(但不影響使用)。提了個 issue #2213, 按照大佬的回覆修改也沒解決問題,實在受不了一片紅,索性改成了commonJS.

找不到模組

Problem

下面是專案中存在的一些問題,有興趣的話歡迎大家一起討論。

圖片載入不友好

介面圖片的 url 來自aws,因為眾所周知的原因,圖片經常會掛掉, 所以有必要在圖片掛掉的時候觸發onError事件,然後給使用者一個提示。

因為小程式不支援new Image(),所以只能用官方提供的Image元件,幸好這個 元件支援onLoadonError事件。

載入失敗的問題解決了,但因為aws的速度太慢,所以正常載入時也很不友好(可以自行體會)

做了一些嘗試,比如先載入縮圖,再展示完整圖片,但介面提供的最小尺寸的圖片也已經達到了 70 多 k,並且該死的 Yahoo 恰好將圖片 url 控制大小的那段用了加密,所以這個方式 pass 掉了。

搜尋輸入框加個節流

現在的做法是在 store 的 構造器加個節流,但不知道這樣合不合理。

  construtor() {
    this.getRegion = _.debounce(this.getRegion, 150);
  }
複製程式碼

TODO

  • 國際化
  • 效能優化
  • 圖片載入優化
  • Jest 搞起來(初始化已搭好)
  • Travis CI 搞起來(初始化已搭好)
  • 將搜尋模組放到一個新頁面(強行加個路由?)

最後

老子再也不寫小程式了!

老子再也不寫小程式了!

相關文章