小程式的核心語法
小程式的核心語法
開篇
小程式核心語法:
- 資料驅動原則
- 實現兩個案例,串聯核心知識點
- 商品案例
- 列表案例
小程式的資料驅動原則
問題:
- 什麼是資料驅動?
- 在小程式中如何完成資料繫結?
資料驅動:
js
// 商品
let product = {
price: 10,
num: 5
}
// 總價格
let total = 0;
// 計算總價格的方法
function getTotal(product) {
return product.price * product.num
}
// 計算商品的總價格
total = getTotal(product)
// 進行列印
console.log('總價格:' + total);
// 50 太貴了,所以我們少購買了兩個商品,也就是讓 num = 3
product.num = 3;
// 問:總價格是多少?
console.log('總價格:' + total); // 此時,打印發現總價格還是 50 元,如果要說原因的話,那麼應該很簡單,【因為我們沒有重新進行價格的計算嘛】
// 但是,此時大家有沒有想過一點?我們為什麼要進行價格的計算呢?
// ----------------------------------------------------
// 當商品的數量發生變化時,商品的總價格【理應發生變化】,不是嗎?
上面的例子,就是我想要跟大家說的:【當數量發生變化時,商品的總價格理應發生改變】。
那麼同樣的道理,在我們的頁面中,假如:
某一個 DOM 依賴於某個資料進行展示,那麼【當資料發生變化時,檢視也理應發生變化】。
而這個就是【響應式資料驅動】。
小程式中完成響應式:
- 在
data
中定義資料
```js // index.js // 獲取應用例項 const app = getApp()
Page({ data: { product: { price: 10, num: 5 } } }) ```
- 在
wxml
中使用資料
html
<view>
<view>
<!-- wxml 中訪問資料,必須使用 {{}} 語法,{{}} 語法中可以放置【任意的、單一的 JavaScript 表示式】 -->
商品的單價:{{product.price}}
</view>
<view>
商品的數量:{{product.num}}
</view>
<view>
商品的總價格:{{product.price * product.num}}
</view>
</view>
現在我們已經可以在 js 的 data
中定義資料,並且在 wxml 中通過 {{}}
語法使用資料。
那麼我們回過頭來看我們的問題:
答案:
- 什麼是資料驅動?
- 當資料發生變化時,檢視理應發生變化
- 在小程式中如何完成資料繫結?
- 在 data 中定義資料
- 在 wxml 中通過 {{}} 使用資料
但是在此時,大家心裡應該還有一個疑惑,那就是:【現在資料還沒有發生變化呀?我也沒有看到檢視的變化呀?】。
如果你心中確實有這麼一個困惑的話,那麼就繼續往下看!
小程式中的常用事件與屬性列表
問題:
- 如何為按鈕新增點選事件?
- 如何修改 data 中資料的值?
處理點選事件
接下來我們希望做一件事情:
建立一個按鈕
當用戶點選按鈕時
讓 product 的 num + 1
建立按鈕的方式非常簡單:
html
<button type="primary">num + 1</button>
問題在於:我們如何給這個按鈕新增點選事件呢?
有過開發經驗的同學,可能會猜到:我們可以給 button
一個 click
事件來監聽按鈕的點選。
可是大家需要知道,現在我們是在【小程式】中,那麼如果想要給 button
新增點選事件則不可以使用 click
而是 bind:tap / bindtap
。
其中 bind: / bind
表示【繫結事件】,tap
為繫結的具體事件。小程式具體事件列表,可以點選 這裡 檢視。
html
<button type="primary" bind:tap="onAddNum">num + 1</button>
接下來需要在 js
中定義對應的 事件
js
/**
* 定義事件處理的方法
*/
onAddNum () {
console.log('onAddNum')
}
到目前:我們已經 監聽了按鈕的點選事件,並且寫入了對應的處理函式 ,接下來就需要 修改 num 的值
修改 data 的資料
想要修改 data
中的資料,那麼我們需要藉助一個函式 setData
。
setData
接收一個 物件作為引數,這個物件就是最新的 data
資料。
其中 key
為要修改的資料, value
為最新的值
訪問 data 的資料
因為我們想要讓 num + 1
,所以我們還需要拿到 num
的當前值,想要訪問 num
的值,可以通過 this.data.product.num
的形式訪問
所以最終的修改 num
的程式碼為:
js
/**
* 定義事件處理的方法
*/
onAddNum () {
this.setData({
'product.num': this.data.product.num + 1
})
此時,當我們點選 button
,可以發現:【當 num 發生改變時,總價格也發生了對應的變化】
答案:
- 如何為按鈕新增點選事件?
bindtap
||bind:tap
- 如何修改 data 中資料的值?
- 通過
this.setData({})
定義新的值- 通過
this.data
訪問具體的值
事件傳參
問題:
- 如果想要在【點選事件中】傳遞引數,那麼需要怎麼做?
新的需求
現在讓我們把需求變得更加複雜一些。
我們希望
onAddNum
方法可以接收一個引數,每次點選num
增加的數量為傳入的引數
那麼如果想要實現這個需求的話,那麼就需要涉及到一個知識點:【事件傳參】。
如果大家有過開發經驗的話,那麼可能會認為這是一個非常簡單的需求,順便可以寫下如下程式碼:
```js // html
// js onAddNum (step) { this.setData({ 'product.num': this.data.product.num + step }) } ```
可是,假如我們真按照以上程式碼進行實現的話,那麼 你應該會收到以下如下的警告:
這個警告的意思是:沒有一個叫做 onAddNum(5)
的方法用來處理當前的這個 tap
事件。
也即是說:onAddNum(5)
會被當做一個 完整的方法名字,而不是 方法名為:onAddNum
,傳入了引數為 5
!
那麼如果我們想要傳遞引數應該怎麼做呢?
在小程式中,如果想要給 點選事件傳遞引數的話,那麼需要藉助 event 物件 和 data- 屬性 !
引數的傳遞包含兩個部分:
- 形參
- 實參
形參:
首先先來看 形參,對於 點選事件的回撥方法 而言,預設會接收一個引數 event (事件物件)。這個 event
物件為:回撥方法的唯一引數
實參:
對於 小程式 中,我們不能直接為 回撥方法傳遞實參。
而是需要通過:屬性繫結的形式,把需要傳遞的引數繫結到 當前 DOM
元素中,繫結資料的屬性需要以 data-
開頭。該屬性可以通過 e.target.dataset
進行訪問。
```js // html
// js onAddNum (e) { // 獲取 data-step 的值 let step = parseInt(e.target.dataset.step); this.setData({ 'product.num': this.data.product.num + step }) } ```
答案:
- 如果想要在【點選事件中】傳遞引數,那麼需要怎麼做?
- 通過屬性繫結(data-xx)的形式,把需要傳遞的引數繫結到 當前
DOM
元素中- 在對應的回撥函式中,通過
e.target.dataset
進行訪問
實現【雙向資料繫結】
問題:
- 什麼叫做雙向資料繫結?
- 小程式中如何實現雙向資料繫結?
上一章節中我們通過【事件傳參】實現了【每次點選 + 5】 的功能,但是這樣的功能未免還是有些太單調了。
所以我們接下來希望實現一個新的功能:
建立一個數字輸入框,輸入框 與【商品數量】完成 【雙向資料繫結】。
即:
- 輸入框內容發生變化時,商品數量同步跟隨變化
- 商品數量發生變化時,輸入框內容同步跟隨變化
那麼這樣的功能我們應該如何去實現呢?
如果想要實現這個功能,那麼我們需要先把這個功能進行拆解,【把一個複雜的功能拆解成多個簡單的功能】是實現一個複雜邏輯的標準方式。
那麼如何進行拆解呢? 大家可以先進行以下思考,然後再繼續向下進行學習!
以上功能拆解如下:
- 建立一個【數字輸入框】
- 設定 【商品數量】 為輸入框的初始值
- 監聽使用者的輸入行為
- 獲取使用者輸入的值
- 賦值給【商品數量】
```js
// html
// js /* * 3. 監聽 input 的輸入事件 / onInput (e) { // 4. 獲取使用者輸入的值 const val = parseInt(e.detail.value); // 5. 賦值給【商品數量】 this.setData({ 'product.num': val })
```
那麼現在功能我們已經實現了,那麼大家在回憶一下我們的問題:
答案:
- 什麼叫做雙向資料繫結?
- 當檢視發生變化時,資料跟隨發生變化。
- 當資料發生變化時,檢視跟隨發生變化.
- 小程式中如何實現雙向資料繫結?
- 通過
value
為input
檢視繫結資料- 通過監聽
bindinput
獲取檢視的變化,在回撥方法中修改資料
條件渲染
問題:
- v-if 和 hidden 的區別是什麼?
現在你已經買了很多的商品了,可是當你出去結賬的時候,售貨員小姐姐對你發出了一聲驚呼:
- 如果【總價格 <= 100 】:hello 帥哥
- 如果【總價格 > 100 && 總價格 < 1000】:哇哦 有錢人哦
- 如果【總價格 >= 1000】:土豪你好
如果想要實現這麼一個功能的話,那麼就需要使用【條件渲染】的功能了。
小程式中提供了兩個 API 都可以實現【條件渲染】的功能:
wx:if ... wx:elif ... wx:else
hidden
那麼下面我們就分別用這兩個語法來實現一下這個功能:
```html
答案:
- v-if 和 hidden 的區別是什麼?
v-if
用來控制 【元件是否會被渲染】hidden
用來控制【元件是否會被隱藏】- 一般來說,
wx:if
有更高的切換消耗而hidden
有更高的初始渲染消耗。因此,如果需要頻繁切換的情景下,用hidden
更好,如果在執行時條件不大可能改變則wx:if
較好。
列表渲染
問題:
- 使用
wx:for
時,當前項的【下標變數名】和【當前項變數名】預設分別是什麼?block
元件是否會被渲染?
新的需求:
如果我們有一組商品,並且希望把這組商品全部渲染出來得話,那麼就需要使用到【列表渲染】的功能。
小程式中為我們提供了 v-for
指令,讓我們進行【列表渲染】的實現。
同時也為我們提供了一個:包裹性質的容器 block
元件,當我們去迴圈多個元素時,可以使用 block
進行包裹,block
元件只起到包裹的其他元件的作用,本身並不會進行渲染。
```js // html
// js data: { products: [ { name: '蘋果', price: 3.2 }, { name: '麵包', price: 5.0 }, { name: '可樂', price: 2.5 } ] } ```
答案:
- 使用
wx:for
時,當前項的【下標變數名】和【當前項變數名】預設分別是什麼?- 預設陣列的當前項的下標變數名預設為 index
- 陣列當前項的變數名預設為 item
block
元件是否會被渲染?block
只是一個包裹性質的容器,不會被渲染。
配置檔案解讀
app.json
配置檔案:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.htmlpages
陣列:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#pages- 建立
list
頁面
- 建立
window
物件:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#windowtabbar
物件:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#tabBarindex
頁面list
頁面
頁面.json
配置檔案:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html
資料請求
場景
先去試想一個場景,現在你是【慕課網的前端開發工程師】,然後你開發了這樣的一個【小程式】
現在系統已經上線了。
有一天,你想要修改裡面的一塊資料,比如:把【C語言系統化精講】改成【C語言精講】,那麼你應該怎麼做?
記住,現在你的專案已經發布上線了!你想要修改線上版本的內容,那麼你怎麼做呢?難道要為了修改這個文字釋出一個新的版本嗎?如果以後再有了類似的文字修改呢?
那麼此時面對這樣的場景,我們就需要使用到【資料請求】了。
問題
- 小程式中的資料請求有什麼限制?以及如何解決這種限制
- 小程式的資料請求會存在跨域問題嗎?為什麼?
- 小程式的資料請求可以叫做
ajax
請求嗎?為什麼?
內容
wx.request 發起網路請求,請求的方式主要分為兩種:
- get 請求
- post 請求
這裡準備了兩個資料請求介面,可以用來測試 wx.request 的資料請求(詳見介面文件):
- /api/test/getList
- /api/test/postData
那麼接下來我們就根據 wx.request 來完成一個基本的介面請求
```js
// html
```
這樣的程式碼看起來沒有任何問題,但是我們卻得到了一個錯誤(可測試的 APPID:wxf01e2ce0eb588aac
):
而要解決這個問題,我們就需要明確一個問題:小程式中的資料請求有什麼限制?
- 只能請求
HTTPS
型別的介面 - 必須將介面的域名新增到信任列表中
解決方案:
- 生產環境:將想要請求的域名協議【更改為 HTTPS】並【新增到域名信任列表】
- 開發環境:通過勾選
當 get
請求完成,接下來來測試一下 post
請求:
js
// html
<button type="primary" bindtap="onPostClick">發起 post 請求</button>
// js
onPostClick () {
wx.request({
url: 'https://api.imooc-blog.lgdsunday.club/api/test/postData',
method: 'POST',
data: {
msg: '願大家心想事成,萬事如意'
},
success: (res) => {
console.log(res);
}
})
}
題外話(擴充套件內容:針對有 web 前端開發經驗的同學):
- 跨域問題: 跨域問題主要針對 瀏覽器 而言,而小程式宿主環境為【微信小程式客戶端】,所以小程式中不存在【跨域問題】
ajax
請求:ajax
依賴於XMLHttpRequest
物件,而小程式宿主環境為【微信小程式客戶端】,所以小程式中的【網路請求】不是ajax
請求
答案
- 小程式中的資料請求有什麼限制?以及如何解決這種限制
- 限制:
- 只能請求
HTTPS
型別的介面- 必須將介面的域名新增到信任列表中
- 解決方案:
- 生產環境:將想要請求的域名協議【更改為 HTTPS】並【新增到域名信任列表】
- 開發環境:通過勾選
- 小程式的資料請求會存在跨域問題嗎?為什麼?
- 不會
- 【跨域問題】只存在於基於瀏覽器的
Web
開發中- 由於小程式的宿主環境不是瀏覽器,而是微信客戶端
- 所以小程式中不存在跨域問題
- 小程式的資料請求可以叫做
ajax
請求嗎?為什麼?- 不可以
ajax
的核心是依賴於 【瀏覽器端】 的XMLHttpRequest
物件- 由於小程式的宿主環境不是瀏覽器,而是微信客戶端
- 所以小程式的資料請求不可以叫做
ajax
請求
非同步程式設計新方案 - promise
場景
首先先去假設一個場景:
目前有一個需求,需要你按照以下的邏輯去進行介面請求:
- 先去請求介面 A
- 在介面 A 獲取到資料之後,再去請求介面 B
- 在介面 B 獲取到資料之後,再去請求介面 C
- 在介面 C 獲取到資料之後,再去請求介面 D
如果按照上一小節學習到的內容,那麼我們會得到以下的程式碼(介面程式碼請見:03-小程式核心語法/02-回撥地獄.html):
js
A(function (res) {
console.log(res);
B(function (res) {
console.log(res);
C(function (res) {
console.log(res);
D(function (res) {
console.log(res);
})
})
})
})
在這個 顏值即正義 的世界裡面,我們這樣的程式碼結構應該是 沒有前途的。 因為它太醜了,並且太難以閱讀了。
假想一下,如果我們要請求 10 個介面的話,那麼程式碼會變成什麼樣子?
所以在程式設計圈裡對這樣的程式碼有一個非常學術的名字:回撥地獄 -> 回撥函式的大量巢狀導致出現 複雜且難以閱讀 的邏輯
問題
- promise 是如何解決回撥地獄的問題呢?
- Promise 的狀態分為幾種,分別是什麼?
- 如何讓 Promise 變成 已兌現(fulfilled)的狀態,如何接收已兌現(fulfilled)的結果
內容
點選 Promise 進入官方文件:
```js