实现可通过命令式调用的组件

3次阅读

共计 2611 个字符,预计需要花费 7 分钟才能阅读完成。

前端时间项目需要个预览功能 我的设计是给预览按钮添加自定义指令然后赋予预览功能 自定义指令就没什么可说的了 但是要在自定义指令里去调用预览组件就难到我了 当然最后也是实现了的 但是代码就丑陋了些 这里也就不现眼了 然后今天有看到大佬发的文章 代码也是美妙的狠 所以就爬下来 原文在最下面有连接

下面实现一个简易的 message 弹窗组件
Message/index.vue

<template>
  <div class="wrap">
    <template v-for="item in notices">
      <transition name="fade" :key="item._name">
        <div class="message" :class="item.type">
          <div class="content">{{item.content}}</div>
        </div>
      </transition>
    </template>
  </div>
</template>
<script>
// 默认选项
const DefaultOptions = {
  duration: 1500,
  type: "info",
  content: "这是一条提示信息!"
};
let mid = 0;
export default {data() {
    return {notices: []
    };
  },
  methods: {add(notice = {}) {
      // name 标识 用于移除弹窗
      let _name = this.getName();
      // 合并选项
      notice = Object.assign(
        {_name},
        DefaultOptions,
        notice
      );

      this.notices.push(notice);

      setTimeout(() => {this.removeNotice(_name);
      }, notice.duration);
    },
    getName() {return "msg_" + mid++;},
    removeNotice(_name) {let index = this.notices.findIndex(item => item._name === _name);
      this.notices.splice(index, 1);
    }
  }
};
</script>
<style lang="scss" scoped>
.wrap {
  position: fixed;
  top: 50px;
  left: 50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  transform: translateX(-50%);
}

.message {
  --borderWidth: 3px;
  min-width: 240px;
  max-width: 500px;
  margin-bottom: 10px;
  border-radius: 3px;
  box-shadow: 0 0 8px #ddd;
  overflow: hidden;
}

.content {
  padding: 8px;
  line-height: 1.3;
}

.message.info {border-left: var(--borderWidth) solid #909399;
  background: #f4f4f5;
}

.message.success {border-left: var(--borderWidth) solid #67c23a;
  background: #f0f9eb;
}

.message.error {border-left: var(--borderWidth) solid #f56c6c;
  background: #fef0f0;
}

.message.warning {border-left: var(--borderWidth) solid #e6a23c;
  background: #fdf6ec;
}

.fade-enter-active,
.fade-leave-active {transition: opacity 0.5s;}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {opacity: 0;}
</style>

Message/index.js

import Vue from 'vue'
import Index from './index.vue'

let messageInstance = null
let MessageConstructor = Vue.extend(Index) // 构造子类

let init = () => {messageInstance = new MessageConstructor() // 实例化组件
    // $mount 可以传入选择器字符串,表示挂载到该选择器
    // 如果不传入选择器,将渲染为文档之外的的元素,你可以想象成 document.createElement()在内存中生成 dom
    messageInstance.$mount() 
    // messageInstance.$el 获取的是 dom 元素
    document.body.appendChild(messageInstance.$el)
}

/**
 * 单例且惰性
 */
export let caller = (options) => {if (!messageInstance) {init(options)
    }
    messageInstance.add(options)
}

export default {
    // 返回 install 函数 用于 Vue.use 注册
    install(vue) {vue.prototype.$message = caller}
}

main.js

import Message from '@/components/Message/index.js'
Vue.use(Message)

两种使用使用方式

      /**
       * 在.js 文件里这样用
       */
      import {caller} from "@/components/Message/index.js";
      caller({
        type: "success",
        content: "成功信息提示",
        duration: 3000
      });
      
      /**
       * 在.vue 文件里这样用
       */
      this.$message({
        type: "success",
        content: "成功信息提示",
        duration: 3000
      });

细节主要集中在 Message/index.js 这个文件里
主要是通过 Vue.extend 这个全局是 api 和这个 vm.$mount实例的 api 去实现的
原文 10 个 Vue 开发技巧助力成为更好的工程师

正文完
 0