wepy一些問題和解決方案
小程序開發和傳統的web開發有相識的地方,但是也有不同的地方,要區分。
computed屬性名和props屬性名重復
- 如果那個組件的渲染值是重名的computed屬性,每次點擊都是之前的計算加上最新計算的值
- 定義computed屬性名的時候避免和props屬性名重復
父子組件傳值和Vue中的區別
- Vue中的prop傳值默認是只要父組件的數據改變,子組件同時改變,但是這種模式在wepy中是靜態的 也即
:name="name"是靜態的,父組件改變name子組件還是不會變,子組件還是父組件第一次傳進來的值 - wepy要做到父組件數據變的同時,子組件也跟着變,要加個修飾符
.sync,也即:name.sync="name" - 還有一個是雙向綁定的問題,Vue中可以通過
$emit事件的方式實現,這里wepy可以在子組件中的props配置類型、默認值、以及是否雙向中的twoWay為true,就實現了雙向數據流 - 默認
twoWay的值是false,如果不是特別需要,請按單向數據的方式傳遞數據最好,再配合$emit、$broadcast、$invoke,盡量少用,除非是表單這種
如何給當前自定義組件或者頁面類添加靜態屬性
- 可以在
constructor構造函數中添加靜態屬性,但是要添加super(),才可以使用this來添加靜態屬性 - 由於
constructor執行時間比較早,所有在這里面的變量,可以直接用於wepy組件,也就是可以和data的數據一樣用於數據綁定,但是原生組件只能放在data中 - 如果有些數據是一次性的,不會改變的(靜態),最好在
constructor中定義,不要都放到data中,data中的數據都會被轉換成Observable數據的
組件與組件通信$broadcast、$emit、$invoke
- 這些方法不可以在
constructor調用,可以在onLoad中調用 events用於定義事件,用於接收用的- 如果想傳數據給子組件,可以使用
$broadcast('eventName', ...args),但是所有的后代組件都會觸發該事件,如果后代組件的events.eventName有定義到,會調用其函數,並將args傳給其函數 - 如果想傳數據給父組件可以使用
$emit,同理,父組件(到根組件)的events要事先定義好事件 $broadcast和$emit都會一次觸發多個事件,定義events的時候要注意$invoke('./componentName', 'methodName', args)可以直接調用指定組件的方法methods中定義的方法,如果是調用父組件可以用../往上層走,如果要調用下層的./oneLevelComponentName/twoLevelComponentName- 如果有很多組件都公用一個方法
methods或者events,還可以使用mixins
區分Page、App、Component、Mixin這些實例
- 有些生命周期鈎子是那個實例特有的,有些生命周期是所有都共享的
components不同於Vue或React的組件
- 小程序的組件和Vue和React組件是有區別的
- 在wepy中引入組件在
components中,和Vue的一樣,但是使用的時候是不同的,一個組件對應一個id,如果在同一個模板中使用同一組件超過2次,它們會共享數據 - 共享數據指的是props等,可以測試一下,同一個組件傳入的props名一樣,值不一樣,最終所有組件那的那個props值是最后一個值
- 因為它們共享同一個props,多個組件一起使用,類似定義多個同名的函數一樣,最后使用的是最后一個函數
- 為了避免這種情況下可以用同一個組件去定義多個組件名
{ coma: Button, comb: Button },實際上就是new多一個Button - 也就是說小程序中的組件在使用的時候,不會自動地new一個新的組件,而是同一個組件,而像
Vue和React這樣的組件,它們會new一個新的Button實例 - 還有一種情況是,在循環體
<repeat>中,在小程序中就會去new一個新的組件實例,如果你的內容重復,又不想定義多個組件名(new 多個組件),可以使用循環體<repeat> - 還有一種情況是
slot,如果那個組件和數據無關,只是單純的用於slot模板,它們最終生成的標簽是不同的,因為這些生成的模板和數據無關,如果生成的模板中包含數據,那生成那部分關聯數據的元素都是一樣的 - 這里說的數據是
<view class='{{ className }}' >{{ data }}</view>這些花括號中的數據className和data,它們是共享的,所以它們最終生成的視圖是一模一樣的 - 小程序的組件和目前流行的web組件有很大的區別,不能老用web組件傳值得思維思考(很多莫名的bug),多從廣播(訂閱)的角度思考
repeat注意點
- 分兩種情況
//repeat包着元素view text等
<repeat for="{{mylist}}">
<view>{{item.name}}</view>
</repeat>
//repeat包着自定義組件
<repeat for='{{myList}}'>
<List :mylist.sync="mylist"></List>
<Item :item='item'></Item>
</repeat>
- 不支持在
repeat的組件中使用props、computed、watch等特性 - 在使用
repeat的時候,盡量使用純文本渲染的組件,純組件 - 原生組件的情況下,可以使用
.或者[]的方式選擇想傳的數據,但是自定義組件不可以,要整個數據傳入,或者傳入item([{}, {}]這種格式的數據),item就是wx:item - 數據源要在
data中,才有效果,靜態屬性不行 - 盡量只包含一個組件,出現多個組件,會出現莫名的bug,其它的功能再在這一個組件內部處理,然后組件間的通信通過
$broadcast、$emit、$invoke - 使用組件多使用
$broadcast、$emit、$invoke的方式傳值,別老想着使用props,會有很多莫名的bug - 關於
$broadcast、$emit、$invoke,可以通過mixins拯救一下,唯一沒什么莫名bug的
Object.create(null)
- 上面返回的對象不帶原型鏈,也即不包含
Object原生對象的信息 - 小程序中一些數據,像
data需要的對象要帶原型鏈,不能用這個,否則在綁定數據到模板的時候會報錯
字符串雙引號""和單引號''
- 在模板中務必使用
"",別用'' :age='2'和:age="2",前面的''是傳不了2給age的,除非使用"2"- 在模板中最外層字符串最好用
""不要使用'',比如定義事件函數的時候'handlerTap("123")',這里是不成功的給handlerTap傳遞參數"123",但是"handlerTap('123')"就可以 - 正確用法:
@tap="handlerTap('love')",最外層用"",最內層''
mixins的computed順序問題
- mixins中的computed執行順序和vue的相反,也即先執行component或者頁面的定義的,然后再執行mixins中的
- 還有就是延遲的問題,mixins的慢於組件或者當前頁面定義的,如果想在當前頁面獲取mixins的一些值,會獲取不到
組件傳值和模板綁定問題
- 一個是上面提到的字符串要用雙引號
- 然后就是要區分原生組件和自定義組件
原生組件:就是小程序自帶的組件<view>、<text>、<image>等等,這些原生組件綁定數據,要求數據必須在data中,如果綁定的數據不在data中將不會渲染
<template>
<view class="{{className}}">{{text}}</view>
</template>
<script>
import wepy from 'wepy'
// 注意:這個是組件實例
export default class Test extends wepy.component {
data = {
text: 123,
className: 'hahah'
}
// 由於computed最后返回值會成為data中一部分,computed也可以用於原生組件數據綁定
// 但是在repeat的時候,不可以使用這個,repeat的時候不可以使用computed和watch
computed = {
text () {
return this.text + 1
}
}
}
</script>
自定義組件:綁定數據可以是data中,也可以用自定義函數,或者靜態變量來綁定數據,且綁定數據的方式要和原生小程序的一樣,不能用vue的方式
<template>
<UserComponent :fn="fn()" :staticV="staticVar" :text="text"/>
</template>
<script>
import wepy from 'wepy'
// 注意:這個是頁面實例
export default class Index extends wepy.page {
constructor () {
super()
this.staticVar = 123
}
data = {
text: 123
}
// 自定義函數
fn () {
return '123'
}
// 由於computed最后返回值會成為data中一部分,computed也可以用於原生組件數據綁定,如果和data中的屬性重名,優先使用computed的
computed = {
text () {
return this.text + 1
}
}
}
</script>
原生組件的綁定只能是data中的數據,不可以像vue中的那樣綁定數據,但是如果是自定義的組件就可以像vue那樣綁定數據。還有methods自能用於定義事件處理程序,不能把自定義函數定義在里面
也就是碰到原生組件的時候,請用原生小程序的方式處理,碰到自定義組件的時候才考慮用你vue的方式處理,也就是區分原生組件和自定義組件非常重要
圖片和導航的路徑問題
- 不要在組件中使用相對定位,如
../這種,因為當組件被引入到某個頁面時,會相對哪個頁面,導致路徑無法復用 - 使用絕對定位,
/,在小程序中,/是指的當前項目的文件夾,例如:/pages/index,這里的首個字符/就相當於@/都是指向/src目錄 - 如果是圖片,如
@/assets/icons/logo.png可以用/assets/icons/logo.png,因為第一個字符是/就相當於@/,表示/src目錄之下 - 如果是導航
wx.navigateTo,如果導航到/pages/user,不要使用/或者../,直接user,最終會自動拼成/pages/user,也就是說微信默認/pages/+user
canvas中的canvas-id不能動態賦值
- 可以通過slot來實現一個組件,不同canvas-id
<component-name sid="canvas">
<canvas canvas-id="canvas"></canvas>
</component-name>
component-name是一個組件,接收sid只要是為了可以wx.createCanvasContext(this.sid)- 其實我們在組件的內部只需要知道canvas的id,並且調用
wx.createCanvasContext(this.sid),所以我們可以把canvas獨立成slot
在一個組件中引入另一個組件作為子組件,子組件的樣式<style>不起作用
- 是因為父組件沒有
<style>標簽,導致子組件不編譯<style>標簽的內容 - 最好還是每個組件都添加
<style>標簽,即使里面沒有內容
