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' })