一起來做一下Vue全局提示組件

全局提示組件在前端中算是比較重要的,在開發業務時候肯定能用的上,畢竟任何報錯隻要提示“服務器異常”就可以完美把鍋扔給後臺(手動滑稽)

全局提示組件在人氣比較🔥的 UI 組件庫必有他身影,可能叫法不太相同,有叫 message、toast、alert 等,但就是這麼一玩意。

拿 ant-design-vue 組件庫為例

其核心代碼

message.info('This is a normal message')

他是API的方式進行調用組件,以平常使用components註冊組件,之後在template中使用的方式不太相同

想要實現這個,其實並不困難

但在之前我想聲明一下:ant-design-vue 的 message 做個type分類,有info、success、error等。但為瞭讀者方便理解,我們統一就用message來進行調用,讀者可以根據本文提供的demo源碼自行進行調整(貼一下 ant-design-vue 源碼)

首先,我們在 components 目錄下創建 message 目錄,同時創建 Message.vue 和 index.js 文件

然後在 Message.vue 中寫

<!-- Message.vue -->
<template>
  <div>{{ content }}</div>
</template>

<script>
export default {
  name: 'Message',
  props: {
    content: {
      type: String,
      default: ''
    }
  }
}
</script>

再者在 index.js 中寫

// index.js
import { render, createVNode } from 'vue'
import Message from './Message.vue'

export default function message (content) {
  const div = document.createElement('div')
  const vm = createVNode(Message, {
    content
  })
  render(vm, div)
  document.body.appendChild(div)
}

最後在 app.vue 引入使用

<!-- app.vue -->
<template>
</template>
<script>
import message from './components/message'
export default{
  setup() {
    message('消息組件1')
    message('消息組件2')
  }
}
</script>

效果:

ok,這樣就實現瞭以API的方式進行調用組件

其核心代碼其實隻有以下

const div = document.createElement('div')
const vm = createVNode(Message, {
  content
})
render(vm, div)
document.body.appendChild(div)

createVNode 可以認為就是h函數,支持直接轉成虛擬dom對象

再去調用 render,渲染至div下,再將div插入body中

由於我們做的是全局組件,應該不被任何因素幹擾,比如 vue-router

但是上面例子我們會發現,我們隻要調用一個 message 方法,就要創建一個div插入至body,這顯然不是符合vue數據驅動視圖的理念

那,接下裡進行改造

先從 Message.vue 文件開始

我們先定義一個消息列表messages,之後提供添加 add 和刪除delete消息列兩方法,再暴露add方法出去

當然,刪除消息是需要根據id進行刪除,可不能瞎刪

<!-- Message.vue -->
<script>
import { ref, unref } from 'vue'
export default {
  name: 'Message',
  setup (props, { expose }) {
    // 消息列表
    const messages = ref([])
    
    let id = 0
    // 生成id
    const uuid = () => `message_${id++}`
    
    // 添加消息對象
    const add = (message) => {
      const id = uuid()
      const _message = {
        ...message,
        id
      }
      unref(messages).push(_message)
      const { duration = 3 } =  message
      // 設置定時器
      const timer = setTimeout(() => {
        clearTimeout(timer)
        remove(id)
      }, duration * 1000)
    }
    
    // 根據 id 刪除消息對象
    const remove = (id) => {
      messages.value = unref(messages).filter(message => message.id !== id)
    }
    
    // 暴露出add 和 remove
    expose({
      add
    })
    
    return {
      messages
    }
  }
}
</script>

用v-for把消息列表渲染出來,同時使用transition-group做一些列表過渡動畫

<!-- Message.vue -->
<template>
  <transition-group
    class="message"
    tag="div"
  >
    <div
      class="message-content"
      v-for="message in messages"
      :key="message.id"
    >
      {{ message.content }}
    </div>
  </transition-group>
</template>

再編寫一點消息列表樣式和其彈出動畫樣式

<!-- Message.vue -->
<style scoped>
.message {
  position: fixed;
  z-index: 999;
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
}

.message-content {
  padding: 8px 16px;
  border-radius: 3px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
  background: #fff;
  margin-bottom: 20px;
}

.v-enter-active,
.-leave-active {
  transition: all 200ms ease-in;
}
.v-enter-from,
.v-leave-to {
  opacity: 0;
  transform: translateY(-30px);
}
</style>

再改造一下入口文件

之前我們發現在 app.vue 中調用一次 message方法,就會一次 dom 操作,那我們使用閉包寫一個單例模式進行改造

再者我們在 Message.vue 暴露出add的方法可以直接進行操作消息列表

// index.js
import { render, createVNode } from 'vue'
import Message from './Message.vue'

let vm
// 使用單例模式,不再重新插入body
function getMessageInstance () {
  if (vm) return
  const div = document.createElement('div')
  vm = createVNode(Message)
  render(vm, div)
  document.body.appendChild(div)
}

export default function message (content = '', duration) {
  getMessageInstance()
  vm.component.exposed.add({
    content,
    duration
  })
}

最後在 app.vue 文件中隨便編寫一些測試代碼

<!-- app.vue -->
<template>
  <button @click="onClick">測試</button>
</template>
<script>
import message from './components/message'
export default{
  setup() {
    message('消息組件1', 4)
    message('消息組件2', 2)
    let index = 3
    const onClick = () => {
      message(`消息組件${index++}`)
    }
    return {
      onClick
    }
  }
}
</script>

看看最終效果

好,這就是全局提示組件設計大致思路。

回顧一下會發現,其實Vue的組件開發依舊繞不開 JavaScript,可見 JavaScript 在前端的份量。

總結

到此這篇關於Vue全局提示組件的文章就介紹到這瞭,更多相關Vue全局提示組件內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: