解读element-ui-----this.$message()

前言

在平时的开发过程中,我们总是先写好一个组件,然后在需要的页面中用 import 引入即可,但如果是下面这种类型的组件呢👇

image

上面这种类型的浮层提示有一个很大的特点,就是使用频率特别高,几乎每个页面都会用到它,于是乎我们就要在每个页面中去引入该组件,并且在每个页面都得通过一个变量来控制它的显隐,这显然不是我们想要的🙅。。。那我们想要的是什么样呢🤔?用过一些 UI 框架的同学们应该知道有这样一种用法:

this.$message({
    duration: 3000,
    content: '这是一条消息提示'
});

没错,就是这么简单的一句话就万事大吉了(就是用 js 调用组件而已啦🧐)。那这种效果究竟是怎么实现的呢?今天就让我们来(手把手🤝 )一探究竟吧!

前置知识

不知道小伙伴们有没有用过 Vue.extend() 这个东东,反正我是很少碰过,印象不深,所以这里我们先来短暂了解一下 Vue.extend() 主要是用来干嘛的。先来个官方说明(不多的,坚持下):

image

没怎么看懂?😴没关系,不重要,你只要记住(加少许理解)以下用法即可:

// 导入以往的普通组件
import Main from './main.vue';
// 用 Vue.extend 创建组件的模板(构造函数)
let mainConstructor = Vue.extend(Main);
// 实例化组件
let instance = new mainConstructor();
// 挂载到相应的元素上
instance.$mount('#app');


不知道你看懂没有,上面的 Vue.extend(Main) 就是一个基于 main.vue 的组件模板(构造函数),instance 是实例化的组件,$mount() 是手动挂载的意思。其中 Vue.extend()$mount() 就是我们通过 js 调用、渲染并挂载组件的精髓所在,相当于早前的 createElementappendChild,有异曲同工之效。这个点需要我们好好熟悉一下,所以你可以先停下来屡屡思路🤔。
补充一下🤐:$mount() 里面如果没有参数,说明组件只是渲染了但还没有挂载到页面上,如果有正确的(元素)参数则直接挂载到元素下面。

写一个 message 组件

js 调用归调用,最原始的组件还是要有的,只是我们不通过 import 来引入到页面中而已。ok,我们就以最开始的那个 message图片来简单写一下这个 vue 组件(element-ui中的message 和 alert 也是一样的)。这里就直接上代码啦,毕竟它的结构简单到爆了,也不是本章节的重点:

<!-- message.vue -->
<template>
  <div class="echojoy-message" >
    <p>服务器错误,请稍后重试</p>
  </div>
</template>
<script>
export default {
  name: "EchojoyMessage",
  
  mounted() {
    setTimeout(() => {
        // 3s 后通过父级移除子元素的方式来移除该组件实例和 DOM 节点
        this.$destroy(true);
        this.$el.parentNode.removeChild(this.$el);
      }, 3000);
    
  },
};
</script>
<style lang="scss" scoped>
.echojoy-message {
  display: flex;
  align-items: center;
  justify-content: center;
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  color: #fff;
  z-index: 9999;
  background: transparent;
  > p {
    padding: 12px 22px;
    font-size: 18px;
    border-radius: 4px;
    background: rgba(17, 17, 17, 0.7);
   }
}
</style>

上面的内容想必大家应该都能看懂,所以这里就直接讲下面的重点了。

写一个 message.js

我们在 message.vue 的同级目录下新建一个 message.js 文件。我们先瞟一眼文件内容(也不多,已经是个最简版了)👇:

// message.js
import Vue from "vue"; // 引入 Vue 是因为要用到 Vue.extend() 这个方法
import message from "./message.vue"; // 引入刚才的 toast 组件


const Toast = function() {
  instance = new ToastConstructor().$mount(); // 渲染组件
  
};

let messageConstructor = Vue.extend(message); // 这个在前面的前置知识内容里面有讲到
let instance;

const Message = function (options = {}) {
    instance = new messageConstructor().$mount(); // 渲染组件
    document.body.appendChild(instance.$el); // 挂载到 body 下
};
export default Message;

上面的代码暴露了一个 Message 函数。为什么要暴露一个函数呢?原因很简单:你想想,我们最终是不是要根据 this.$Message () 来调用一个组件,说白了,通过 js 调用,本质就是调用一个 函数。也就是说 this.$Message () 就是执行了上面代码中导出的 export default Message,也就是执行了 Message 函数(const Message = function() {}),所以当我们调用 this.$Message() 的时候其实就是执行了 Message() 函数。而 Message() 函数只做了一件事情:就是通过手动挂载的方式把组件挂载到 body 下面。
补充一下🤐:一般来说我们常见的是 $mount("#app"),也就是把组件挂载到 #app 下面,<router-view /> 也包含在 #app 中,但是我们这种 Message提示是放在 body 下面的,也就是说它不受 #app<router-view /> 的管控,所以当我们切换页面(路由)的时候,这个 Message 组件是不会跟着立马消失的,这点要注意哦😯。
这里顺便给个组件的目录结构,如下图所示:

message.png

开始调用

调用方式很简单,首先我们在入口文件 main.js(和上面不是同一个😢) 里加上两行代码,这样我们就能在需要的地方直接用 js 调用它了,

import EchojoyMessage from './../packages/message/src/message.js'
Vue.prototype.$message = EchojoyMessage;

然后在页面中测试一下,就像下面这样子:


ALAERT.png

运行一下代码:


image

<figcaption></figcaption>

嗯,挺好,小有成就的 feel 👏👏👏。

支持可传参数

别急,我们好像还漏了点什么🤔。。。对了,现在还不支持传参呢,直接调用 this.$message() 就只能显示————服务器错误,请稍后重试(这下全都是后端的锅了😊)。但我们可是个有追求的前端,不能局限于此,所以现在让我们来尝试增加下两个可配置参数,这里拿 durationcontent 举个栗子🌰。
首先我们要修改 message.vue 组件里面的内容(其实没啥大变化),就像下面这样:

<!-- message.vue 可配置版 -->
<template>
   <div class="echojoy-message" >
    <p >
      {{content}} 
    </p>
  </div>
</template>

<script>
// 主要就改了 data
export default {
  name: "EchojoyMessage",
  data() {
    return {
      content: "",
      duration: 3000
    };
  },
  mounted() {
    setTimeout(() => {
      this.$destroy(true);
      this.$el.parentNode.removeChild(this.$el);
    }, this.duration);
  }
};
</script>

上面的代码应该算是浅显易懂了,接下来我们看下 message.js 里面改了啥:

// message.js
import Vue from "vue"; // 引入 Vue 是因为要用到 Vue.extend() 这个方法
import message from "./message.vue"; // 引入刚才的 toast 组件

let messageConstructor = Vue.extend(message); // 这个在前面的前置知识内容里面有讲到
let instance;

const Message = function (options = {}) {
  instance = new messageConstructor({
    data: options // 这里的 data 会传到 message.vue 组件中的 data 中,当然也可以写在 props 里
  }); // 渲染组件
  document.body.appendChild(instance.$mount().$el); // 挂载到 body 下
};
export default Message;

其实 message.js 也没多大变化,就是在函数里面加了个参数。要注意的是 new messageConstructor({ data: options }) 中的 data 就是 message.vue 组件中的 data,不是随随便便取的字段名,传入的 options 会和组件中的 data 合并(Vue 的功劳)。
em。。。是的,就这么简单,现在让我们继续来调用一下它:

<script>
export default {
  methods: {
    show(){
      this.$message.success({
        content: "哈哈哈哈,消失的贼快",
        duration: 0
      })
    }
  }
};
</script>

运行一下就可以看到:

image

<figcaption></figcaption>

当然,这还没完,我们继续添加个小功能点🙄。。。

支持 this.$message.error()

这里我们打算支持 this.$message.error()this.$message.success() 这两种方式,所以我们第一步还是要先去修改一下 message.vue 文件的内容(主要就是根据 type 值来修改组件的样式),就像下面这样:

<!--main.vue-->
<div class="echojoy-message" :class="type ? `echojoy-message--${type}` : ''">
    <p class="echojoy-message-content">
      {{content}} 
    </p>
  </div>
<script>
export default {
  ...
  data() {
    return {
      type: "",
      content: "",
      duration: 3000
    };
  },
  ...
};
</script>
<style lang="scss" scoped>
.echojoy-message{
  ...
  &--error p { background: rgba(255, 0, 0, 0.5); }
  &--success p { background: rgba(0, 255, 0, 0.5); }
}
</style>

其次,this.$message.error() 其实就等价于 Message.error(),所以我们现在的目的就是要给 Message函数扩充方法,也比较简单,就先看代码再解释吧:

// message.js
const Message= function(options = {}) {
 ...
};
// 以下就是在 Message函数中拓展 ["success", "error"] 这两个方法

["success", "error"].forEach(type => {
  Message[type] = options => {
    options.type = type;
    return Message(options);
  };
});

export default Message;

我们可以看到 Message.error()Message.success() 最终还是调用 Message(options) 这个函数,只不过在调用之前需要多做一步处理,就是将 ["success", "error"] 作为一个 type 参数给合并进 options 里面再传递,仅此而已😬。
那就试试效果吧:

<script>
export default {
  methods: {
    show() {
      this.$message({ content: "这是正常的" });
    },
    showError() {
      this.$message.error({ content: "竟然失败了" });
    },
    showSuccess() {
      this.$message.success({ content: "居然成功了" });
    }
  }
};
</script>
image

duration <= 0,message一直存在

  • 传入参数非number类型
    首先我们为了让组件可以在传入错误类型的duration是能正常使用,做了一个小小的兼容,判断变量类型是否为number,非number类型,控制台输出错误,并强行转化duration为数字1000.(处理办法相当粗暴了!!)
  • 传入参数为number类型移除该组件实例和 DOM 节点
    >0
    在duration的时间范围内,移除该组件实例和 DOM 节点
    <=0
    不做任何处理
mounted() {
   if (typeof this.duration !== 'number' ) {
     console.error('the duration must be a number!')
     this.duration = 1000
   }
   if (this.duration > 0) {
     setTimeout(() => {
       // 3s 后通过父级移除子元素的方式来移除该组件实例和 DOM 节点
       this.$destroy(true);
       this.$el.parentNode.removeChild(this.$el);
     }, this.duration);
   }
 },

手动关闭message

在dom结构上设置可以执行关闭的icon图标,添加事件

<i class="echojoy-icon-close echojoy-message-close" @click="close"></i>

在scripts中增加函数

methods:{
    close(){
      this.$destroy(true);
      this.$el.parentNode.removeChild(this.$el);
    }
  }

结束语

再见吧!

参考 连接:https://juejin.im/post/5ca20e426fb9a05e42555d1d

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 一:什么是闭包?闭包的用处? (1)闭包就是能够读取其他函数内部变量的函数。在本质上,闭包就 是将函数内部和函数外...
    xuguibin阅读 9,513评论 1 52
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,072评论 0 3
  • 主要还是自己看的,所有内容来自官方文档。 介绍 Vue.js 是什么 Vue (读音 /vjuː/,类似于 vie...
    Leonzai阅读 3,324评论 0 25
  • 深入响应式 追踪变化: 把普通js对象传给Vue实例的data选项,Vue将使用Object.defineProp...
    冥冥2017阅读 4,838评论 6 16
  • 张三已年近半百,一辈子在山区林场兢兢业业地工作,几乎没出过远门。快退休了,领导破天荒照顾张三到省城出一次差。 出省...
    田芯蕊阅读 362评论 0 6