學習了一段時間小程序,大體過了兩遍開發文檔,抽空作個本身的天氣預報小程序,全當是練手,在這記錄下。小程序開發的安裝、註冊和接入等流程就不羅列了,在小程序接入指南已經寫得很清楚了,如下只對開發過程經常使用到得一些概念進行簡單梳理,類比 Vue
增強記憶,最後選取我的項目天氣小程序中要注意的幾點來講明。css
歡迎掃碼體驗html
源碼請戳這裏,歡迎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
app.json配置是當前小程序的全局配置,包括小程序的全部頁面路徑、界面表現、網絡超時時間、底部 tab 等。json
工具配置在小程序的根目錄,對工具作的任何配置都會寫入這個文件,使得只要載入同一個項目代碼包,開發則工具會自動恢復當時你開發項目時的個性設置。小程序
頁面配置 是小程序頁面相關的配置,讓開發者能夠獨立定義每一個頁面的一些屬性,好比頂部顏色,是否下拉等。微信小程序
WXML
充當相似 HTML
的角色,有標籤,有屬性,可是仍是有些區別:api
HTML
經常使用標籤 <div>
,<p>
,<span>
等,而小程序中標籤更像是封裝好的組件,好比<scroll-view>
, <swiper>
, <map>
,提供相應的基礎能力給開發者使用。wx:if
,{{}}等模板語法。React
,Vue
的MVVM
開發模式,而不是讓 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-if
、v-else-if
、v-else
指令條件渲染,多個元素使用<template>
包裹,而小程序中使用wx:if
、wx:elseif
、wx: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>
而在小程序中,綁定事件以 key
,value
的形式,key
以 bind
或 catch
開頭,而後跟上事件的類型,如 bindtap
、catchtouchstart
,也可緊跟一個冒號形式,如 bind:tap
、catch:touchstart
。bind
事件綁定不會阻止冒泡事件向上冒泡,catch
事件綁定能夠阻止冒泡事件向上冒泡。
<!-- 單擊事件冒泡繼續傳播 --> <view bindtap="doThis">bindtap</view> <!-- 阻止單擊事件冒泡繼續傳播 --> <view catchtap="doThis">bindtap</view>
採用 capture-bind
、capture-catch
分別捕獲事件和中斷捕獲並取消冒泡。
<!-- 捕獲單擊事件繼續傳播 --> <view capture-bind:tap="doThis">bindtap</view> <!-- 捕獲單擊事件阻止繼續傳播,而且阻止冒泡 --> <view capture-catch="doThis">bindtap</view>
在 Vue
中引用用於組件的服用引入
import ComponentA from './ComponentA' import ComponentC from './ComponentC'
在小程序中,WXML
提供兩種引用方式 import
和 include
。
在 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(WeiXin Style Sheets) 具備 CSS 大部分的特性,也作了一些擴充和修改。
支持新的尺寸單位 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
。
對於小程序生命週期,分爲 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
組件所在的頁面尺寸變化時執行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-type
爲 getUserInfo
類型,用戶主動點擊後才彈窗。
天氣小程序獲取用戶頭像和用戶名採用的是另外一種方式,使用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)
獲取佈局位置,相似於 DOM
的 getBoundingClientRect
。距離大小爲最小負數的標誌位是當前剛滑過的,右側索引欄對應字母應當高亮。
<!-- 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) },
頻繁輸入,或者頻繁滾動,回調觸發會形成性能問題,而其接口也有限定調用頻率,這樣就須要作節流處理。節流是再頻繁觸發的狀況下,在大於必定時間間隔才容許觸發。
// 節流 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), // ... })
對於上面城市列表滾動,獲取標誌元素位置也應用節流處理。
小程序的基本入門學習門檻不高,小程序的設計應該借鑑了不少如今流行的框架,若是有 React
或 Vue
的基礎會有不少似曾相識的感受,固然,在深刻的探索過程還有不少「坑」要跨越,本文只是簡單的梳理,具體問題還能多看文檔和小程序社區,還有什麼錯誤歡迎指正哈,完~