“ less is more!” 寫代碼到現在,越來越喜歡這一句話。以少量的代碼,實現好的功能,這應該是我們所追求的,寫出優雅的代碼。
Vue
的mixin
大部分人應該是知道的,不過使用可能不多,倒不是不會用,較大的可能是想不到要用。今天分享一點mixin
的妙用,希望給各位一點小啟發,以后可以多用并且善用mixin
。
移動端開發,很多ui會出層次分明的樣式結構,比如背景色,可能會有一個統一風格的顏色。這時候我們需要給背景添加顏色,如果是將整個頁面框在一個組件內的,而組件又沒有提供背景色props
去修改,這時如果要修改背景色,通常想的就是修改原組件樣式,或者在組件外加個div
設置樣式,但是這些都會導致一個問題,樣式變成全局的了,并且一個項目可能很多頁面,每個頁面都這么搞,那就要命了。要想一個辦法,輕松的解決這個問題。
首先,為了比較少的寫代碼,修改原組件樣式或在外層再加個樣式控制,這樣都不太優雅,沒有體現可復用性。說到全局復用,就應該想到mixin
,上面的問題,一個輕松的混入方法就能解決。
export default{
data(){
return {
backgroundColor: 'white',
}
},
activated() {
this.$el.style.backgroundColor = this.backgroundColor;
},
deactivated() {
this.$el.style.backgroundColor = "white";
},
}
通常我們移動端項目都是設置keep-alive
的,所以在actived
和deactivated
來操作,因為這些混入的鉤子函數會和組件合并,所以只要在組件的data
里設置一下backgroundColor
的值為頁面背景色值,即可完成頁面內樣式背景色的控制,這就是混入。
前兩天有同事問我,他的頁面彈出了popup
,這時候安卓物理機點擊了返回,直接就路由返回了,有什么辦法可以解決阻止返回只隱藏popup
呢?我直接就回答說在組件的鉤子函數beforeRouteLeave
里(項目必須要使用vue-router
,這個鉤子函數是router
混入的)判斷下popup
是否關閉就行,沒有關閉return next(false)
就行,和我們常見的頁面未保存點擊返回提示一下是一樣的道理。說完了解決方法之后,我又想了下,能否全局解決這個問題呢,確實是很多頁面都會有popup
彈出的問題。
首先,我想了下是不是可以使用vue-router
的守衛函數beforeEach
方法,確實是可以的。
router.beforeEach((to, from, next) => {
const popup = document.querySelectorAll('.hips-popup')
let ifNeedHide = false
for (let i = 0; i < popup.length; i++) {
if(popup[i].style.display !== 'none'){
ifNeedHide = true
popup[i].style.display = 'none'
next(false)
break
}
}
!ifNeedHide && next()
});
因為這時候拿不到組件實例,所以我選擇dom
查詢,順便說一下querySelectorAll()
得到的是一個NodeList
,看著像數組,但是沒法使用forEach
去處理。上面的思路是找到當前頁面上所有的popup
組件,判斷它display
是否不為none
,是的話表面當前頁面返回要先關閉該popup
,然后再返回。但是這樣的話,有一些弊端,有些popup
不需要關閉就應該可以進行路由跳轉,比如說只是個提示框的那種popup
,這時如果要跳轉就必須先手動關閉popup
,親測關閉后的路由跳轉必須要設置延時,時間還不小于幾百毫秒才行(可能是由于dom渲染導致),還有就是如果有多層popup
父子嵌套,這也沒法正確的關閉popup
。所以很麻煩,還不如需要做攔截的時候自己攔截一下處理,而且盡量不直接操作dom
是我們的宗旨。這也是個全局的問題,mixin
當仁不讓,并且超級簡單。
export default {
methods: {
ifNeedHide(){
return true
}
},
beforeRouteLeave(to, from, next){
if(!this.ifNeedHide()){
return next(false)
}
next()
}
}
歸根結底問題所在就是路由跳轉前做下攔截,那mixin
只需要知道是否需要攔截即可。全局混入之后,如果組件需要攔截,那么復寫ifNeedHide
方法即可,返回一個false
就攔截了,不需要重復的寫beforeRouteLeave
,寫好業務邏輯就行。
還有個比較高級的用法,是以前寫組件的時候,同事使用的,寫的很好,一個混入方法,實現css
樣式在template
里的簡寫。
/**
* bem helper
* b() // 'button'
* b('text') // 'button__text'
* b({ disabled }) // 'button button--disabled'
* b('text', { disabled }) // 'button__text button__text--disabled'
* b(['disabled', 'primary']) // 'button button--disabled button--primary'
*/
const ELEMENT = '__'
const MODS = '--'
const join = (name, el, symbol) => el ? name + symbol + el : name
const prefix = (name, mods) => {
if (typeof mods === 'string') {
return join(name, mods, MODS)
}
if (Array.isArray(mods)) {
return mods.map(item => prefix(name, item))
}
const ret = {}
Object.keys(mods).forEach(key => {
ret[name + MODS + key] = mods[key]
})
return ret
}
export default {
methods: {
b (el, mods) {
const { name } = this.$options
if (el && typeof el !== 'string') {
mods = el
el = ''
}
el = join(name, el, ELEMENT)
return mods ? [el, prefix(el, mods)] : el
},
},
}
<template>
<button
:class="[
b([type, size, {
loading,
icon,
block,
ripple
}])]"
:disabled="disabled"
:type="htmlType"
@click="handleClick"
>
<spin v-if="loading" size="16px"/>
<ripple v-if="ripple && !loading && !disabled" :ripple-opacity="rippleOpacity" :ripple-color="rippleColor"/>
<slot name="icon">
<icon v-if="icon" :name="icon" />
</slot>
<slot/>
</button>
</template>
看看最上面的注釋,對照著template
,大家應該能看明白這塊的規則是什么,也很符合我們前端的BEM規范,很優雅的簡化了項目代碼。
說這么多,就是希望大家能善用mixin
,便捷的實現更多有趣的東西。