小程序基礎入門
一、課程介紹
二、本章任務
-
了解小程序的概念以及優勢和劣勢
-
掌握小程序的申請和創建流程
-
掌握小程序的基本模板語法
-
掌握小程序代碼組成
-
了解小程序的渲染模型
-
掌握小程序事件的使用
三、本章目標
-
了解小程序是什么,以及小程序的優勢和劣勢
-
能夠申請並且創建小程序
-
能夠獨立完成小程序界面的搭建和開發
-
實現Todo List的功能
四、知識點
1. 小程序介紹
小程序是什么?
-
小程序是一種新的開放能力,開發者可以快速地開發一個小程序。小程序可以在微信內被便捷地獲取和傳播,同時具有出色的使用體驗。
-
任何一個普通的開發者,經過簡單的學習和練習后,都可以輕松地完成一個小程序的開發和發布。
小程序的發展史
-
早年,隨着微信越來越流行,微信逐漸成為移動互聯網中一個主要的入口,越來越多的人會通過微信來分享和瀏覽網站。
-
微信基於WebView實現了前端界面的渲染,為了豐富微信中H5頁面的功能,微信提供了JS-SDK,開放了拍攝、錄音、語音識別、二維碼、地圖、支付、分享、卡券等幾十個API。給所有的 Web 開發者打開了一扇全新的窗戶,讓所有開發者都可以使用到微信的原生能力,去完成一些之前做不到或者難以做到的事情了。
-
JS-SDK 解決了移動網頁能力不足的問題,通過暴露微信的接口使得 Web 開發者能夠擁有更多的能力,然而在更多的能力之外,JS-SDK 的模式並沒有解決使用移動網頁遇到的體驗不良的問題。
-
用戶在訪問網頁的時候,在瀏覽器開始顯示之前都會有一個的白屏過程,在移動端,受限於設備性能和網絡速度,白屏會更加明顯。
-
除了白屏,影響 Web 體驗的問題還有缺少操作的反饋,主要表現在兩個方面:頁面切換的生硬和點擊的遲滯感。
-
-
為了提高用戶體驗和使用的流暢度,微信研發設計並且推出了微信小程序。
微信小程序的優勢和劣勢
優勢
-
微信助理,容易推廣。在微信中,小程序擁有眾多入口,例如附近的小程序、小程序碼、分享、發現-小程序等五十多個的入口。這些入口有助於企業更好的獲取流量,從而進行轉化、變現。
-
使用便捷。用戶在使用小程序時,只需要輕輕點一下就可以使用,更加符合用戶對使用方便、快捷的需求,所以小程序的用戶數量不斷增加。
-
體驗良好,有接近原生app的體驗。在微信生態里,小程序在功能和體驗上是可以秒殺掉 H5 頁面的,H5 頁面經常出現卡頓、延時、加載慢、權限不足等原因,而這些問題在小程序里都不會出現。
-
成本更低,從開發成本到運營推廣成本,小程序的花費僅為APP的十分之一,無論是對創業者還是傳統商家來說都是一大優勢。
不足
-
單個包大小限制為2M,這導致無法開發大型的應用,采用分包最大是16M(這個值一直在變化,以官網為准)。
-
需要像app一樣審核上架,這點相對於H5的發布要麻煩一些。
-
處處受微信限制。例如不能直接分享到朋友圈,涉及到積分,或者虛擬交易的時候,小程序也是不允許的。
如何學習微信小程序
- 借助官方文檔學習。 微信小程序有非常完善的官方文檔,可以在官方文檔上查到我們需要的所有東西。
https://developers.weixin.qq.com/miniprogram/dev/framework/
https://developers.weixin.qq.com/ebook?action=get_post_info&docid=0008aeea9a8978ab0086a685851c0a
- 可以通過社區進行學習,社區提供了很多優質的資源,可以加快小程序的開發效率,提升小程序的開發體驗。
https://github.com/justjavac/awesome-wechat-weapp
2. 小程序的申請和創建流程
賬號的申請
開發小程序首先要進行小程序的賬號申請。
https://mp.weixin.qq.com/wxopen/waregister?action=step1&token=&lang=zh_CN
下載開發工具
這里大家可以從官方網站下載最新版的開發工具。因為這個開發工具一直在更新,所以建議大家直接從官網下載。
https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
獲取AppId
登錄后台管理系統獲取AppId。
創建項目
3. 項目的目錄結構
-
pages:
-
wxml: 編寫小程序界面結構的地方
-
wxss: 編寫小程序樣式的地方
-
json:編寫界面配置的地方
-
js:編寫界面邏輯的地方
-
-
utils: 編寫工具類的地方
-
app.js:創建程序實例的位置
-
app.json: 編寫全局配置地方
-
app.wxss: 編寫全局樣式的地方
-
project.config.json: 項目的配置文件
-
sitemap.json:配置哪些頁面可以被檢索到
4. 小程序常用配置
https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html
配置導航窗口
https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#window
配置頁面
一般情況下我們會給每個頁面創建一個文件夾,所以我們首先在pages下創建一個文件夾。
小程序直接給我們提供了創建頁面的功能,右鍵這個文件夾,點擊創建新建Page即可直接生成頁面所需的文件。
生成文件之后,所有的頁面都需要在app.json的pages屬性中聲明之后,才可以訪問,小程序會默認先加載pages屬性中第一個頁面。如果開發過程如果我們需要調試別的頁面,我們可以添加一個新的編譯模式。
配置tabbar
https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabBar
在移動端經常會涉及到tabbar的使用,小程序直接給我們提供了配置tabbar的方法。
這里需要注意的是,tabbar的圖標不能是線上的地址,需要提前准備好放到項目里,一般情況下,這些靜態資源可以放在assets文件夾下。這些圖標大家可以從阿里矢量圖標庫中進行下載https://www.iconfont.cn/。
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首頁",
"iconPath": "assets/icons/index.png",
"selectedIconPath": "assets/icons/index-active.png"
},{
"pagePath": "pages/my/index",
"text": "我的",
"iconPath": "assets/icons/wode.png",
"selectedIconPath": "assets/icons/wode-active.png"
}]
},
總結
小程序中的配置還有很多,大家也不用全部記住,需要的時候在查就可以了,大家需要掌握的是如何查閱文檔。
5. 小程序模板語法WXML
WXML 全稱是 WeiXin Markup Language,是小程序框架設計的一套標簽語言,結合小程序的基礎組件、事件系統,可以構建出頁面的結構。
標簽的使用
在小程序中沒有H5提供的那些標簽了,這里我們需要使用小程序給我們提供的組件。小程序給我們提供的標簽很多,這里我們就介紹幾個常用的,剩下的大家可以結合文檔使用。
view標簽是我們開發過程中最常用的標簽了,這個就相當於Html中的div。
text標簽也是我們開發中常用的,這個相當於Html中的span
image標簽相當於我們Html中的img。
...
剩下的大家可以結合官方文檔使用
https://developers.weixin.qq.com/miniprogram/dev/component/
數據綁定
用戶界面呈現會因為當前時刻數據不同而有所不同,或者是因為用戶的操作發生動態改變,這就要求程序的運行過程中,要有動態的去改變渲染界面的能力。
在 Web 開發中,開發者使用 JavaScript 通過Dom 接口來完成界面的實時更新。在小程序中,使用 WXML 語言所提供的數據綁定功能,來完成此項功能。
數據定義
在界面對應的JS文件中的data屬性上定義數據
data: {
msg:\"hello world\",
num: 18,
},
引用數據
通過{{}}的方式可以引用數據。
除了引用數據之外,這里還可以進行一些計算,最終顯示的結果是計算之后得到的結果。
<view>{{msg}},{{num + 10}}</view>
小程序中任何需要獲取數據的地方都需要用{{}},包括標簽內的屬性。
邏輯渲染
WXML 中,使用 wx:if="{{condition}}" 來判斷是否需要渲染該代碼塊:
<view wx:if="{{condition}}"> True </view>
使用 wx:elif 和 wx:else 來添加一個 else 塊:
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
因為 wx:if 是一個控制屬性,需要將它添加到一個標簽上。如果要一次性判斷多個組件標簽,可以使用一個 <block/> 標簽將多個組件包裝起來,並在上邊使用 wx:if 控制屬性。
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
除此之外微信小程序還可以通過hidden屬性進行條件渲染。wx:if在不滿足條件的時候會刪除掉對應的DOM,hidden屬性則是通過display屬性設置為none來進行條件渲染。
<view hidden="{{condition}}">
隱藏
</view>
列表渲染
在組件上使用 wx:for 控制屬性綁定一個數組,即可使用數組中各項的數據重復渲染該組件。默認數組的當前項的下標變量名默認為 index,數組當前項的變量名默認為 item。
<!-- array 是一個數組 -->
<view wx:for="{{array}}">
{{index}}: {{item.name}}
</view>
<!-- 對應的腳本文件
Page({
data: {
array: [{
name: '天亮教育',
}, {
name: '尚雲科技'
}]
}
})
-->
使用 wx:for-item 指定數組當前元素的變量名,使用 wx:for-index 指定數組當前下標的變量名。
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
{{idx}}: {{itemName.name}}
</view>
Key
如果列表中項目的位置會動態改變或者有新的項目添加到列表中,並且希望列表中的項目保持自己的特征和狀態(如 <input/> 中的輸入內容, <switch/> 的選中狀態),需要使用 wx:key 來指定列表中項目的唯一的標識符。
wx:key 的值以兩種形式提供:
-
字符串,代表在 for 循環的 array 中 item 的某個 property,該 property 的值需要是列表中唯一的字符串或數字,且不能動態改變。
-
保留關鍵字 this 代表在 for 循環中的 item 本身,這種表示需要 item 本身是一個唯一的字符串或者數字。
當數據改變觸發渲染層重新渲染的時候,會校正帶有 key 的組件,框架會確保他們被重新排序,而不是重新創建,以確保使組件保持自身的狀態,並且提高列表渲染時的效率。
這里大家可以先體會一下key的概念,等到我們介紹到數據變化的時候在演示key作用。
6. 小程序樣式wxss
WXSS(WeiXin Style Sheets)是一套用於小程序的樣式語言,用於描述WXML的組件樣式,也就是視覺上的效果。
WXSS與Web開發中的CSS類似。為了更適合小程序開發,WXSS對CSS做了一些補充以及修改。
尺寸單位
在WXSS中,引入了rpx(responsive pixel)尺寸單位。引用新尺寸單位的目的是,適配不同寬度的屏幕,開發起來更簡單。
在小程序中,規定所有手機的屏幕寬度都是750rpx,所以在不同尺寸的屏幕下1rpx的寬度都不同。
1rpx = (屏幕寬度/750)px
在iphone6手機下,屏幕寬度是375px,所以1rpx=0.5px。
樣式的導入
在小程序中,樣式引用是這樣寫:
@import './test_0.wxss'
由於WXSS最終會被編譯打包到目標文件中,用戶只需要下載一次,在使用過程中不會因為樣式的引用而產生多余的文件請求。
小程序中的樣式選擇器
目前支持的選擇器。
小程序中會大量使用flex布局。多用一用flex布局。
7. 小程序中的JS
小程序中的js和瀏覽器中和node中的區別
瀏覽器中JS:
-
ES
-
DOM
-
BOM
Node中的JS:
-
ES
-
NPM
-
Native
小程序中的JS:
-
ES
-
小程序框架
-
小程序API
小程序中js的模塊化
在小程序中實現JS模塊化,和node中、ES6中是一致的,大家還可以使用之前的方式進行JS的模塊化編程。
小程序中js的加載執行順序
小程序的執行的入口文件是 app.js 。並且會根據其中 require 的模塊順序決定文件的運行順序。
案例:JS文件模塊化
小程序中js的執行環境
小程序目前可以運行在三大平台:
-
iOS平台,包括iOS9、iOS10、iOS11
-
Android平台
-
小程序IDE
8. 小程序中的數據渲染
小程序和瀏覽器中有什么不同
瀏覽器中渲染是單線程的。
而在小程序中的運行環境分成渲染層和邏輯層,第2章提到過 WXML 模板和 WXSS 樣式工作在渲染層,JS 腳本工作在邏輯層。
小程序中如何渲染
我們看看小程序是如何把腳本里邊的數據渲染在界面上的。
WXML模板使用 view 標簽,其子節點用 {{ }} 的語法綁定一個 msg 的變量。
<view>{{ msg }}</view>
在 JS 腳本使用 this.setData 方法把 msg 字段設置成 "Hello World"。
Page({
onLoad: function () {
this.setData({ msg: 'Hello World' })
}
})
從這個例子我們可以看到3個點:
1.渲染層和數據相關。
2.邏輯層負責產生、處理數據。
3.邏輯層通過 Page 實例的 setData 方法傳遞數據到渲染層。
小程序中通訊模型
小程序的渲染層和邏輯層分別由2個線程管理:
渲染層的界面使用了WebView 進行渲染;
邏輯層采用JsCore線程運行JS腳本。
一個小程序存在多個界面,所以渲染層存在多個WebView線程,這兩個線程的通信會經由微信客戶端(下文中也會采用Native來代指微信客戶端)做中轉,邏輯層發送網絡請求也經由Native轉發。
生命周期示意圖
數據驅動的思想
在開發UI的過程中,程序需要維護很多變量狀態,同時要操作對應的UI元素。隨着頁面越來越復雜,我們需要維護的變量越來越多,同時要處理的界面上的交互也越來越多,整個程序會變的越來越復雜。
通常情況下視圖和變量的狀態是相關聯的,如果有某種方法可以讓狀態和視圖綁定在一起,那就可以讓我們省去修改視圖的工作,這個方法就叫做數據驅動。
在有了框架之后,我們一般很少會采用直接去修改視圖的方式,更新界面了,絕大多數情況,都會通過操作數據的方式,來更新視圖。通過數據去驅動視圖渲染。
9. 程序和界面
程序
"小程序"指的是產品層面的程序,而"程序"指的是代碼層面的程序實例,為了避免誤解,下文采用App來代替代碼層面的"程序"概念。
App({
onLaunch: function(options) {},
onShow: function(options) {},
onHide: function() {},
onError: function(msg) {},
globalData: 'I am global data'
})
宿主環境提供了 App() 構造器用來注冊一個程序App,需要留意的是App() 構造器必須寫在項目根目錄的app.js里,App實例是單例對象,在其他JS腳本中可以使用宿主環境提供的 getApp() 來獲取程序實例。
-
onLaunch: 當小程序初始化完成時,會觸發 onLaunch(全局只觸發一次)
-
onShow:當小程序啟動,或從后台進入前台顯示,會觸發 onShow
-
onHide:當小程序從前台進入后台,會觸發 onHide
-
onError:當小程序發生腳本錯誤,或者 API 調用失敗時,會觸發 onError 並帶上錯誤信息。
-
其他字段:可以添加任意的函數或數據到 Object 參數中,在App實例回調用 this 可以訪問。
小程序全局數據
我們在前面說到小程序的JS腳本是運行在JsCore的線程里,小程序的每個頁面各自有一個WebView線程進行渲染,所以小程序切換頁面時,小程序邏輯層的JS腳本運行上下文依舊在同一個JsCore線程中。
在上文中說道App實例是單例的,因此不同頁面直接可以通過App實例下的屬性來共享數據。App構造器可以傳遞其他參數作為全局屬性以達到全局共享數據的目的。
在其他JS中可以通過getApp()獲取App實例,之后可以獲取到定義在App實例上的數據。
Page
一個小程序可以有很多頁面,每個頁面承載不同的功能,頁面之間可以互相跳轉。
頁面構造器
Page({
data: { text: "This is page data." },
onLoad: function(options) { },
onReady: function() { },
onShow: function() { },
onHide: function() { },
onUnload: function() { },
onPullDownRefresh: function() { },
onReachBottom: function() { },
onShareAppMessage: function () { },
onPageScroll: function() { }
})
生命周期
onLoad:生命周期函數--監聽頁面加載,觸發時機早於onShow和onReady
onReady:生命周期函數--監聽頁面初次渲染完成
onShow:生命周期函數--監聽頁面顯示,觸發事件早於onReady
onHide:生命周期函數--監聽頁面隱藏
onUnload:生命周期函數--監聽頁面卸載
數據
WXML可以通過數據綁定的語法綁定從邏輯層傳遞過來的數據字段,這里所說的數據其實就是來自於頁面Page構造器的data字段,data參數是頁面第一次渲染時從邏輯層傳遞到渲染層的數據。
在JS腳本中如果需要獲取到data上的數據,需要通過this.data獲取。
<!-- page.wxml -->
<view>{{text}}</view>
// page.js
Page({
data: {
text: '天亮教育',
},
onLoad(){
console.log(this.data.text)
}
})
如果涉及到更新,這里可以調用Page實例提供的setData把數據傳遞給渲染層,從而達到更新界面的目的。由於小程序的渲染層和邏輯層分別在兩個線程中運行,所以setData傳遞數據實際是一個異步的過程,所以setData的第二個參數是一個callback回調,在這次setData對界面渲染完畢后觸發。
setData其一般調用格式是 setData(data, callback),其中data是由多個key: value構成的Object對象。
// page.js
Page({
onLoad: function(){
this.setData({
text: 'change data'
}, function(){
// 在這次setData對界面渲染完畢后觸發
})
}
})
注意事項:
-
直接修改 Page實例的this.data 而不調用 this.setData 是無法改變頁面的狀態的,還會造成數據不一致。
-
由於setData是需要兩個線程的一些通信消耗,為了提高性能,每次設置的數據不應超過1024kB。
-
不要把data中的任意一項的value設為undefined,否則可能會有引起一些不可預料的bug。
頁面的用戶行為:
onPullDownRefresh 下拉刷新
監聽用戶下拉刷新事件,需要在app.json的window選項中或頁面配置page.json中設置enablePullDownRefresh為true。當處理完數據刷新后,wx.stopPullDownRefresh可以停止當前頁面的下拉刷新。
onReachBottom 上拉觸底
監聽用戶上拉觸底事件。可以在app.json的window選項中或頁面配置page.json中設置觸發距離onReachBottomDistance。在觸發距離內滑動期間,本事件只會被觸發一次。
onPageScroll 頁面滾動
監聽用戶滑動頁面事件,參數為 Object,包含 scrollTop 字段,表示頁面在垂直方向已滾動的距離(單位px)。
onShareAppMessage 用戶轉發
只有定義了此事件處理函數,右上角菜單才會顯示"轉發"按鈕,在用戶點擊轉發按鈕的時候會調用,此事件需要return一個Object,包含title和path兩個字段,用於自定義轉發內容,如代碼清單3-13所示。
// page.js
Page({
onShareAppMessage: function () {
return {
title: '自定義轉發標題',
// 自定義點擊鏈接需要跳轉的頁面,默認當前頁面
path: '/page/user?id=123'
}
}
})
10. 事件
事件定義
在小程序中綁定事件可以以bind開頭然后跟上事件的類型,如bindtap綁定一個點擊事件,對應的值是一個字符串,需要在page構造器中定義同名函數,每次觸發事件之后就會執行對應函數的內容。
<view bindtap="handleTap">點擊事件</view>
<view bind:tap="handleTap">另一種寫法</view>
// pages/my/index.js
Page({
handleTap(){
console.log("執行了點擊事件");
}
})
常見的事件類型
-
touchstart 手指觸摸動作開始
-
touchmove 手指觸摸后移動
-
touchcancel 手指觸摸動作被打斷,如來電提醒,彈窗
-
touchend 手指觸摸動作結束
-
tap 手指觸摸后馬上離開
-
longpress 手指觸摸后,超過350ms再離開,如果指定了事件回調函數並觸發了這個事件,tap事件將不被觸發
-
longtap 手指觸摸后,超過350ms再離開(推薦使用longpress事件代替)
-
transitionend 會在 WXSS transition 或 wx.createAnimation 動畫結束后觸發
-
animationstart 會在一個 WXSS animation 動畫開始時觸發
-
animationiteration 會在一個 WXSS animation 一次迭代結束時觸發
-
animationend 會在一個 WXSS animation 動畫完成時觸發
事件傳參
在小程序中進行事件傳參不能像傳統的Web項目中一樣,在括號里寫參數。在小程序中需要在標簽上通過data-方式定義事件所需的參數。
<!-- data-參數名='參數值' -->
<view bindtap="handleTap" data-msg="我是事件的參數">點擊事件</view>
每個事件回調觸發時,都會收到一個事件對象,通過這個對象可以獲取路由傳遞的參數。
handleTap(e){
console.log("執行了點擊事件");
// 通過currentTarget中的dataset屬性可以獲取時間參數
console.log(e.currentTarget.dataset.msg);
}
關於這個事件對象其他屬性
-
type 事件類型
-
timeStamp 頁面打開到觸發事件所經過的毫秒數
-
target 觸發事件的組件的一些屬性值集合
-
currentTarget 當前組件的一些屬性值集合
-
detail 額外的信息
-
touches 觸摸事件,當前停留在屏幕中的觸摸點信息的數組
-
changedTouches 觸摸事件,當前變化的觸摸點信息的數組
這里需要注意的是target和currentTarget的區別,currentTarget為當前事件所綁定的組件,而target則是觸發該事件的源頭組件。
阻止事件冒泡
在小程序中除了通過bind之外,還可以通過catch進行事件綁定,通過catch綁定的事件不會觸發事件冒泡。
事件捕獲
事件的觸發分為兩個階段,首先是捕獲階段,其次是冒泡階段。默認情況下事件都是在冒泡階段觸發。如果希望事件可以在捕獲階段觸發,可以通過capture-bind進行事件綁定。