- 事件綁定語法使用優化語法代替
原 bindtap="click" 替換爲 @tap="click",原catchtap="click"替換爲@tap.stop="click"。
原 capture-bind:tap="click" 替換爲 @tap.capture="click",原capture-catch:tap="click"替換爲@tap.capture.stop="click"。 - 自定義組件命名應避開微信原生組件名稱以及功能標籤<repeat>。 不可以使用input、button、view、repeat等微信小程序原生組件名稱命名自定義組件
- 開發模式對比
原生代碼:
//index.js
//獲取應用實例
var app = getApp()
//通過Page構造函數創建頁面邏輯
Page({
//可用於頁面模板綁定的數據
data: {
motto: 'Hello World',
userInfo: {}
},
//事件處理函數
bindViewTap: function() {
console.log('button clicked')
},
//頁面的生命週期函數
onLoad: function () {
console.log('onLoad')
}
})
基於WePY的代碼:
//index.wpy中的<script>部分
import wepy from 'wepy';
//通過繼承自wepy.page的類創建頁面邏輯
export default class Index extends wepy.page {
//可用於頁面模板綁定的數據
data = {
motto: 'Hello World',
userInfo: {}
};
//事件處理函數(集中保存在methods對象中)
methods = {
bindViewTap () {
console.log('button clicked');
}
};
//頁面的生命週期函數
onLoad() {
console.log('onLoad');
};
}
- 對小程序原生API進行promise處理,同時修復了一些原生API的缺陷,比如:wx.request的併發問題等。需要手動開啓promise、async/await
原生代碼:
onLoad = function () {
var self = this;
wx.login({
success: function (data) {
wx.getUserInfo({
success: function (userinfo) {
self.setData({userInfo: userinfo});
}
});
}
});
}
基於WePY的代碼:
import wepy from 'wepy';
async onLoad() {
await wepy.login();
this.userInfo = await wepy.getUserInfo();
}
- 注意,對於WePY中的methods屬性,因爲與Vue中的使用習慣不一致,非常容易造成誤解,這裏需要特別強調一下:WePY中的methods屬性只能聲明頁面wxml標籤的bind、catch事件,不能聲明自定義方法,這與Vue中的用法是不一致的。
// 錯誤示例
import wepy from 'wepy';
export default class MyComponent extends wepy.component {
methods = {
bindtap () {
let rst = this.commonFunc();
// doSomething
},
bindinput () {
let rst = this.commonFunc();
// doSomething
},
//錯誤:普通自定義方法不能放在methods對象中
customFunction () {
return 'sth.';
}
};
}
// 正確示例
import wepy from 'wepy';
export default class MyComponent extends wepy.component {
methods = {
bindtap () {
let rst = this.commonFunc();
// doSomething
},
bindinput () {
let rst = this.commonFunc();
// doSomething
},
}
//正確:普通自定義方法在methods對象外聲明,與methods平級
customFunction () {
return 'sth.';
}
}
- 需要注意的是,WePY中的組件都是靜態組件,是以組件ID作爲唯一標識的,每一個ID都對應一個組件實例,當頁面引入兩個相同ID的組件時,這兩個組件共用同一個實例與數據,當其中一個組件數據變化時,另外一個也會一起變化。
<template>
<view class="child1">
<child></child>
</view>
<view class="child2">
<anotherchild></anotherchild>
</view>
</template>
<script>
import wepy from 'wepy';
import Child from '../components/child';
export default class Index extends wepy.component {
components = {
//爲兩個相同組件的不同實例分配不同的組件ID,從而避免數據同步變化的問題
child: Child,
anotherchild: Child
};
}
</script>
如果需要避免這個問題,則需要分配多個組件ID和實例。代碼如下:
- 注意:WePY中,在父組件template模板部分插入駝峯式命名的子組件標籤時,不能將駝峯式命名轉換成短橫杆式命名(比如將childCom轉換成child-com),這與Vue中的習慣是不一致。
- 當需要循環渲染WePY組件時(類似於通過wx:for循環渲染原生的wxml標籤),須使用WePY定義的輔助標籤<repeat>,代碼如下:
<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>
當然微信小程序原生的 wx:for
也是支持的
<block wx:for-items="{{list}}" wx:for-index="index" wx:for-item="item" wx:key="id">
<view @tap="tap" class="mylist">
<text>{{item.id}}</text>: {{item.title}}
</view>
</block>
-props組件傳值
靜態傳值-也就是父子傳值,跟vue類似,用props={}
接收
<child title="mytitle"></child>
// child.wpy
props = {
title: String
};
onLoad () {
console.log(this.title); // mytitle
}
動態傳值
動態傳值是指父組件向子組件傳遞動態數據內容,父子組件數據完全獨立互不干擾。但可以通過使用.sync修飾符來達到父組件數據綁定至子組件的效果,也可以通過設置子組件props的twoWay: true來達到子組件數據綁定至父組件的效果。那如果既使用.sync修飾符,同時子組件props中添加的twoWay: true時,就可以實現數據的雙向綁定了。
// 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';//改變title的值
console.log(this.$parent.parentTitle); // p-title.父組件中title的值未被改變
this.twoWayTitle = 'two-way-title';//用twoWay爲true時候,改變子組件的props中的值,下一步驗證父組件的title是否有被改變
this.$apply();
console.log(this.$parent.parentTitle); // two-way-title. --- 驗證成功,父組件title值被改變---twoWay爲true時,子組件props中的屬性值改變時,會同時改變父組件對應的值
this.$parent.parentTitle = 'p-title-changed';//同理。改變父組件的props中的title值,打印驗證子組件加上
.sync的props的title的值是否有改變
this.$parent.$apply();
console.log(this.title); // 'c-title';
console.log(this.syncTitle); // 'p-title-changed' --- 有改變驗證成功---有.sync修飾符的props屬性值,當在父組件中改變時,會同時改變子組件對應的值。
}
這段分析有點長,簡單的說就是,子組件中改變twoWayTitle的值,在父組件中可以接收到,可以理解爲子父通信;父組件中改變props的值,在子組件中this.syncTitle中可以接收到,也就是父子通信。
- 組件之間的通信
wepy.component基類提供$broadcast、$emit、$invoke
三個方法用於組件之間的通信和交互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
}
-
$broadcast
事件是由父組件發起,所有子組件都會收到此廣播事件,除非事件被手動取消。 -
$emit
與$broadcast
正好相反,事件發起組件的所有祖先組件會依次接收到$emit
事件。 -
$invoke
是一個頁面或組件對另一個組件中的方法的直接調用,通過傳入組件路徑找到相應的組件,然後再調用其方法。
this.$invoke('ComA', 'someMethod', 'someArgs');
this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs');
- Mixin混合
默認式混合
對於組件data數據,components組件,events事件以及其它自定義方法採用默認式混合,即如果組件未聲明該數據,組件,事件,自定義方法等,那麼將混合對象中的選項將注入組件之中。對於組件已聲明的選項將不受影響。
// mixins/test.js
import wepy from 'wepy';
export default class TestMixin extends wepy.mixin {
data = {
foo: 'foo defined by page',
bar: 'bar defined by testMix'
};
methods = {
tap () {
console.log('mix tap');
}
}
}
// pages/index.wpy
import wepy from 'wepy';
import TestMixin from './mixins/test';
export default class Index extends wepy.page {
data = {
foo: 'foo defined by index'
};
mixins = [TestMixin ];
onShow() {
console.log(this.foo); // foo defined by index
console.log(this.bar); // bar defined by testMix
}
}
兼容式混合
對於組件methods
響應事件,以及小程序頁面事件將採用兼容式混合,即先響應組件本身響應事件,然後再響應混合對象中響應事件。注意,這裏事件的執行順序跟Vue中相反,Vue中是先執行mixin中的函數, 再執行組件本身的函數。
// mixins/test.js
import wepy from 'wepy';
export default class TestMixin extends wepy.mixin {
methods = {
tap () {
console.log('mixin tap');
}
};
onShow() {
console.log('mixin onshow');
}
}
// pages/index.wpy
import wepy from 'wepy';
import TestMixin from './mixins/test';
export default class Index extends wepy.page {
mixins = [TestMixin];
methods = {
tap () {
console.log('index tap');
}
};
onShow() {
console.log('index onshow');
}
}
// index onshow
// mixin onshow
// ----- when tap
// index tap
// mixin tap
其實也很好理解,混合就是把兩份代碼都打包起來,只是程序執行的時候,執行的順序,先執行哪個後執行哪個。且再仔細琢磨一下:
- 默認式混合:對於組件data數據,components組件,events事件以及其它自定義方法採用默認式混合,即如果組件未聲明該數據,組件,事件,自定義方法等,那麼將混合對象中的選項將注入組件之中。對於組件已聲明的選項將不受影響。
- 兼容式模式:對於組件
methods
響應事件,以及小程序頁面事件將採用兼容式混合,即先響應組件本身響應事件,然後再響應混合對象中響應事件(注意不是不執行混合對象中的響應事件,而是先執行組件本身,後執行混合對象中響應事件,而data,components,events是組件本身已聲明的則不受影響)。注意,這裏事件的執行順序跟Vue中相反,Vue中是先執行mixin中的函數, 再執行組件本身的函數。 - WXS用法稍有不同
/**
project
└── src
├── wxs
| └── mywxs.wxs wxs 文件
├── pages
| └── index.wpy 頁面
└──app.wpy
**/
// mywxs.wxs
module.exports = {
text: 'This is from wxs',
filter: function (num) {
return num.toFixed(2);
}
};
// index.wpy
<template>
<text>{{m1.text}}</text>
<text>{{m1.filter(num)}}</text>
</template>
<script>
import wepy from 'wepy';
import mywxs from '../wxs/mywxs.wxs';
export default class Index extends wepy.page {
data = {
num: 10
};
wxs = {
m1: mywxs
}
};
</script>
注意:
wxs是基於原生的wxs去實現的,只是通過編譯把現在的語法編譯爲原生語法(必須以wxs文件結尾引入)。
wxs必須是外鏈文件。並且後綴爲.wxs(必須外鏈)。
wxs引入後只能在template中使用,不能在script中使用(必須在script中引入並註冊,纔可在template中使用)。
- interpector攔截器使用
- 數據綁定---髒數據檢查
原生小程序通過Page提供的setData方法來綁定數據
this.setData({title: 'this is title'});
wepy綁定數據的方式(跟vue一樣,異步時候要加上this.$apply()
纔會觸發髒數據檢測保證性能)
this.title = 'this is title';
WePY使用髒數據檢查對setData進行封裝,在函數運行週期結束時執行髒數據檢查,一來可以不用關心頁面多次setData是否會有性能上的問題,二來可以更加簡潔去修改數據實現綁定,不用重複去寫setData方法。this.title = 'this is title';
需注意的是,在異步函數中更新數據的時候,必須手動調用$apply方法,纔會觸發髒數據檢查流程的運行
setTimeout(() => {
this.title = 'this is title';
this.$apply();
}, 3000);
- wx.request接收參數修改
// 原生代碼:
wx.request({
url: 'xxx',
success: function (data) {
console.log(data);
}
});
// WePY 使用方式, 需要開啓 Promise 支持,參考開發規範章節
wepy.request('xxxx').then((d) => console.log(d));
// async/await 的使用方式, 需要開啓 Promise 和 async/await 支持,參考 WIKI
async function request () {
let d = await wepy.request('xxxxx');
console.log(d);
}
- 優化事件參數傳遞
// 原生的事件傳參方式:
<view data-id="{{index}}" data-title="wepy" data-other="otherparams" bindtap="tapName"> Click me! </view>
Page({
tapName: function (event) {
console.log(event.currentTarget.dataset.id)// output: 1
console.log(event.currentTarget.dataset.title)// output: wepy
console.log(event.currentTarget.dataset.other)// output: otherparams
}
});
// WePY 1.1.8以後的版本,只允許傳string。
<view @tap="tapName({{index}}, 'wepy', 'otherparams')"> Click me! </view>
methods: {
tapName (id, title, other, event) {
console.log(id, title, other)// output: 1, wepy, otherparams
}
}
- 改變數據綁定方式
// 原生代碼:
<view> {{ message }} </view>
onLoad: function () {
this.setData({message: 'hello world'});
}
// WePY
<view> {{ message }} </view>
onLoad () {
this.message = 'hello world';
}
- 組件代替模板和模塊
簡單的說就是引入組件,這個地方wepy和vue語法一樣。而小程序是分部引入,在wxml中引入wxml組件,在js中引入js。
// 原生代碼:
<!-- item.wxml -->
<template name="item">
<text>{{text}}</text>
</template>
<!-- index.wxml -->
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>
<!-- index.js -->
var item = require('item.js')
// WePY
<!-- /components/item.wpy -->
<text>{{text}}</text>
<!-- index.wpy -->
<template>
<com></com>
</template>
<script>
import wepy from 'wepy';
import Item from '../components/item';
export default class Index extends wepy.page {
components = { com: Item }
}
</script>
- 存在的問題
WePY 1.x 版本中,組件使用的是靜態編譯組件,即組件是在編譯階段編譯進頁面的,每個組件都是唯一的一個實例,目前只提供簡單的 repeat 支持。不支持在 repeat 的組件中去使用 props, computed, watch 等等特性。
<!-- 錯誤使用 --->
// list.wpy
<view>{{test.name}}</view>
// index.wpy
<repeat for="{{mylist}}">
<List :test.sync="item"></List>
</repeat>
<!-- 推薦用法 --->
// list.wpy
<repeat for="{{mylist}}">
<view>{{item.name}}</view>
</repeat>
// index.wpy
<List :mylist.sync="mylist"></List>