我们常常看到很多vue生态下的ui框架的消息提示框,在页面切换的时候依然停留在页面,我们知道vue这种单页面渐进式框架,所有的操作和元素都是挂载在一个节点上的,当路由变化是整个节点下的dom元素也在重新渲染,若要实现ui框架中的类似的消息提示框的话,理论上就该把这样的组件挂载到其他的节点上,这样就互不影响了。vue已经为我们考虑好了,在vue上提供了一个vue.extend()函数,可以创建一个“子类”。可查看Vue官方API

首先你得有一个消息提示框组件,我这里创建了一个VNotify组件。

<template>
  <div
    v-if="options.show"
    class="v-notify animated"
    :class="[options.type, options.hide ? 'fadeOutRight' : 'fadeInRight']">
    <h3 v-if="options.title" class="title">{{ options.title }}</h3>
    <div class="content">{{ options.message }}</div>
  </div>
</template>

<script>
import { reactive, onBeforeUnmount } from 'vue'

export default {
  name: 'VNotify',
  setup(props, ctx) {
    const options = reactive({
      type: '',
      show: false,
      hide: false, // 消失动画判断
      timeout: 3000
    })
    const init = (params) => {
      options.show = true
      options.title = params.title
      options.type = params.type
      options.message = params.message
      autoDestory()
    }
    // 自动销毁
    let timer
    let innerTimer
    const autoDestory = (timeout) => {
      timer = setTimeout(() => {
        options.hide = true
        innerTimer = setTimeout(() => {
          options.show = false
          options.hide = false
          clearTimeout(timer)
          clearTimeout(innerTimer)
        }, 400) // 动画时间
      }, options.timeout + 400)
    }
    onBeforeUnmount(() => {
      options.type = ''
      options.show = false
      options.hide = false
      clearTimeout(timer)
      clearTimeout(innerTimer)
    })
    return {
      options,
      init
    }
  }
}
</script>

<style lang="scss" scoped>
.v-notify {
  position: fixed;
  right: 12px;
  top: 24px;
  z-index: 999;
  padding: 12px;
  min-width: 296px;
  min-height: 146px;
  background-color: #fff;
  border: 2px solid transparent;
  box-shadow: 0 0 5px rgba($color: #000000, $alpha: 0.2);
  border-radius: 6px;
  overflow: hidden;
  animation-duration: 0.4s;
  .title {
    line-height: 44px;
    border-bottom: 1px solid rgba($color: #000, $alpha: 0.1);
    margin-bottom: 12px;
  }
  .content {
    line-height: 24px;
  }
}
.v-notify.success {
  border-color: $theme-color;
  .title {
    border-bottom-color: $theme-color;
  }
}
.v-notify.danger {
  border-color: $danger-color;
  .title {
    border-bottom-color: $danger-color;
  }
}
.v-notify.warning {
  border-color: $warning-color;
  .title {
    border-bottom-color: $warning-color;
  }
}
</style>

然后写一个js文件,在入口文件处引用,将组件挂载到body上。下面代码中我是用了一个类封装起来这是为了可以将使用此逻辑实现dialog,message等组件。也可以给子类继承,实现可以同事创建多个提示框。这里给出部分代码,功能根据需求扩展。其中的process.client这个是因为,在这个项目中使用了服务端渲染技术,保证能获取到body元素。若非服务端项目,直接挂在即可。

import Vue from 'vue'
import VNotify from '@/components/global/VNotify'

class InstallDialog {
  // 创建提示信息框
  static CreateNotify(v) {
    const NotifyConstructor = v.extend(VNotify)
    // 创建一个 #_nuxt实例外的VNotify实例并挂载document上
    const NotifyInstance = new NotifyConstructor().$mount() 
    v.prototype.$notify = (params) => {
      document.body.appendChild(NotifyInstance.$el) // 将组件根节点作为挂在的dom
      NotifyInstance.init(params) // 这里执行的组件提供的init函数
      return NotifyInstance
    }
  }
}
if (process.client) {
  Vue.use(InstallDialog.CreateNotify)
}

看看如何使用。

<template>
  <div id="vue-page" class="container">
    <button class="button--green" @click="showMessage">Open</button>
  </div>
</template>

<script>
export default {
  name: 'VuePage',
  setup(props, ctx) {
    const { root } = ctx
    const showMessage = () => {
      root.$notify({ // 直接调取我们挂在上的函数就能执行了
        type: 'success',
        title: 'Test title',
        message: 'Test message'
      })
    }
    return {
      showMessage
    }
  }
}
</script>

效果图。
效果图

Logo

前往低代码交流专区

更多推荐