1. 程式人生 > >造輪子-toast元件的實現(下)

造輪子-toast元件的實現(下)

1.解決 toast 中傳入 html 的問題,通過假的 slot 來實現

// plugins.js
toast.$slots.default = [message]
// toast.vue
 <div v-html="$slots.default[0]"></div>
// 使用
created() {
    this.$toast('<p>我是<strong>hi</strong></p>',{})
},

2.在 toast 中加 html 是比較危險的一個動作,所以要加一個選項預設不開啟。

// toast.vue
<slot v-if="!enableHtml"></slot>
<div v-else v-html="$slots.default[0]"></div>
// plugin.js,進行傳遞引數的改寫
propsData:toastOptions
// 使用
created() {
  this.$toast('<p>我是<strong>hi</strong></p><a href="http://qq.com">qq</a>',{
    enableHtml: false
  })
},

3.flex-shrink的使用,flex-shrink屬性定義了專案的縮小比例,預設為1,即如果空間不足,該專案將縮小。

.item {
  flex-shrink: <number>; /* default 1 */
}

如果所有專案的flex-shrink屬性都為1,當空間不足時,都將等比例縮小。如果一個專案的flex-shrink屬性為0,其他專案都為1,則空間不足時,前者不縮小。如果數值越大,縮小比例越大。

4.line的高度問題,如果高度寫了最小高度,那麼子元素的height%就不生效了。用js去操作line的高度。

// toast.vue
<div class="toast" ref="wrapper">
    <div class="line" ref="line"></div>
</div>

mounted() {
  this.$nextTick(()=>{
    this.$refs.line.style.height = `${this.$refs.wrapper.getBoundingClientRect().height}px`
  })
}, // 這個計較太trick

debugger的技巧,如果眼睛觀察到的是0,但是打印出來不是0,可能就是非同步的問題。

5.增加toast的位置。

// toast.vue
props: {
  position: {
    type: String,
    default: 'top',
    validator(value){
      return ['top', 'bottom', 'middle'].indexOf(value) >= 0
    }
  }
},
computed:{
  toastClasses(){
    return {
      [`position-${this.position}`]:true
    }
  }
}
// 使用
this.$toast('你的智商需要充值', {
  position: 'bottom'
})
// plugin.js
export default {
  install(Vue, options){
    Vue.prototype.$toast = function (message, toastOptions) {
      let Constructor = Vue.extend(Toast)
      let toast = new Constructor({
        propsData:toastOptions // 在這裡將position傳進去
      })
      toast.$slots.default = [message]
      toast.$mount()
      document.body.appendChild(toast.$el)
    }
  }
}

6.開始做如果已經有一個toast就把之前那個幹掉,再出現。

  1. 先寫一個函式
  2. 給函式取一個名字
  3. 把引數提出來
// plugin.js
import Toast from './toast'

let currentToast

export default {
  install(Vue, options){
    Vue.prototype.$toast = function (message, toastOptions) {
      if(currentToast){
        currentToast.close()
      }
      currentToast = createToast({Vue,message, propsData: toastOptions})
    }
  }
}

/* helpers */
function createToast ({Vue,message,propsData}){
  let Constructor = Vue.extend(Toast)
  let toast = new Constructor({propsData})
  toast.$slots.default = [message]
  toast.$mount()
  document.body.appendChild(toast.$el)
  return toast
}

7.實現動畫

  1. 宣告一個動畫,然後寫到類上面
   @keyframes fade {
     0% {opacity: 0; transform: translateY(100%);}
     100% {opacity: 1;transform: translateY(0);}
   } 
   .toast {
     animation: fade 1s;
   }
  1. 這裡有個bug,我們在實現一次的時候是有問題的,如果toast被關閉了,我們不需要重複關閉,而我們寫的是不管你之前的toast有沒有關閉,只要有值的我們就關閉,那這樣就會出現一個問題,點了關閉currentToast還是一個Toast並沒有把它變成null,所以要加上一個回撥告訴外面,我被關了不要重複關我,程式碼會多調一次close。
  // toast.vue
  close() {
    this.$el.remove()
    this.$emit('close')
    this.$destroy()
  }
  // plugin.js
  export default {
    install(Vue, options){
      Vue.prototype.$toast = function (message, toastOptions) {
        if(currentToast){
          currentToast.close()
        }
        currentToast = createToast({Vue,message, propsData: toastOptions,onclose: ()=>{
            currentToast = null
          }
        }) // 加了這句話
      }
    }
  }

  /* helpers */
  function createToast ({Vue,message,propsData,onclose}){
    let Constructor = Vue.extend(Toast)
    let toast = new Constructor({propsData})
    toast.$slots.default = [message]
    toast.$mount()
    toast.$on('close',onclose) // 加了這句話
    document.body.appendChild(toast.$el)
    return toast
  }
  1. git相關的鉤上,不想管的不用鉤上
  2. 回憶bug是如何產生的,預設樣式是:transform:translateX(-50%),進入0%時候transform:translateY(100%),它們兩會覆蓋。有三個方案,解決。
    • 換一種方式去做居中,但是這種方法是最好的,很難想
    • 不要用css做動畫
    • 做兩個div外面一個居中,裡面一個做動畫
  3. 為什麼不寫兩個動畫幀來控制居中,如果一段程式碼要背下來,那麼一定是有問題的。
  4. 優化三種動畫,上下中是不一樣的,通過css進行優化。