Using vue to implement a message notification component from zero

Keywords: Vue less

Using vue to implement a message notification component from zero

At ordinary times, we must have used some UI frameworks like element UI and antd to feel the convenience they bring us. But when the difference between our requirements or the design of these frames is too big, it will be very awkward to use them. At this time, it is necessary to rebuild the wheels by ourselves.

There are several advantages of remaking the wheel. 1. All the code serves your business, and there are not too many useless things. 2. The code is maintained by itself, not by a third party, which is convenient for maintenance. 3. Improve your vision and stand at a higher point of view.

OK, let's start our component development!

Components of the file directory

If you want to be good at your work, you must first make use of your tools. If you want to realize a component and a good directory structure, you can divide responsibilities and different modules handle different logics!

My catalog results like this:

Next, we will introduce three files: notification.vue, notify.js and index.js.

notification.vue

notification.vue is responsible for the visual presentation of the message notification component, and its logic is very simple.

<template>
  <transition name="fade" @after-enter="handleAfterEnter">
    <div class="notification" :style="style" v-show="visible">
      <span class="notification__content">
        {{content}}
      </span>
      <span class="notification__btn" @click="handleClose">{{btn}}</span>
    </div>
  </transition>
</template>
<script>
export default {
  name: 'Notification',
  props: {
    content: {
      type: String,
      required: true
    },
    btn: {
      type: String,
      default: 'Close'
    }
  }
}
</script>
<style lang="less" scoped>
.fade-enter-active, .fade-leave-active{
  transition: opacity 1s;
}
.fade-enter, .fade-leave-to{
  opacity: 0;
}
.notification{
  display: flex;
  background-color: #303030;
  color: rgba(255, 255, 255, 1);
  align-items: center;
  padding: 20px;
  position: fixed;
  min-width: 280px;
  box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.3);
  flex-wrap: wrap;
  transition: all 0.3s;
  &__content{
    padding: 0;
  }
  &__btn{
    color: #ff4081;
    padding-left: 24px;
    margin-left: auto;
    cursor: pointer;
  }
}
</style>

notify.js

notify.js is a logical part of the message notification component. Its main function is to expose a notify method. The code is as follows:

import Vue from 'vue'
import Notification from './notification'

const NotificationConstructor = Vue.extend(Notification)

const instances = []
let seed = 1
const removeInstance = (instance) => {
  if (!instance) return
  const len = instances.length
  const index = instances.findIndex(ins => instance.id === ins.id)

  instances.splice(index, 1)

  if (len <= 1) return
  const removeHeight = instance.height
  for (let i = index; i < len - 1; i++) {
    instances[i].verticalOffset = parseInt(instances[i].verticalOffset) - removeHeight - 16
  }
}
const notify = (options = {}) => {
  if (Vue.prototype.$isServer) return
  // Get vue instance
  let instance = new NotificationConstructor({
    propsData: options,
    data() {
      return {
        verticalOffset: 0,
        timer: null,
        visible: false,
        height: 0
      }
    },
    computed: {
      style() {
        return {
          position: 'fixed',
          right: '20px',
          bottom: `${this.verticalOffset}px`
        }
      }
    },
    mounted() {
      this.createTimer()
      this.$el.addEventListener('mouseenter', () => {
        if (this.timer) {
          this.clearTimer(this.timer)
        }
      })
      this.$el.addEventListener('mouseleave', () => {
        if (this.timer) {
          this.clearTimer(this.timer)
        }
        this.createTimer()
      })
    },
    updated() {
      this.height = this.$el.offsetHeight
    },
    beforeDestroy() {
      this.clearTimer()
    },
    methods: {
      createTimer() {
        this.timer = setTimeout(() => {
          this.visible = false
          document.body.removeChild(this.$el)
          removeInstance(this)
          this.$destroy()
        }, options.timeout || 3000)
      },
      clearTimer() {
        if (this.timer) {
          clearTimeout(this.timer)
        }
      },
      handleClose() {
        this.visible = false
        document.body.removeChild(this.$el)
        removeInstance(this)
        this.$destroy(true)
      },
      handleAfterEnter() {
        // eslint-disable-next-line no-debugger
        this.height = this.$el.offsetHeight
      }
    }
  })

  const id = `notification_${seed++}`
  instance.id = id
  // Generate $el in vue
  instance = instance.$mount()
  // Insert the contents of $el into the dom node
  document.body.appendChild(instance.$el)
  instance.visible = true

  // eslint-disable-next-line no-unused-vars
  let verticalOffset = 0

  instances.forEach(item => {
    verticalOffset += item.$el.offsetHeight + 16
  })

  verticalOffset += 16
  instance.verticalOffset = verticalOffset

  instances.push(instance)

  return instance
}

export default notify

index.js

index.js is mainly used to register the notification.vue component and mount the notify method. The code is as follows:

import Notification from './notification'
import notify from './notify'

export default (Vue) => {
  Vue.component(Notification.name, Notification)
  Vue.prototype.$notify = notify
}

Import in main.js

import Notification from './components/notification'
Vue.use(Notification)

Use

this.$notify({
  content: 'Hello'
})

Effect

135 original articles published, 29 praised, 30000 visitors+
Private letter follow

Posted by Mind freak on Mon, 02 Mar 2020 22:58:57 -0800