在element中,有一些全局的组件,类似于this. m e s s a g e ( ) , t h i s . message(),this. message(),this.toast()这种,通过原型的方式挂载在vue的全局上面,以供我们全局使用,平时自己用的多,那么我现在就尝试自己来实现一下。

一.普通组件方式
我们实现一个普通的提示框(Alert)组件不难,一般都是这样写:

<template>
  <div>
    <Alert v-if="show">这是一条提示信息</Alert>
    <button @click="show = true">显示</button>
  </div>
</template>
<script>
  import Alert from '../component/alert.vue';

  export default {
    components: { Alert },
    data () {
      return {
        show: false
      }
    }
  }
</script>

这样的做法很简单粗暴,但是伴随着以下的问题:

  • 每个使用组件的地方,都要注册组件。
  • 需要预先把Alert组件放置于模板中。
  • 需要通过额外的data来控制Alert显示状态。
    这对于使用者并不算友好。所以我们就需要进行改造。
    我们期望最终的使用方式是这样的:
methods: {
  handleShow () {
    this.$Alert({
      content: '这是一条提示信息',
      duration: 3
    })
  }
}

这样,this$alert()可以在任意的位置进行调用,无需单独引用。这个方法接受两个参数:

  • content:提示内容
  • duration:持续时间,单位为秒,默认为1.5秒

二.编写Alert组件

<template>
  <transition name="fade">
    <div class="alert" v-if="notices.length>0">
      <div class="alert-main" v-for="item in notices" :key="item.name">
        <div class="alert-content" :class="[item.type ? item.type:'primary']">{{item.content}}</div>
      </div>
    </div>
  </transition>
</template>

<script>
let seed = 0;
function getUuid() {
  return "alert_" + seed++;
}
export default {
  data() {
    return {
      notices: []
    };
  },
  methods: {
    // alert添加方法
    add(notices) {
      const name = getUuid();
      let _notice = Object.assign(
        {
          name: name
        },
        notices
      );
      if (this.notices.length === 0) {
        this.notices.push(_notice);
        const duration = _notice.duration;

        setTimeout(() => {
          this.remove(name);
        }, duration * 1000);
      }
    },
    // 删除alert的方法
    remove(name) {
      const notices = this.notices;
      for (let i = 0; i < notices.length; i++) {
        if (notices[i].name === name) {
          this.notices.splice(i, 1);
          break;
        }
      }
    }
  }
};
</script>

<style scoped lang="scss">
.alert {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  text-align: center;
  pointer-events: none;
}
.alert-main {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  background: rgba($color: #000000, $alpha: 0.7);
}
.alert-content {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 0.3rem;
  font-size: 0.14rem;
  background: $primary-color;
  color: $font-color;
  border-radius: 3px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
  &.success {
    background: $success-color;
  }
  &.warn {
    background: $warn-color;
  }
  &.info {
    background: $info-color;
  }
  &.error {
    background: $error-color;
  }
}

.fade-enter {
  opacity: 0;
}
.fade-enter-active {
  transition: opacity 0.2s;
}
.fade-leave-to {
  opacity: 0;
}
.fade-leave-active {
  transition: opacity 0.2s;
}
</style>

因为Alert组件是通过js来进行调用的,则不需要保留props和event,在上面只需要向添加notices增加数据,这个提示框就可以显示内容。注意:如果要给元素添加动画,则需满足以下条件:

  • 条件渲染 (使用 v-if)
  • 条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点
    3.实例化封装
    在src/components/alert/src目录下,新建notification.js
import Alert from './alert.vue';
import Vue from 'vue';
Alert.newInstance = properties => {
  const props = properties || {};
  const Instance = new Vue({
    data: props,
    render(h) {
      return h(Alert, {
        props: props
      })
    }
  })
  const component = Instance.$mount();
  document.body.appendChild(component.$el);
  const alert = Instance.$children[0]; // 找到alert实例目的是需要alert实例本身的add方法
  // console.log(alert);
  return {
    add(noticeProps) {
      alert.add(noticeProps);
    },
    remove(name) {
      alert.remove(name);
    }
  }
}
export default Alert;

它只是对 alert.vue 添加了一个方法 newInstance。虽然 alert.vue 包含了 template、script、style 三个标签,并不是一个 JS 对象,那怎么能够给它扩展一个方法 newInstance 呢?事实上,alert.vue 会被 Webpack 的 vue-loader 编译,把 template 编译为 Render 函数,最终就会成为一个 JS 对象,自然可以对它进行扩展。这里的 alert 就是 Render 的 Alert 组件实例。在 newInstance 里,使用闭包暴露了两个方法 addremove。这里的 add 和 remove 可不是 alert.vue 里的 add 和 remove,它们只是名字一样。

4.入口
最后要做的,就是调用 notification.js 创建实例,并通过 add 把数据传递过去,这是组件开发的最后一步,也是最终的入口。在 src/component/alert 下创建文件 alert.js

import Notification from './notification.js';
let messageInstance;
function getMessageInstance() {
  messageInstance = messageInstance || Notification.newInstance(); // 确保一定能获取到alert实例
  return messageInstance;
}
function notice({ duration = 1.5, content = '', type = 'default' }) {
  let instance = getMessageInstance();
  // console.log(instance);
  instance.add({
    content: content,
    duration: duration,
    type: type
  });
}

// export default {
//   info(options) {
//     return notice(options);
//   }
// }
export default notice;

最后把 alert.js 作为插件注册到 Vue 里就行,在入口文件 src/main.js中,通过 prototype 给 Vue 添加一个实例方法:

// src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import Alert from '../src/components/alert/alert.js'

Vue.config.productionTip = false

Vue.prototype.$Alert = Alert

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

这样在项目任何地方,都可以通过 this.$Alert 来调用 Alert 组件了,我们创建一个 alert 的路由,并在 src/views 下创建页面 alert.vue

<!-- src/views/alert.vue -->
<template>
  <div>
    <button @click="handleOpen1">打开提示 1</button>
    <button @click="handleOpen2">打开提示 2</button>
  </div>
</template>
<script>
  export default {
    methods: {
      handleOpen1 () {
        this.$Alert({
          content: '我是提示信息 1'
        });
      },
      handleOpen2 () {
        this.$Alert({
          content: '我是提示信息 2',
          duration: 3,
          type:"success"
        });
      }
    }
  }
</script>

5.颜色样式

$primary-color:#5cadff;
$success-color:#19be6b;
$warn-color:#ff9900;
$info-color:#2db7f5;
$error-color:#ed4014;
$font-color:#333;

Logo

前往低代码交流专区

更多推荐