在原生小程序中,組件之間是如何通信的?

語言: CN / TW / HK
ead>

highlight: an-old-hope

2. 組件的創建和引用

2.1 創建自定義組件
  1. 創建組件

    • 在項目的根目錄中,鼠標右鍵,創建 components -> test 文件夾
    • 在新建的 components -> test 文件夾上,鼠標右鍵,點擊 新建 Component
    • 鍵入組件的名稱之後回車,會自動生成組件對應的 4 個文件,後綴名分別為 .js.json.wxml.wxss
    • 注意:為了保證目錄結構的清晰,建議把不同的組件,存放到單獨目錄中

2.2 局部引用組件
  1. 組件的引用方式

    • 組件的引用方式分為 局部引用全局引用

      • 局部引用:組件只能在當前被引用的頁面內使用
      • 全局引用:組件可以在每個小程序頁面中使用
  2. 局部引用組件

    • 頁面的 .json 配置文件中引用組件的方式,叫做 局部引用

2.3 全局引入組件

app.json 全局配置文件中引用組件的方式,叫做 全局引用

2.5 全局引用和局部引用的區別
  1. 根據組件的使用頻率範圍,來選擇合適的引用方式:

    • 如果某組件在多個頁面中經常被用到,建議進行 全局引用
    • 如果某組件只在特定的頁面中被用到,建議進行 局部引用
2.6 組件和頁面的區別
  1. 從表面來看,組件和頁面都是由 .js.json.wxml.wxss 這四個文件組成的。但是,組件和 頁面的 .js.json 文件有明顯的不同:

    • 組件的 .json 文件中需要聲明 "component": true 屬性

    • 組件的 .js 文件中調用的是 Component() 函數

      • 整個程序啟動調用的是 App()
      • 某個頁面的渲染調用的是 Page()
      • 某個組件的渲染調用的是 Component()
    • 組件的事件處理函數需要定義到 methods 節點中

    3. 組件的數據、方法和屬性

3.1 定義 data 私有數據
  1. 在小程序組件中,用於組件模板渲染的私有數據,需要定義到 data 節點中

3.2 methods 方法
  1. 在小程序組件中,事件處理函數和自定義方法需要定義到 methods 節點中

3.3 定義 properties 屬性
  1. 在小程序組件中, properties 是組件的對外屬性,用來接收外界(父組件)傳遞到組件中的數據

3.4 data 和 properties 的區別
  1. 在小程序的組件中, properties 屬性和 data 數據的用法相同,它們都是可讀可寫的,只不過:

    • data 更傾向於存儲組件的私有數據
    • properties 更傾向於存儲外界傳遞到組件中的數據
    • 所以, 也不太建議修改 properties 數據,如果要修改 properties 的數據, 最好通過子組件通信給父組件的方式實現

3.5 使用 setData 修改 properties 的值
  1. 由於 data 數據和 properties 屬性在本質上沒有任何區別,因此 properties 屬性的值也可以用於頁面渲染,或使用 setDataproperties 中的屬性重新賦值

4. 數據監聽器

4.1 什麼是數據監聽器以及其基礎語法
  1. 什麼是數據監聽器

    數據監聽器用於監聽和響應任何屬性和數據字段的變化,從而執行特定的操作。它的作用類似於 vue 中的 watch 偵聽器。在小程序組件中,數據監聽器的基本語法格式如下

4.2 數據監聽器的基本用法
  1. 組件結構

  2. 組件的 js 代碼

  1. 完整代碼

    html <view> {{ num1 }} + {{ num2 }} = {{ sum }} </view> ​ <button type="warn" bindtap="addNum1">Num + 1</button> <button type="warn" bindtap="addNum2">Num + 2</button> ​

    js // components/test/test.js Component({ ​ /** * 組件的初始數據 */ data: { num1: 0, num2: 0, sum: 0 }, ​ // 數據偵聽器 observers: { 'num1, num2': function (newNum1, newNum2) { this.setData({ sum: newNum1 + newNum2 }) } }, ​ /** * 組件的方法列表 */ methods: { addNum1 () { this.setData({ num1: this.data.num1 + 1 }) }, ​ addNum2 () { this.setData({ num2: this.data.num2 + 1 }) } } }) ​

4.3 監聽對象屬性的變化
  1. 數據監聽器支持監聽對象中單個或多個屬性的變化

html <button type="warn" bindtap="changeObj">監聽對象的屬性</button> ​ <view>{{ obj.name }}</view>

js // components/test/test.js Component({ // 組件的初始數據 data: { obj: { name: 'tom' } }, ​ // 數據偵聽器 observers: { 'obj.name': function (newName) { console.log(newName) } }, ​ // 方法列表 methods: { changeObj() { this.setData({ 'obj.name': 'jerry' }) } } }) ​

5. 組件的生命週期

5.1 組件全部的生命週期函數

| 生命週期 | 參數 | 描述 | | ---------- | -------------- | -------------------- | | created | | 在組件實例剛剛被創建時執行 | | attached | | 在組件實例進入頁面節點樹時執行 | | ready | 無 | 在組件在視圖層佈局完成後執行 | | moved | 無 | 在組件實例被移動到節點樹另一個位置時執行 | | detached | 無 | 在組件實例被從頁面節點樹移除時執行 | | error | Object Error | 每當組件方法拋出錯誤時執行 |

5.2 組件主要的生命週期函數

組件的生命週期,指的是組件自身的一些函數,這些函數在特殊的時間點或遇到一些特殊的框架事件時被自動觸發。

  1. 最重要的生命週期是 created, attached, detached ,包含一個組件實例生命流程的最主要時間點。

    • 組件實例剛剛被創建好時, created 生命週期被觸發

      • 此時還不能調用 setData
      • 通常情況下,這個生命週期只應該用於給組件 this 添加一些自定義屬性字段
    • 在組件完全初始化完畢、進入頁面節點樹後, attached 生命週期被觸發

      • this.data 已被初始化完畢
      • 這個生命週期很有用,絕大多數初始化工作可以在這個時機進行
    • 在組件離開頁面節點樹後, detached 生命週期被觸發

      • 退出一個頁面時,會觸發頁面內每個自定義組件的detached 生命週期被觸發
      • 如果組件還在頁面節點樹中,則 detached 會被觸發。
      • 此時適合做一些清理性質的工作
5.3 lifetime 節點
  1. 生命週期方法可以直接定義在 Component 構造器的第一級參數中,組件的的生命週期也可以在 lifetimes 字段內進行聲明(這是推薦的方式,其優先級最高)

js //推薦方式 lifetimes: { attached () { console.log('在組件實例進入頁面節點樹') }, detached () { console.log('在組件實例被從頁面節點樹移除') } }, ​ //以下是舊方式 attached () { console.log('~~~~~在組件實例進入頁面節點樹') }, detached () { console.log('~~~~~在組件實例被從頁面節點樹移除') }, ​ /** * 組件的初始數據 */ data: { // rgb 的顏色值對象 _rgb: { r: 0, g: 0, b: 0 }, // 根據 rgb 對象的三個屬性,動態計算 fullColor 的值 fullColor: '0, 0, 0' }

6. 組件所在頁面的生命週期

6.1 什麼是組件所在頁面的生命週期
  1. 有時,自定義組件的行為依賴於頁面狀態的變化,此時就需要用到組件所在頁面的生命週期

    • 例如:每當觸發頁面的 show 生命週期函數的時候,我們希望能夠重新生成一個隨機的 RGB 顏色值。在自定義組件中,組件所在頁面的生命週期函數有如下 3 個,分別是

| 生命週期 | 參數 | 描述 | | ------ | ------------- | -------------- | | show | 無 | 組件所在的頁面被展示時執行 | | hide | 無 | 組件所在的頁面被隱藏時執行 | | resize | Object Size | 組件所在的頁面尺寸變化時執行 |

6.2 pageLifetimes 節點
  1. 組件所在頁面的生命週期函數,需要定義在 pageLifetimes 節點中

6.3 生成隨機的 RGB 顏色值

js Component({ // 在組件的methods節點中,定義一個隨機顏色的方法 methods: { _randomColor() { // 設置data中的數據 this.setData({ _rgb: { r: Math.floor(Math.random() * 256), g: Math.floor(Math.random() * 256), b: Math.floor(Math.random() * 256) } }) } }, // 在組件內部的pageLifetimes節點中,監聽組件在頁面的生命週期函數 pageLifetimes: { // 在頁面被展示的時候,調用該方法 show() { this._randomColor() }, hide() { }, // 頁面被隱藏 resize() { } // 頁面尺寸變化 } })


7. 插槽

7.1 什麼是插槽
  1. 在自定義組件的 wxml 結構中,可以提供一個 節點(插槽),用於承載組件使用者提供的 wxml 結構

  1. 其實插槽, 説的通俗一些, 就是 子組件挖坑,父組件填坑 的過程。由父組件在使用子組件的時候, 決定子組件內部的某一些佈局展示

    • 子組件通過挖坑
    • 父組件通過組件標籤中間的內容來填坑
7.2 單個插槽
  1. 在小程序中,默認每個自定義組件中只允許使用一個 slot 佔位,這種個數上的限制叫做單個插槽

    • 默認情況下,一個組件的 wxml 中只能有一個 slot
    • 需要使用多 slot 時,可以在組件 js 中聲明啟用
    • 注意:小程序中目前只有默認插槽和多個插槽,暫不支持作用域插槽

7.3 啟動多個插槽

在小程序的自定義組件中,需要使用多 插槽時,可以在組件的 .js 文件中

7.4 定義多個插槽

可以在組件的 .wxml 中使用多個 標籤,以不同的 name 來區分不同的插槽

8. 父子組件通訊

8.1 瞭解父子組件之間通信的 3 個方式
  1. 屬性綁定

    • 用於父組件向子組件的指定屬性設置數據,僅能設置 JSON 兼容的數據(只能傳遞數據,不能傳遞方法)
  2. 事件綁定

    • 用於子組件向父組件傳遞數據,可以傳遞任意數據(包括數組和方法)
  3. 獲取組件實例

    • 父組件還可以通過 this.selectComponent() 獲取子組件實例對象這樣就可以直接訪問子組件的任意數據和方法
8.2 屬性綁定
  1. 傳遞數據

    • 屬性綁定用於實現父向子傳值,而且只能傳遞普通類型的數據,無法將方法傳遞給子組件

  1. 接收數據

    • 子組件在 properties 節點中聲明對應的屬性並使用

8.3 實現子組件的 count 屬性自增 + 1

js data: { count: 0 }

js addCount() { this.setData({ count: this.data.count + 1 }) }

8.4 瞭解事件綁定的 4 個核心實現步驟
  1. 事件綁定用於實現子向父傳值,可以傳遞任何類型的數據。使用步驟如下:

    • 父組件js 中,定義一個函數,這個函數即將通過自定義事件的形式,傳遞給子組件
    • 父組件wxml 中,通過自定義事件的形式,將步驟 1 中定義的函數引用,傳遞給子組件
    • 子組件js 中,通過調用 this.triggerEvent('自定義事件名稱', {/* 參數對象 */}),將數據發送到父組件
    • 在父組件的 js 中,通過 e.detail 獲取到子組件傳遞過來的數據
8.5 瞭解事件綁定的核心實現代碼
  1. 步驟 1: 在父組件的 js 中,定義一個函數,這個函數即將通過自定義事件的形式,傳遞給子組件

  2. 步驟 2:在父組件的 wxml 中,通過自定義事件的形式,將 步驟 1 中定義的函數引用,傳遞給子組件

  3. 步驟 3:在 子組件js 中,通過調用 this.triggerEvent('自定義事件名稱', {/* 參數對象 */}),將數據發送到父組件

  4. 步驟 4:在父組件的 js 中,通過 e.detail 獲取到子組件傳遞過來的數據

8.6 使用 selectComponent 獲取組件實例

可在父組件裏調用 this.selectComponent("id或class選擇器"),獲取子組件的實例對象,從而直接訪問子組件的任意數據和方法。調用時需要傳入一個選擇器,例如 this.selectComponent(".my-component")

9. 使用 npm

9.1 小程序對 npm 的支持和限制

目前,小程序中已經支持使用 npm 安裝第三方包,從而來提高小程序的開發效率。但是,在小程序中使用 npm 包有如下 3 個限制:

  1. 不支持依賴於 Node.js 內置庫的包

    • 不支持依賴於 Node 核心模塊的包
    • fs Node 文件系統模塊 -- 不支持
    • path Node 路徑模塊 -- 不支持
  2. 不支持依賴於瀏覽器內置對象的包

    • 小程序的宿主環境是微信,不存在 DOM 和 DOM
    • 所以依賴於內置對象的包,也不能夠使用
  3. 不支持依賴於 C++ 插件的包

總結:雖然 npm 上的包有千千萬,但是能供小程序使用的包卻“為數不多”

9.2 瞭解什麼是 vant Weapp
  1. Vant有贊前端團隊開源的移動端組件庫,於 2016 年開源,已持續維護 4 年時間。Vant 對內承載了有贊所有核心業務,對外服務十多萬開發者,是業界主流的移動端組件庫之一
  2. 採用 MIT 開源許可協議,對商業使用比較友好
  3. 掃描下方小程序二維碼,體驗組件庫示例

9.3 安裝 Vant 組件庫

在小程序項目中,安裝 Vant 組件庫主要分為如下幾步

  1. 通過 npm 安裝

    • 注意:項目目錄不能存在中文!不能存在中文!不能存在中文!否則會報錯 !

shell npm init -y

shell npm i @vant/[email protected] -S --production

  1. 構建 npm 包功能

    如果提示構建失敗,可以將微信開發者工具重啟,重啟以後,運行小程序項目,查看是否還報錯

    • 建議先點擊 微信開發者工具 --> 詳情 --> 本地設置 --> 使用 npm 模塊

    • 然後點擊 微信開發者工具 --> 菜單欄 --> 工具 --> 構建npm

    • 提示 構建成功,耗時 xxx 秒 且控制枱沒有任何的錯誤,説明包構建成功,可以進行使用

      • 否則就需要把 node_modulesminiprogram_npm 刪除
      • 刪除以後,重新安裝包,並點擊 工具 --> 構建npm,進行重新構建
  2. 修改 app.json

    • styles: v2 進行移除,防止 小程序本身的 UI 樣式和 Vant 的組件樣式庫 衝突

詳細的操作地址:安裝 vant Weapp

9.4 使用 Vant 組件

安裝完 Vant 組件庫之後,可以在 app.jsonusingComponents 節點中引入需要的組件,即可在 wxml 中直接使用組件

josn "usingComponents": { "van-button": "@vant/weapp/button/index" }

html <van-button type="default">默認按鈕</van-button> <van-button type="primary">主要按鈕</van-button> <van-button type="info">信息按鈕</van-button> <van-button type="warning">警告按鈕</van-button> <van-button type="danger">危險按鈕</van-button>

9.5 定義和使用 CSS 變量 (瞭解)

Vant Weapp 使用 CSS 變量來實現定製主題。 關於 CSS 變量的基本用法,請參考 MDN 文檔

http://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties

  1. 自定義屬性(有時候也被稱作 CSS變量 或者 級聯變量)是由 CSS 作者定義的,它包含的值可以在整個文檔中重複使用

  2. 由自定義屬性標記設定值

    • 聲明一個自定義屬性,屬性名需要以兩個減號(--)開始,屬性值則可以是任何有效的 CSS
    • 比如: --main-color: black;
  3. var() 函數來獲取值

    • color: var(--main-color);

```html

CSS 變量

​ ```

9.6 使用 CSS 變量定製 Vant 的主題樣式 (瞭解)

app.wxss 中,寫入 CSS 變量,即可對全局生效

所有可用的顏色變量,請參考 Vant 官方提供的配置文件

http://github.com/youzan/vant-weapp/blob/dev/packages/common/style/var.less

9.7 什麼是小程序 API 的 Promise 化
  1. 基於回調函數的異步 API 的缺點

    • 默認情況下,小程序官方提供的異步 API 都是基於回調函數實現的,例如,網絡請求的 API 需要按照如下的方式調用

    • 這種代碼的缺點是顯而易見的, 容易造成回調地獄的問題,代碼的可讀性、維護性差!而我們就想將這種類型的代碼使用 API Promise 化進行改造

  2. 什麼是 API Promise

    • API Promise 化,指的是通過額外的配置,將官方提供的、基於回調函數的異步 API ,升級改造為基於 Promise 的異步 API ,從而提高代碼的可讀性、維護性,避免回調地獄的問題
9.8 安裝並構建 miniprogram-api-promise
  1. 在小程序中,實現 API Promise 化主要依賴於 miniprogram-api-promise 這個第三方的 npm

    js npm i --save [email protected]

  2. 下載完成,我們不能直接使用這個包,而是需要再次重新構建npm包

    • 建議在構建前先刪除原有的 miniprogram_npm
    • 然後再點擊工具,構建 npm
  1. 如果刪除了 miniprogram_npm 目錄,構建還是失敗

    • 需要把 node_modulesminiprogram_npm 刪除
    • 刪除以後,重新安裝包,重新安裝以後,再次進行構建
9.9 三個步驟實現 API 的 Promise 化

  1. 在小程序入口文件中調用一次 promisifyAll()方法

    js import { promisifyAll } from 'miniprogram-api-promise'

  2. 聲明一個常量,為一個空對象

    js const wxp = wx.p = {}

  3. 調用 promisifyAll()方法

    js promisifyAll(wx, wxp)

  4. 上述代碼是什麼含義呢 ?

    • promisifyAll : 做的事就是將 wx 擁有的屬性方法都 copy 並改造了一份給了 wxp 這個對象
    • 然而, wxp 只是當前 js 文件的一個常量,只能在當前文件使用
    • 因此:我們在 wx 上掛載一個屬性 p 讓他和 wxp 指向同一個空對象
    • 在其他頁面或者組件就可以通過全局對象 wx 點出 p 來訪問到 wxp
    • 此時 wx.p 發起異步的請求時,得到的是一個 promise 對象
    • 那麼我們就可以使用 async/await 簡化 Promise 語法

js // 1、導入 promisifyAll 這個方法 import { promisifyAll } from 'miniprogram-api-promise' ​ // 2、聲明一個空對象 wxp // wxp 只是一個單純的變量,只能夠在當前 js 中使用,外部不能使用 // wx 是微信小程序最頂級的對象 // 只需要往 wx 上去掛載一個對象即可 // 掛載好,以後 wxp 和 wx.p 指定的是同一個內存空間 // 也就是説 wxp 拷貝得到的 wx 的屬性和方法 // wx.p 同樣也是擁有的 // 調用方式: wx.p.xxxxx 即 wx.p.request({}) const wxp = wx.p = {} ​ // 3、promisifyAll 作用就是將 wx 內部的方法和屬性深拷貝一份給 wxp 這個對象 promisifyAll(wx, wxp) ​

9.10 調用 Promise 化之後的異步 API

html <van-button type="warning" bindtap="getInfo">警告按鈕</van-button>

js async getInfo () { const { data: res } = await wx.p.request({ url: 'http://www.escook.cn/api/get', method: 'GET', data: { name: 'zs', age: 19 } }) ​ console.log(res) }