此次的教程裏,咱們要把組件化進行到底!最近半年的幾個項目中,都遇到了須要使用Toast或者Notification組件的狀況。在目前已有的一些基於Vue.js開發的組件庫,都沒有找到太合適的,因此本身重頭實現了一個。歷經幾個項目的磨練,這個提示組件的功能已經愈來愈完善,此次就分享一下組件以及其實現思路吧。css
GitHub 倉庫:https://github.com/Yuyz0112/vue-notie
Demo 地址:http://lab.myriptide.com/vue-notie/vue
Vue.js的組件化能夠說是其招牌特性之一,而在實際應用時,並不是一味地追求組件顆粒越小越好,而是須要根據項目的實際需求,來分析本身須要什麼級別的組件。git
例如在一個SPA中,我可能有主頁、文章列表頁、文章頁、我的中心頁4個主要的視圖,因而我將其分別對應的寫成4個組件。github
可是在實際編寫的過程當中,發現他們共用了同一套側邊欄,而側邊欄對應的代碼也在4個組件中重複書寫了4次。因此能夠將側邊欄單獨寫成一個組件進行復用。web
以後,咱們可能發現能夠複用的還有一些表單、按鈕之類的內容咱們均可以複用成組件。但實際上,咱們也會發現過分的組件化會致使代碼量上升、開發時間增長以及額外的數據傳遞等等。因此若是不打算製做一個完整的組件庫,那麼在實際項目中作到按需拆分、整合便可,不用過度的追求每一個可複用的部分都寫成單個組件。vuex
由於alert大部分時間不能知足咱們的需求啊。每每項目裏須要一個相似於alert的東西,用美觀、可定製的方式提示用戶一些信息,所以這樣一個提示組件頗有必要。數組
同時,咱們也不但願同一時間出現多個提示混淆用戶,所以在設計上,咱們將提示組件設定爲具備惟一性,整個應用中各個視圖調用的都是同一個提示組件。sass
接下來,由簡入繁依次實現提示組件的各個功能。app
最基本的功能固然是觸發後顯示,而且可以以某種方式關閉。惟一須要自定義的部分,就是具體顯示的內容。因此最開始組件長這樣:ide
<template> <div class="notification fixed" v-if="show" transition="slide"> <div class="delete" @click="close()"></div> <div class="content"> {{ options.content }} </div> </div> </template> <script> export default { props: { options: { type: Object, default: () => { return {} } }, show: { type: Boolean, default: false } }, methods: { close () { this.show = false this.options = {} } } } </script> <style scoped lang="sass"> .slide-transition transition: all .3s ease transform: translate3d(0, 0, 0) .slide-enter, .slide-leave transform: translate3d(0, -100%, 0) .delete -moz-appearance: none -webkit-appearance: none background: rgba(51,51,51,0.2) cursor: pointer display: inline-block height: 24px position: relative vertical-align: top width: 24px float: right &:before, &:after background: #fff content: "" display: block height: 2px left: 50% margin-left: -25% margin-top: -1px position: absolute top: 50% width: 50% &:before transform: rotate(45deg) &:after transform: rotate(-45deg) &:hover background: rgba(51,51,51,0.5) .notification width: 100% line-height: 2 z-index: 3 position: fixed top: 0 left: 0 .content padding: .75rem 2rem </style>
思路很簡單,props傳遞兩個數據,show用於控制顯示,options傳入包括內容在內的自定義內容。爲了讓提示的顯示更加天然,添加了一個滑動進入和離開的transition。
注意:這裏的關閉按鈕是經過css實現的,若是在你的項目中有對應的icon,能夠將其替換掉。
在此處,也可使用slot來進行內容的傳遞,但考慮到以後還有別的參數須要傳遞至組件內,一次用一個統一的對象options進行傳遞。
一般提示的內容種類不少,有的是成功提示,有的是警告,有的則是報錯。所以咱們須要定義不一樣的樣式以表達不一樣的內容。
方法很簡單,在options中傳入背景色和文字顏色兩個參數,若是組件中檢測到了傳入的樣式參數,就用其替換默認樣式。
Vue.js在處理動態樣式時很是靈活,爲了讓代碼更清晰,我沒有選擇將動態樣式內聯,而是單獨使用一個計算屬性setStyle進行設定:
computed: { setStyle () { return { color: this.options.textColor || '#fff', background: this.options.backgroundColor || '#21e7b6' } } }
這樣一來,只要在options中一併傳入textColor和backgroundColor兩個屬性,就能夠輕鬆自定義提示樣式了。
不少時候,咱們但願提示在必定時間以後能夠自動關閉,所以組件也須要擴展出一個自動關閉的模式。一樣的,在「數據驅動」的思想下,咱們應該提供一個數據,用來代表這個提示是否自動關閉。
options中的autoClose屬性就是這個做用。一樣的,自動關閉的延遲時間顯然也要可以自定義,所以還一同添加了showTime這一屬性。
自動關閉自己不太複雜,咱們只須要使用setTimeout,定義一個計時器便可。
首先是監聽提示組件的顯示。
在這裏,我經過watch監聽options的變化來處罰計時器。因爲咱們已經定義了一個close方法用於關閉計時器,而且在關閉時重置了show和options的值,因此在options變化時,只須要判斷options中的autoClose是否爲true,就能知道是否須要啓動計時器了。這裏單獨使用一個countdown方法來處理定時器相關的操做。
新增代碼以下:
data () { return { timers: [] } }, methods: { countdown () { if (this.options.autoClose) { const t = setTimeout(() => { this.close() }, this.options.showTime || 3000) this.timers.push(t) } } }, watch: { options () { this.timers.forEach((timer) => { window.clearTimeout(timer) }) this.timers = [] this.countdown() } }
細心地你確定會發現,這段代碼中,有一些奇怪的處理。咱們定義了一個空數組timers,而且每次開始一個計時器的時候,就把計時器存入數組中,而每次options變化時,咱們也從timers中遍歷全部計時器並取消,以後清空timers。
這個作法,主要是爲了不一個計時器尚未結束時,又開始一個新的提示所引起的提示被提早關閉的清空。舉個例子,若是沒有這樣的處理,那麼先發出一個自動關閉的提示,在其沒自動關閉以前,就再發出一個新的提示。那麼第一個提示的定時器依然會錯誤的關閉新提示。
這樣的問題主要是因爲咱們全部的計時器都是在同一個組件中,本質上都是同一個提示,所以須要清除計時器,避免衝突。許多組件庫中相似的功能組件,是採用每一條提示就新生成一個提示組件的方式來實現的。可是那樣在多個提示連續出現時,就會出現堆疊在一塊兒,又各自離開的狀況。
以前的版本中,個人提示組件也採用了相似的設計方式,可是在最近的一個項目中,須要實現半透明的提示組件,就出現了堆疊後看不清提示文字的現象,才使用瞭如今新的模式。
緊接着,我拓展了一個自動關閉模式下的倒計時條功能。思路上沒有使用Vue.js的transition系統,而是採用了Css3自己的動畫系統。在一個自動關閉的提示被初始化時,爲計時條添加一個樣式,效果是向X軸負方向移動100%,transition時間則經過計算屬性對應設定。具體實現能夠參考源代碼,這裏很少作贅述。
最後則是讓提示組件更靈活。有的時候,咱們想展現的多是能夠自定義樣式的文本、亦或是一個超連接甚至更多。而Vue.js實現起來不要太簡單。咱們只須要將組件中用於渲染的{{ options.content }}
變爲{{{ options.content }}}
便可,對於3重花括號的模板,Vue.js會將其中的HTML標籤按照正常內容渲染。
如此一來,咱們就能夠將任何HTML內容放入提示中了。固然必定要注意避免將用戶輸入的內容渲染到3重花括號的模板中,避免XSS攻擊。
不少時候,咱們會把提示組件引入到App.vue這個根組件中,可是發出提示的多是組件樹中的任何一個組件。若是不想代碼中遍及各類dispatch和broadcast,那麼引入vuex來進行管理是個很好的方案。
大體的思路以下:
// store.js const state = { show: false, options: { autoClose: false, content: 'notice content' } } const mutations = { NEW_NOTICE (state, options) { state.show = true state.options = options }, CLOSE_NOTICE (state) { state.show = false state.options = {} } } // actions.js export const newNotice = ({dispatch}, options) => { dispatch('NEW_NOTICE', options) } export const closeNotice = ({dispatch}) => { dispatch('CLOSE_NOTICE') } // Notification.vue vuex: { getters: { show: state => state.show options: state => state.options }, actions: { close: closeNotice } } // 任意調用notice的組件 vuex: { actions: { notice: newNotice } }
引入vuex後,按上述代碼進行配置,就能夠在任意一處組件中,使用this.notice({options})
傳遞數據。不過因爲vuex的單項數據流動特性,全部對state數據的操做都必須通過actions調用mutations實現,包括提示組件中的close方法也要替換成actions中的closeNotice方法。
經過這個提示組件,咱們更熟練的掌握了Vue.js的組件系統、數據傳遞、計算屬性、transition動畫等特性。另外此組件已經能夠直接用於生產環境中,歡迎star、fork、pr。