從入門到上線一個天氣小程序

前言

學習了一段時間小程序,大體過了兩遍開發文檔,抽空作個本身的天氣預報小程序,全當是練手,在這記錄下。小程序開發的安裝、註冊和接入等流程就不羅列了,在小程序接入指南已經寫得很清楚了,如下只對開發過程經常使用到得一些概念進行簡單梳理,類比 Vue 增強記憶,最後選取我的項目天氣小程序中要注意的幾點來講明。css

minWeather

歡迎掃碼體驗html

minWeather

源碼請戳這裏,歡迎start~vue

初始化項目目錄結構

安裝好開發者工具,填好申請到的AppID,選好項目目錄,初始化一個普通小程序目錄結構,獲得:git

--|-- pages
    |-- index
      |-- index.js // 首頁js文件
      |-- index.json // 首頁json文件
      |-- index.wxml // 首頁wxml文件
      |-- index.wxss // 首頁wxss文件
    |-- logs
      |-- logs.js // 日誌頁js文件
      |-- logs.json // 日誌頁json文件
      |-- logs.wxml // 日誌頁wxml文件
      |-- logs.wxss // 日誌頁wxss文件
  |-- utils
    |-- util.js // 小程序公用方法
  |-- app.js // 小程序邏輯
  |-- app.json // 小程序公共配置
  |-- app.wxss // 小程序公共樣式表
  |-- project.config.json // 小程序項目配置

能夠看到,項目文件主要分爲.json.wxml.wxss.js類型,每個頁面由四個文件組成,爲了方便開發者減小配置,描述頁面的四個文件必須具備相同的路徑與文件名。github

JSON配置

小程序配置 app.json

app.json配置是當前小程序的全局配置,包括小程序的全部頁面路徑、界面表現、網絡超時時間、底部 tab 等。json

工具配置 project.config.json

工具配置在小程序的根目錄,對工具作的任何配置都會寫入這個文件,使得只要載入同一個項目代碼包,開發則工具會自動恢復當時你開發項目時的個性設置。小程序

頁面配置 page.json

頁面配置 是小程序頁面相關的配置,讓開發者能夠獨立定義每一個頁面的一些屬性,好比頂部顏色,是否下拉等。微信小程序

WXML 模板

WXML 充當相似 HTML 的角色,有標籤,有屬性,可是仍是有些區別:api

  1. 標籤名不同。
    HTML 經常使用標籤 <div><p><span>等,而小程序中標籤更像是封裝好的組件,好比<scroll-view>, <swiper>, <map>,提供相應的基礎能力給開發者使用。
  2. 提供 wx:if,{{}}等模板語法。
    小程序將渲染和邏輯分離,相似於ReactVueMVVM開發模式,而不是讓 JS 操做 DOM

下面針對小程序的數據綁定、列表渲染、條件渲染、模板、事件和應用跟 Vue 類比加深記憶。數組

數據綁定

WXML 中的動態數據均來自對應 Page(或 Component) 的 data,而在 Vue中來自當前組件。

小程序和Vue的數據綁定都使用 Mustache 語法,雙括號將變量包起來。區別是 Vue 中使用Mustache 語法不能做用在 HTML 特性上

<div v-bind:id="'list-' + id">{{msg}}</div>

而小程序做用在標籤屬性上

<view id="item-{{id}}">{{msg}}</view>

列表渲染

Vue 中使用 v-for 指令根據一組數組的選項列表,也能夠經過一個對象的屬性迭代進行渲染,使用 (item, index) in items(item, index) of items 形式特殊語法。

<ul>
  <li v-for="(item, index) in items">
    {{ index }} - {{ item.message }}
  </li>
</ul>

渲染包含多個元素,利用 <template>元素

<ul>
  <template v-for="(item, index) in items">
    <li>{{ index }} - {{ item.message }}</li>
    <li class="divider" role="presentation"></li>
  </template>
</ul>

而在小程序中使用 wx:for 控制屬性綁定一個數組(其實對象也能夠),默認數組的當前項的下標變量爲 index ,當前項變量爲 item

<view wx:for="{{items}}"> {{index}} - {{item.message}} </view>

也能夠用 wx:for-item 指定數組當前元素的變量名,用 wx:for-index 指定數組當前下標的變量名。

<view wx:for="{{items}}" wx:for-index="idx" wx:for-item="itemName">
  {{idx}}: {{itemName.message}}
</view>

渲染一個包含多節點的結構塊,利用 <block> 標籤

<block wx:for="{{items}}">
  <view> {{index}} - {{item.message}} </view>
  <view class="divider" role="presentation"></view>
</block>

條件渲染

Vue 中使用v-ifv-else-ifv-else指令條件渲染,多個元素使用<template>包裹,而小程序中使用wx:ifwx:elseifwx:else來條件渲染,多個組件標籤使用<block>包裹。

模板

Vue 中定義模板一種方式是在 <script> 元素中,帶上 text/x-template 的類型,而後經過一個id將模板引用過去。

定義模板:

<script type="text/x-template" id="hello-world-template">
  <p>Hello hello hello</p>
  <p>{{msg}}</p>
</script>

使用模板:

Vue.component('hello-world', {
  template: '#hello-world-template',
  data () {
    return {
      msg: 'this is a template'
    }
  }
})

而在小程序中,在 <template> 中使用 name 屬性做爲模板名稱,使用 is 屬性聲明須要使用的模板,而後將模板所需的 data 傳入。

定義模板:

<template name="hello-world-template">
  <view>Hello hello hello</view>
  <view>{{msg}}</view>
</template>

使用模板:

<template is="hello-world-template" data="{{...item}}"></template>
Page({
  data: {
    item: {
      msg: 'this is a template'
    }
  }
})

事件

Vue 中,用 v-on 指令監聽 DOM 事件,並在觸發時運行一些 JavaScript 代碼,對於阻止事件冒泡、事件捕獲分別提供事件修飾符.stop.capture的形式

<!-- 阻止單擊事件繼續傳播 -->
<a v-on:click.stop="doThis"></a>
<!-- 添加事件監聽器時使用事件捕獲模式 -->
<!-- 即元素自身觸發的事件先在此處理,而後才交由內部元素進行處理 -->
<div v-on:click.capture="doThis">...</div>

而在小程序中,綁定事件以 keyvalue 的形式,keybindcatch 開頭,而後跟上事件的類型,如 bindtapcatchtouchstart,也可緊跟一個冒號形式,如 bind:tapcatch:touchstartbind 事件綁定不會阻止冒泡事件向上冒泡,catch 事件綁定能夠阻止冒泡事件向上冒泡。

<!-- 單擊事件冒泡繼續傳播 -->
<view bindtap="doThis">bindtap</view>
<!-- 阻止單擊事件冒泡繼續傳播 -->
<view catchtap="doThis">bindtap</view>

採用 capture-bindcapture-catch 分別捕獲事件和中斷捕獲並取消冒泡。

<!-- 捕獲單擊事件繼續傳播 -->
<view capture-bind:tap="doThis">bindtap</view>
<!-- 捕獲單擊事件阻止繼續傳播,而且阻止冒泡 -->
<view capture-catch="doThis">bindtap</view>

引用

Vue 中引用用於組件的服用引入

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'

在小程序中,WXML 提供兩種引用方式 importinclude

在 item.wxml 中定義了一個叫item的template:

<!-- item.wxml -->
<template name="item">
  <text>{{text}}</text>
</template>

在 index.wxml 中引用了 item.wxml,就可使用item模板:

<import src="item.wxml" /> <template is="item" data="{{text: 'forbar'}}" />

include 能夠將目標文件除了 <template> <wxs> 外整個代碼引入:

<!-- index.wxml -->
<include src="header.wxml" /> <view> body </view> <include src="footer.wxml" />
<!-- header.wxml -->
<view> header </view>
<!-- footer.wxml -->
<view> footer </view>

WXSS 樣式

WXSS(WeiXin Style Sheets) 具備 CSS 大部分的特性,也作了一些擴充和修改。

尺寸單位rpx

支持新的尺寸單位 rpx,根據屏幕寬度自適應,規定屏幕寬爲750rpx,免去開發換算的煩惱(採用浮點計算,和預期結果會有點誤差)。

設備 rpx換算px(屏寬/750) px換算rpx(750/屏寬)
iPhone5 1rpx = 0.42px 1px = 2.34rpx
iPhone6 1rpx = 0.5px 1px = 2rpx
iPhone6 Plus 1rpx = 0.552px 1px = 1.81rpx

iPhone6上,換算相對最簡單,1rpx = 0.5px = 1物理像素,建議設計師以 iPhone6 爲設計稿。

樣式導入

使用 @import 語句導入外聯樣式表,注意路徑爲相對路徑。

全局樣式與局部樣式

app.wxss中的樣式爲全局樣式,在 Page (或 Component) 的 wxss文件中定義的樣式爲局部樣式,自做用在對應頁面,並會覆蓋 app.wxss 中相同選擇器。

頁面註冊

小程序是以 Page(Object) 構造頁面獨立環境,app加載後,初始化某個頁面,相似於 Vue 的實例化過程,有本身的初始數據、生命週期和事件處理回調函數。

初始化數據

Vue 同樣,在構造實例屬性上都有一個 data 對象,做爲初始數據。

Vue 中修改 data 中某個屬性值直接賦值便可,而在小程序中須要使用 Page 的實例方法 setData(Object data, Function callback) 才起做用,不須要在 this.data 中預先定義,單次設置數據大小不得超過1024kb。

支持以數據路徑的形式改變數組某項或對象某項屬性:

// 對於對象或數組字段,能夠直接修改一個其下的子字段,這樣作一般比修改整個對象或數組更好
  this.setData({
    'array[0].text': 'changed data'
  })

生命週期回調函數

每一個 Vue 實例在被建立時都要通過一系列的初始化過程,每個階段都有相應鉤子函數被調用,created mounted updated destroyed

vueLifecycle

對於小程序生命週期,分爲 Page 的生命週期和 Component 的生命週期。

Page 的生命週期回調函數有:

  • onLoad 生命週期回調-監聽頁面加載
  • onShow 生命週期回調-監聽頁面顯示
  • onReady 生命週期回調-監聽頁面初次渲染完成
  • onHide 生命週期回調-監聽頁面隱藏
  • onUnload 生命週期回調-監聽頁面卸載
  • onPullDownRefresh監聽用戶下拉動做
  • onReachBotton 頁面上拉觸底事件的處理函數
  • onShareAppMessage 用戶點擊右上角轉發
  • onPageScroll 頁面滾動觸發事件的處理函數
  • onTabItemTap 當前是 tab 頁時,點擊 tab 觸發

Component 的生命週期有:

  • created 在組件實例剛剛被建立時執行
  • attached 在組件實例進入頁面節點樹時執行
  • ready 在組件在視圖層佈局完成後執行
  • moved 在組件實例被移動到節點樹另外一個位置時執行
  • detached 在組件實例被從頁面節點樹移除時執行
  • error 每當組件方法拋出錯誤時執行
  • show 組件所在的頁面被展現時執行
  • hide 組件所在的頁面被隱藏時執行
  • resize 組件所在的頁面尺寸變化時執行

vueLifecycle

wxs

WXS(WeiXin Script)是小程序的一套腳本語言,結合 WXML,能夠構建出頁面的結構。wxs 的運行環境和其餘 JavaScript 代碼是隔離的,wxs 中不能調用其餘 JavaScript 文件中定義的函數,也不能調用小程序提供的API。從語法上看,大部分和 JavaScript是同樣的,如下列出一些注意點和差異:

  • <wxs> 模塊只能在定義模塊的 WXML 文件中被訪問。使用 <include><import> 時, <wxs> 模塊不會被引用到對應的 WXML 文件中;
  • <template> 標籤中,只能使用定義該<template>WXML 文件中定義的 <wxs> 模塊;
  • Date對象,須要使用 getDate 函數,返回一個當前時間的對象;
  • RegExp對象,使用 getRegExp 函數;
  • 使用 constructor 屬性判斷數據類型。

組件間通訊

小程序組件間通訊和Vue 組件間通訊很類似

父組件傳值到子組件

Vue 中,父組件定義一些自定義特性,子組件經過 props 實例屬性獲取,也可經過 wm.$refs 能夠獲取子組件獲取子組件全部屬性和方法。

<!-- 父組件 -->
<blog-post title="A title"></blog-post>
<!-- 子組件 -->
<h3>{{ postTitle }}</h3>
export default {
  props: ['postTitle']
}

一樣的,在小程序中,父組件定義一些特性,子組件經過 properties 實例屬性獲取,不一樣的是,提供了 observer 回調函數,能夠監聽傳遞值的變化。父組件還能夠經過 this.selectComponent 方法獲取子組件實例對象,這樣就能夠直接訪問組件的任意數據和方法。

Component({
  properties: {
    myProperty: { // 屬性名
      type: String, // 類型(必填),目前接受的類型包括:String, Number, Boolean, Object, Array, null(表示任意類型)
      value: '', // 屬性初始值(可選),若是未指定則會根據類型選擇一個
      observer(newVal, oldVal, changedPath) {
        // 屬性被改變時執行的函數(可選),也能夠寫成在methods段中定義的方法名字符串, 如:'_propertyChange'
        // 一般 newVal 就是新設置的數據, oldVal 是舊數據
      }
    },
    myProperty2: String // 簡化的定義方式
  }
})

子組件傳值到父組件

在Vue 中經過自定義事件系統觸發 vm.$emit( eventName, […args] ) 回調傳參實現。

<!-- 子組件 -->
<button v-on:click="$emit('enlarge-text')">
  Enlarge text
</button>
<!-- 父組件 -->
<blog-post
  ...
  v-on:enlarge-text="postFontSize += 0.1"
></blog-post>

一樣的,在小程序中也是經過觸發自定義事件 triggerEvent 回調傳參形式實現子組件向父組件傳遞數據。

<!-- page.wxml -->
<my-component bindcustomevent="pageEventListener2"></my-component>
// my-component.js
Component({
  methods: {
    onTap () {
      this.triggerEvent('customevent', {})
    }
  }
})

天氣預報小程序

說了不少小程序開發的基礎準備,下面就結合我的實際練手項目——天氣預報小程序簡單說明。

物料準備

從需求結果導向,天氣程序首先要能獲取到當前所在地天氣情況,再次能夠自由選擇某地,知道其天氣情況。這樣就須要有獲取天氣的API和搜索地址API。

開發前物料(服務能力)準備好了,接下來就是擼小程序了!

首頁獲取用戶信息、佈局相關

佈局

微信小程序的樣式已支持大部分 CSS 特性,不用再去考慮太多傳統瀏覽器兼容性問題了,佈局方便直接選用 flex 佈局。
好比:

/**app.wxss**/
page {
  background: #f6f6f6;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

獲取用戶信息

首頁首次加載獲取用戶,一般會彈窗提示是否容許獲取用戶信息,用戶點擊容許獲取受權,才能成功獲取用戶信息,展現用戶名和用戶頭像等,小程序爲了優化用戶體驗,使用 wx.getUserInfo 接口直接彈出受權框的開發方式將逐步再也不支持。目前開發環境不彈窗了,正式版暫不受影響。提倡使用 button 組件,指定 open-typegetUserInfo類型,用戶主動點擊後才彈窗。
天氣小程序獲取用戶頭像和用戶名採用的是另外一種方式,使用open-data 能夠直接獲取用戶基礎信息,不用彈窗提示。

<!-- 用戶信息 -->
  <view class="userinfo">
    <open-data type="userAvatarUrl" class="userinfo-avatar"/>
    <text class="userinfo-nickname">{{greetings}},</text>
    <open-data type="userNickName"/>
</view>

城市拼音首字母錨點

上下滑動城市列表,當滑過當前可視區的城市拼音首字母,右側字母索引欄對應的字母也會切換到高亮顯示。

要知足當前的這個場景需求,首先要爲城市列表的拼音首字母標題添加標誌(id),當<scroll-view>滾動觸發時獲取各個標誌位距離視窗頂部的位置,此處用到小程序 WXML 節點API NodesRef.boundingClientRect(function callback) 獲取佈局位置,相似於 DOMgetBoundingClientRect。距離大小爲最小負數的標誌位是當前剛滑過的,右側索引欄對應字母應當高亮。

<!-- searchGeo.wxml -->
<scroll-view bindscroll="scroll" scroll-y="{{true}}">
  <!-- 城市列表... -->
</scroll-view>
Page({
  // ...
  // 城市列表滾動
  scroll () {
    wx.createSelectorQuery().selectAll('.city-list-title')
      .boundingClientRect((rects) => {
        let index = rects.findIndex((item) => {
          return item.top >= 0
        })
        if (index === -1) {
          index = rects.length
        }
        this.setIndex(index - 1)
      }).exec()
  },
  // ...
點擊右側字母索引欄的字母,城市列表自動滑動使得對應字母標題可視

知足這個需求場景,能夠利用 <scroll-view> 組件的 scroll-into-view 屬性,因爲已有拼音首字母標題添加標誌(id),只需將當前點擊的字母對應的元素id滾動到可視便可。須要注意:

  • 頻繁 setData 形成性能問題,在這裏過濾重複賦值;
  • 因爲設置了 <scroll-view> 爲動畫滾動效果,滾動到標誌元素位置須要時間,途中可能會通過其它標誌元素,不能當即設置索引焦點,要有必定延時(還沒找到其它好解決方案,暫時這樣)
// 點擊索引條
  tapIndexItem (event) {
    let id = event.currentTarget.dataset.item
    this.setData({
      scrollIntoViewId: `title_${id === '#' ? 0 : id}`
    })

    // 延時設置索引條焦點
    setTimeout(() => {
      this.setData({
        barIndex: this.data.indexList.findIndex((item) => item === id)
      })
    }, 500)
  },

minWeather

頻繁觸發節流處理

頻繁輸入,或者頻繁滾動,回調觸發會形成性能問題,而其接口也有限定調用頻率,這樣就須要作節流處理。節流是再頻繁觸發的狀況下,在大於必定時間間隔才容許觸發。

// 節流
const throttle = function(fn, delay) {
  let lastTime = 0
  return function () {
    let nowTime = Date.now()
    if (nowTime - lastTime > delay || !lastTime) {
      fn.apply(this, arguments)
      lastTime = nowTime
    }
  }
}

具體對一些場景,好比騰訊位置服務提供的關鍵字搜索地址,就限定5次/key/秒,很容易就超了,能夠作節流處理

Page({
  // ...
  // 輸入搜索關鍵字
  input: util.throttle(function () {
    let val = arguments[0].detail.value
    if (val === '') {
      this.setData({
        suggList: []
      })
      this.changeSearchCls()
      return false
    }

    api.getSuggestion({
      keyword: val
    })
      .then((res) => {
        this.setData({
          suggList: res
        })
        this.changeSearchCls()
      })
      .catch((err) => {
        console.error(err)
      })
  }, 500),
  // ...
})

minWeather

對於上面城市列表滾動,獲取標誌元素位置也應用節流處理。

總結

小程序的基本入門學習門檻不高,小程序的設計應該借鑑了不少如今流行的框架,若是有 ReactVue 的基礎會有不少似曾相識的感受,固然,在深刻的探索過程還有不少「坑」要跨越,本文只是簡單的梳理,具體問題還能多看文檔小程序社區,還有什麼錯誤歡迎指正哈,完~

相關文章
相關標籤/搜索