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>
標簽,即使里面沒有內容