文章目錄
原文地址:微信小程序組件化開發框架WePY官方文檔
一、wepy中的文件
wepy的文件主要以.wpy
存在,其分類有三種:
1.1 入口文件
即app.wpy
,其繼承自wepy.app
類,包含一個config
屬性和其它全局屬性、方法、事件。其中config
屬性對應原生的app.json
文件,build編譯時會根據config
屬性自動生成app.json
文件。
一個app.wpy
文件的典型結構如下:
<script>
import wepy from 'wepy';
export default class extends wepy.app {
// 對應 app.json 文件
config = {
"pages":[
"pages/index/index"
],
"window":{
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
}
};
//自定義數據
customData = {};
//自定義方法
customFunction () { }
//生命週期回調
onLaunch () {}
//生命週期回調
onShow () {}
//全局數據
globalData = {}
}
</script>
<style lang="less">
/** less **/
</style>
1.2 頁面文件
即page.wpy
,其繼承自wepy.page
類,該類的主要屬性介紹如下(在Page
頁面實例中,可以通過this.$parent
來訪問App實例):
屬性 | 說明 |
---|---|
config | 頁面配置對象,對應於原生的page.json 文件,類似於app.wpy 中的config |
components | 頁面組件列表對象,聲明頁面所引入的組件列表 |
data | 頁面渲染數據對象,存放可用於頁面模板綁定的渲染數據 |
methods | wxml 事件處理函數對象,存放響應wxml 中所捕獲到的事件的函數,如bindtap 、bindchange |
events | WePY組件事件處理函數對象,存放響應組件之間通過$broadcast 、$emit 、$invoke 所傳遞的事件的函數 |
其它 | 小程序頁面生命週期函數,如onLoad 、onReady 等,以及其它自定義的方法與屬性 |
一個page.wpy
文件的典型結構如下:
<script>
import wepy from 'wepy';
import Counter from '../components/counter';
export default class Page extends wepy.page {
components = {counter1: Counter}; // 聲明頁面中所引用的組件,或聲明組件中所引用的子組件
customData = {} // 自定義數據
customFunction () {} //自定義方法
onLoad () {} // 在Page和Component共用的生命週期函數
onShow () {} // 只在Page中存在的頁面生命週期函數
config = {}; // 只在Page實例中存在的配置數據,對應於原生的page.json文件
data = {}; // 頁面所需數據均需在這裏聲明,可用於模板數據綁定
mixins = []; // 聲明頁面所引用的Mixin實例
computed = {}; // 聲明計算屬性
watch = {}; // 聲明數據watcher
methods = {}; // 聲明頁面wxml中標籤的事件處理函數。注意,此處只用於聲明頁面wxml中標籤的bind、catch事件,自定義方法需以自定義方法的方式聲明
events = {}; // 聲明組件之間的事件處理函數
}
</script>
<template lang="wxml">
<view>
</view>
<counter1></counter1>
</template>
<style lang="less">
/** less **/
</style>
注意:WePY中的組件都是靜態組件,是以組件ID作爲唯一標識的,每一個ID都對應一個組件實例,當頁面引入兩個相同ID的組件時,這兩個組件共用同一個實例與數據,當其中一個組件數據變化時,另外一個也會一起變化。
如果需要避免這個問題,則需要分配多個組件ID和實例(推薦)。
1.3 組件文件
組件文件中所聲明的組件實例繼承自wepy.component
類,除了不需要config
配置以及頁面特有的一些生命週期函數之外,其屬性與頁面屬性大致相同(實際上wepy.Page
類繼承自wepy.Component
,所以page
能用的接口component
都能用,除了生命週期方法)。
一個組件文件的典型結構如下:
<template lang="wxml">
<view> </view>
</template>
<script>
import wepy from 'wepy';
export default class Com extends wepy.component {
components = {};
data = {};
methods = {};
events = {};
// Other properties
}
</script>
<style lang="less">
/** less **/
</style>
二、組件渲染與通信
2.1 WEPY組件的循環渲染
wepy的template
部分的語法是符合wxml
標準的(一般情況下),可以通過wxml
的循環渲染去渲染小程序原生組件。但是如果要循環渲染wepy的自定義組件,則需要特殊處理:
當需要循環渲染WePY組件時(類似於通過
wx:for
循環渲染原生的wxml標籤),必須使用WePY定義的輔助標籤<repeat>
示例如下:
/**
project
└── src
├── components
| └── child.wpy
├── pages
| ├── index.wpy index 頁面配置、結構、樣式、邏輯
| └── log.wpy log 頁面配置、結構、樣式、邏輯
└──app.wpy 小程序配置項(全局樣式配置、聲明鉤子等)
**/
// index.wpy
<template>
<!-- 注意,使用for屬性,而不是使用wx:for屬性 -->
<repeat for="{{list}}" key="index" index="index" item="item">
<!-- 插入<script>腳本部分所聲明的child組件,同時傳入item -->
<child :item="item"></child>
</repeat>
</template>
<script>
import wepy from 'wepy';
// 引入child組件文件
import Child from '../components/child';
export default class Index extends wepy.component {
components = {
// 聲明頁面中要使用到的Child組件的ID爲child
child: Child
}
data = {
list: [{id: 1, title: 'title1'}, {id: 2, title: 'title2'}]
}
}
</script>
2.2 computed和watcher
wepy框架也可以用類似vue
的computed
和watcher
屬性。具體內容可以在vue或者wepy的文檔中查看,這裏僅給出簡要介紹:
computed
計算屬性。computed
計算屬性,是一個有返回值的函數,可直接被當作綁定數據來使用。因此類似於data
屬性,代碼中可通過this.計算屬性名
來引用,模板中也可通過{{ 計算屬性名 }}
來綁定數據。
需要注意的是,只要是組件中有任何數據發生了改變,那麼所有計算屬性就都會被重新計算。watcher
監聽器。通過監聽器watcher
能夠監聽到任何屬性的更新。監聽器在watch
對象中聲明,類型爲函數,函數名與需要被監聽的data
對象中的屬性同名,每當被監聽的屬性改變一次,監聽器函數就會被自動調用執行一次。
監聽器適用於當屬性改變時需要進行某些額外處理的情形。
2.3 父子組件屬性通信
wepy中的父子組件之間,可以使用props
進行傳值,傳值有兩種方式:靜態傳值、動態傳值。靜態傳值指父組件向子組件傳遞常量數據,且只能傳遞String字符串類型。
靜態傳值代碼示例如下:
<child title="mytitle"></child>
// child.wpy
props = {
title: String
};
onLoad () {
console.log(this.title); // mytitle
}
上面的代碼中,在父組件template
模板部分的組件標籤中,使用子組件props
對象中所聲明的屬性名作爲其屬性名來接收父組件傳遞的值。
動態傳值是指父組件向子組件傳遞動態數據內容,父子組件數據完全獨立互不干擾。但可以通過使用.sync
修飾符來達到父組件數據綁定至子組件的效果,也可以通過設置子組件props
的twoWay: true
來達到子組件數據綁定至父組件的效果。那如果既使用.sync
修飾符,同時子組件props
中添加的twoWay: true
時,就可以實現數據的雙向綁定了。
注意:下文示例中的
twoWay
爲true
時,表示子組件向父組件單向動態傳值,而twoWay
爲false
(默認值,可不寫)時,則表示子組件不向父組件傳值。這是與Vue
不一致的地方,而這裏之所以仍然使用twoWay
,只是爲了儘可能保持與Vue
在標識符命名上的一致性。
在父組件template
模板部分所插入的子組件標籤中,使用:prop
屬性(等價於Vue
中的v-bind:prop
屬性)來進行動態傳值。
動態傳值代碼示例如下:
// parent.wpy
<child :title="parentTitle" :syncTitle.sync="parentTitle" :twoWayTitle="parentTitle"></child>
data = {
parentTitle: 'p-title'
};
// child.wpy
props = {
// 靜態傳值
title: String,
// 父向子單向動態傳值
syncTitle: {
type: String,
default: 'null'
},
twoWayTitle: {
type: String,
default: 'nothing',
twoWay: true
}
};
onLoad () {
console.log(this.title); // p-title
console.log(this.syncTitle); // p-title
console.log(this.twoWayTitle); // p-title
this.title = 'c-title';
console.log(this.$parent.parentTitle); // p-title.
this.twoWayTitle = 'two-way-title';
this.$apply();
console.log(this.$parent.parentTitle); // two-way-title. --- twoWay爲true時,子組件props中的屬性值改變時,會同時改變父組件對應的值
this.$parent.parentTitle = 'p-title-changed';
this.$parent.$apply();
console.log(this.title); // 'c-title';
console.log(this.syncTitle); // 'p-title-changed' --- 有.sync修飾符的props屬性值,當在父組件中改變時,會同時改變子組件對應的值。
}
2.4 組件通信與交互
wepy.component
基類提供$broadcast
、$emit
、$invoke
三個方法用於組件之間的通信和交互。
$broadcast
用於父組件向子組件(及子組件的子組件等)廣播事件,事件廣播的順序爲廣度優先搜索順序。如,頁面Page_Index發起一個$broadcast
事件,那麼按先後順序依次接收到該事件的組件爲:ComA、ComB、ComC、ComD、ComE、ComF、ComG、ComH。如下圖:
$emit
用於子組件向父組件(及父組件的父組件等)發射事件,事件發起組件的所有祖先組件會依次接收到$emit
事件。如,組件ComE發起一個$emit
事件,那麼接收到事件的先後順序爲:組件ComA、頁面Page_Index。如下圖:
$invoke
用於組件對另一個組件中的方法的直接調用,語法類似this.$invoke('ComA', 'someMethod', 'someArgs')
。
組件broadcast
、emit
的語法類似this.$emit('some-event', 1, 2, 3, 4)
,如果組件需要接收某事件,那麼需要在自己的events
屬性添加相應的事件處理函數。
import wepy from 'wepy'
export default class Com extends wepy.component {
components = {};
data = {};
methods = {};
// events對象中所聲明的函數爲用於監聽組件之間的通信與交互事件的事件處理函數
events = {
'some-event': (p1, p2, p3, $event) => {
console.log(`${this.$name} receive ${$event.name} from ${$event.source.$name}`);
}
};
// Other properties
}
2.5 組件自定義事件處理函數
wepy的組件可以設置事件處理函數,語法類似@customEvent.user="myFn"
,其中@
表示事件修飾符,customEvent
表示事件名稱,.user
表示事件後綴。
目前總共有三種事件後綴:
.default
: 綁定小程序冒泡型事件,如bindtap
,.default
後綴可省略不寫.stop
: 綁定小程序捕獲型事件,如catchtap
.user
: 綁定用戶自定義組件事件,通過$emit
觸發。注意,如果用了自定義事件,則events
中對應的監聽函數不會再執行
代碼示例:
// index.wpy
<template>
//當$emit發射childFn事件時,會傳遞給parentFn方法
<child @childFn.user="parentFn"></child>
</template>
<script>
import wepy from 'wepy'
import Child from '../components/child'
export default class Index extends wepy.page {
components = {
child: Child
}
methods = {
parentFn (num, evt) {
console.log('parent received emit event, number is: ' + num)
}
}
}
</script>
// child.wpy
<template>
//這裏綁定的是小程序的事件,點擊時會傳遞給tap方法
<view @tap="tap">Click me</view>
</template>
<script>
import wepy from 'wepy'
export default class Child extends wepy.component {
methods = {
tap () {
console.log('child is clicked')
this.$emit('childFn', 100)
}
}
}
</script>
2.6 slot 組件內容分發插槽
可以理解爲子組件的空間佔位標籤,父組件可以用其他組件替代這些佔位標籤。
//Panel組件中模板如下
<view class="panel">
<slot name="title">默認標題</slot>
<slot name="content">默認內容</slot>
</view>
//父組件使用Panel時用自定義的組件替代slot
<panel>
<view slot="title">新的標題</view>
<view slot="content">
<text>新的內容</text>
</view>
</panel>
三、interceptor 攔截器
wepy提供攔截器攔截原生API的請求,具體方法是配置API的config
、fail
、success
、complete
回調函數。參考示例:
import wepy from 'wepy';
export default class extends wepy.app {
constructor () {
// this is not allowed before super()
super();
// 攔截request請求
this.intercept('request', {
// 發出請求時的回調函數
config (p) {
// 對所有request請求中的OBJECT參數對象統一附加時間戳屬性
p.timestamp = +new Date();
console.log('config request: ', p);
// 必須返回OBJECT參數對象,否則無法發送請求到服務端
return p;
},
// 請求成功後的回調函數
success (p) {
// 可以在這裏對收到的響應數據對象進行加工處理
console.log('request success: ', p);
// 必須返回響應數據對象,否則後續無法對響應數據進行處理
return p;
},
//請求失敗後的回調函數
fail (p) {
console.log('request fail: ', p);
// 必須返回響應數據對象,否則後續無法對響應數據進行處理
return p;
},
// 請求完成時的回調函數(請求成功或失敗都會被執行)
complete (p) {
console.log('request complete: ', p);
}
});
}
}
四、數據綁定
wepy中不需要用setData
方法來綁定數據,直接爲綁定數據賦值即可。
注意,在異步函數中更新數據的時候,必須手動調用
$apply
方法。
setTimeout(() => {
this.title = 'this is title';
this.$apply();
}, 3000);