Message在开发中的使用频率很高,也算是Element-UI组件库中比较简单的,对于感兴趣的朋友可以一起探讨一下Message组件的实现

组件功能是什么

常用于主动操作后的反馈提示,比如提交表单时成功或者失败的提示信息展示

组件使用方法

this.$message('这是一条消息提示');this.$message({message: '恭喜你,这是一条成功消息',type: 'success'
});

组件设计思路

Message的调用方式都是通过this.$message进行调用,通过传递不同的options进行组件样式和内容的控制,展示的html是动态的插入到document中并在duration之后移除,组件的展示通过vue实例访问并控制。

组件的整体结构分为展示部分和控制部分

  • 展示部分单独抽离出一个组件,将组件的展示逻辑和交互封装集中处理
  • 控制部分是承接vue实例和组件展示

整体执行过程

Vue项目中

// 1.引入组件库
import ElementUI from 'element-ui';// 2.使用组件库
Vue.use(ElementUI);

Element-UI组件库中逻辑:每次当Vue.use的时候,在Element—UI内部会触发Element-UIinstall方法,然后将组件注册为全局组件,将方法放到Vue.prototype上,本次只看Message部分即可

// 1.引入Message对象
import Message from '../packages/message/index.js';// 2. 定义 install函数,
const install = function(Vue, opts = {}) {// 将组件遍历注册为全局组件,例如Button组件components.forEach(component => {Vue.component(component.name, component);});// 将方法放到Vue原型上Vue.prototype.$message = Message;
};

经过上述两步的处理,边可以直接在项目中通过this.$message进行组件的展示控制,接下来继续探索Element-UI内部如何处理的

展示部分

首先看一下删减版本之后展示部分的组件内容,代码将部分逻辑展示删除,紧紧展示了基本功能,显示icon等功能可自行查看,比较简单

<template><transition name="el-message-fade" @after-leave="handleAfterLeave"><divclass="el-message":style="positionStyle"v-show="visible"><slot><p>{{ message }}</p></slot></div></transition>
</template><script type="text/javascript">
export default {data() {return {visible: false,message: '',duration: 3000,onClose: null,closed: false,verticalOffset: 20,timer: null};},computed: {positionStyle() { // 控制当前组件的显示位置return {'top': `${ this.verticalOffset }px`};}},watch: {// 监听closed的变化,设置为true时,将组件销毁closed(newVal) {if (newVal) {this.visible = false;}}},methods: {// transtion组件的钩子,触发after-leave时执行handleAfterLeave() {this.$destroy(true); // 销毁组件this.$el.parentNode.removeChild(this.$el); // 将组件的DOM移除},close() {this.closed = true; // 组件隐藏if (typeof this.onClose === 'function') {this.onClose(this);}},// 每次手动启动编译之后 设置其展示时间duration之后关闭startTimer() {if (this.duration > 0) {this.timer = setTimeout(() => {if (!this.closed) {this.close();}}, this.duration);}}},mounted() {this.startTimer();}
};
</script><style>
.el-message{min-width: 280px;height: 42px;box-sizing: border-box;border-radius: 4px;border: 1px solid #ebeef5;position: fixed;left: 50%;top: 20px;transform: translateX(-50%);background-color: #edf2fc;transition: opacity .3s,transform .4s,top .4s;overflow: hidden;padding: 15px 15px 15px 20px;display: flex;align-items: center;background-color: #f0f9eb;border-color: #e1f3d8;
}
</style>

template部分

使用了Vue官方封装的transition组件,不仅提供了良好的过渡效果,还提供了合适的钩子便于开发者控制,组件中使用after-leave钩子,当组件销毁时进行组件的销毁和DOM的移除,visible用于控制组件的展示与销毁,计算属性positionStyle用于设置组件的展示位置,message为组件展示的内容数据,搞明白这些变量、计算属性和方法的作用便可以

script部分可参考注释进行理解,需要注意两个地方

首先需要注意生命周期钩子mount时做的事情,为何如此做?因为不存在el选项,实例不会立即进入编译阶段,需要显示调用$mount 手动开启编译

还需要注意的时close函数中做了两件事,设置closed的值触发对应的watch,关闭组件,若是存在onClose方法则调用,注意这个onClose函数的定义是在控制部分定义,稍后会说明

控制部分

至此已经清楚Vue中是通过this.$message触发组件的展示,而展示部分的组件内容也已完成,现在就需要通过控制部分将两者链接,达到期望的功能

Vue关联比较简单,仅仅是定义一个方法并将其导出即可

const Message = options => {// 逻辑编写....
}export default Message;

将其引入并绑到Vue原型上,省略无关代码

// 引入Message对象
import Message from '../packages/message/index.js';// 将方法放到Vue原型上
Vue.prototype.$message = Message;

这个时候通过this.$message即可调用,接下来便是将Message函数与组件关联,并控制展示部分

Message核心需要做那些事情

  • 编译组件,使用渲染并插入到body

  • 控制组件内的visible变量,触发组件的展示

  • 控制组件内的verticalOffset变量,决定组件展示时的位置

手动开启组件编译,获取其实例访问内部data和渲染到页面上

// 1. 使用基础 Vue 构造器,创建一个“子类”
let MessageConstructor = Vue.extend(Main);
// 2. 组件实例, 可以通过instance访问 visible和verticalOffset
instance = new MessageConstructor({data: options
});

整个Message方法其余部分就是在做容错和健壮处理,整体简洁版代码如下

let MessageConstructor = Vue.extend(Main);let instance; // 当前组件
let instances = []; // 将所有的组件收集,用于位置的判断和销毁等
let seed = 1;const Message = options => {// 健壮性处理if (Vue.prototype.$isServer) return;options = options || {message: 'content' + Date.now(),onClose(message){console.log('关闭时的回调函数, 参数为被关闭的 message 实例',message);}};if (typeof options === 'string') {options = {message: options};}// 关闭时的回调函数, 参数为被关闭的 message 实例let userOnClose = options.onClose;let id = 'message_' + seed++;// 增加 onClose 方法,组件销毁时,在组件内部调用options.onClose = function() { Message.close(id, userOnClose);};// 组件实例instance = new MessageConstructor({data: options});instance.id = id; // 设置IDinstance.$mount(); // 因为不存在el选项,实例不会立即进入编译阶段,需要显示调用$mount 手动开启编译document.body.appendChild(instance.$el); // 将Message 组件插入到body中// 设置组件距离顶部的距离let verticalOffset = options.offset || 20;instances.forEach(item => {verticalOffset += item.$el.offsetHeight + 16;});instance.verticalOffset = verticalOffset;instance.visible = true; // 控制展示instance.$el.style.zIndex = 99; // 控制层级instances.push(instance);return instance;
};

Message组件支持this.$message.error('错了哦,这是一条错误消息');调用使用,到目前为止还不支持,代码比较简单直接上代码

// 为每个 type 定义了各自的方法,如 Message.success(options),可以直接调用
['success', 'warning', 'info', 'error'].forEach(type => {Message[type] = options => {if (typeof options === 'string') {options = {message: options};}options.type = type;return Message(options);};
});

展示组件内部会调用this.onClose(this),组件内部设置this.visible=false关闭弹框,并且移除其对应的DOM结构,但是页面展示多个组件时需要修改其余组件的位置

onClose函数是在Message函数中定义

// 关闭时的回调函数, 参数为被关闭的 message 实例
let userOnClose = options.onClose;// 增加 onClose 方法,组件销毁时,在组件内部调用
options.onClose = function() { Message.close(id, userOnClose);
};

onClose函数最终调用的是Message上的静态方法close

函数Message.close内部主要做了几件事情

  • 在页面显示的组件数组中找到需要关闭的组件,将其移除
  • 重新计算剩余组件的位置
Message.close = function(id, userOnClose) {let len = instances.length;let index = -1;for (let i = 0; i < len; i++) {// 若是匹配到 则介绍此for循环if (id === instances[i].id) {index = i;// 执行初始化组件时传入的onClose回调函数, 参数为被关闭的 message 实例if (typeof userOnClose === 'function') { userOnClose(instances[i]);}instances.splice(i, 1);break;}}console.log(`每个close ${len} -- ${index}`);// 当只存在一个时组件展示或者没有匹配到需要销毁的组件时,不需要继续处理页面已有组件的展示位置if (len <= 1 || index === -1 || index > instances.length - 1) return;// 每一次销毁一个有效组件时,而页面还存在超过一个的组件,需要将页面展示的组件进行位置调整const removedHeight = instances[index].$el.offsetHeight;for (let i = index; i < len - 1 ; i++) {let dom = instances[i].$el;dom.style['top'] =parseInt(dom.style['top'], 10) - removedHeight - 16 + 'px';}
};

至此基础版本的Message已经完成,组件的代码不到200行,通过源码的简单阅读和分析,知识点并不是很多,但是优秀组件封装的思路还是值得学习和借鉴

element-ui:message组件实现相关推荐

  1. Element UI——滚动条组件(ElScrollBar)修改.el-scrollbar__wrap和el-scrollbar__view的CSS属性

    基本概念 el-scrollbar:Element UI隐藏组件. 注意事项: 1.el-scrollbar的父层要有固定高度 2.el-scrollbar的高度要设成100% 3.如果出现横滚动条, ...

  2. element ui table组件扩展关于列表编辑按钮的位置放置

    最近在用vue做项目,主要是用的element ui的组件,在用的过程中发现有部分组件需要扩展,改源码太折腾,成本高,就想着如何节省成本来实现这些需求,由于项目时间紧张,有些实现来也没来得及记录一下, ...

  3. 实战 | Element UI 父子组件传值与事件绑定(逆向)

    这是小小本周的第四篇,本篇将会倒过来讲解Element UI 父子组件传值与事件绑定. 父子组件传值 新建父组件和子组件 新建父组件 代码如下 <template><div id=& ...

  4. 实战 | Element UI 父子组件传值与事件绑定(正向)

    这是小小的本周的第三篇,本篇将会讲解关于Element UI 父子组件传值与事件绑定. 父子组件传值 新建父组件和子组件 新建父组件 代码如下 <template><div id=& ...

  5. 响应式滚动图懒加载 element ui el-carousel 组件优化代码

    响应式滚动图懒加载 element ui  el-carousel 组件优化代码 懒加载插件vue-lazyload //main.js import VueLazyload from 'vue-la ...

  6. element ui table组件筛选数据

    需求:element ui table组件筛选全部数据 element ui 文档 注意: 1. @filter-change 要写在table根元素,也就是<el-table @filter- ...

  7. element走马灯自动_vue Element UI走马灯组件重写

    1.element ui走马灯组件 -- carousel 分析一波源代码: carousel/src/main.vue 文件为 el-carousel文件主要功能 carousel/src/item ...

  8. element走马灯自动_[转]vue Element UI走马灯组件重写

    1.element ui走马灯组件 -- carousel 分析一波源代码: carousel/src/main.vue 文件为 el-carousel文件主要功能 carousel/src/item ...

  9. element ui upload组件文件上传一次 后,不论是上传成功之后修改文件再上传还是上传失败重新上传,再次点击上传均无反应。

    问题: Element UI Upload 组件文件上传一次 后,不论是上传成功之后修改文件再上传还是上传失败重新上传,再次点击上传均无反应. 原因: 第一次上传文件后,浏览器还保存着我们已经上传的文 ...

  10. Vue Element UI 表格组件 利用插槽实现按下按钮后获得本行数据(内容)

    Vue Element UI 表格组件 利用插槽实现按下按钮后获得本行数据(内容) 能够解决的问题 需要在表格中添加一个类似修改或编辑的按钮,按下按钮,弹出的窗口需要本行的渲染数据 需要向服务端提交一 ...

最新文章

  1. js原型和原型链_重学js --原型与原型链
  2. 网站收录上不去估计是这几个方面出了问题
  3. iis布置asp.net网站——服务应用程序不可用
  4. centos组件显示乱码,centos组件显示乱码_解决CentOS下中文显示乱码
  5. Tomcat启动问题,启动是Tomcat8,结果却是Tomcat9
  6. 文本挖掘(part5)--文本信息的分布式表示
  7. android多点触控自由对图片缩放
  8. [书目20081126]转型:用对策略,做对事
  9. python 编码解码原理_Python的编码解码问题
  10. 数学表达式3+(a+b)2对应的python表达式是_与数学表达式cd/2ab对应的Python表达式中,不正确的是:...
  11. 微信发红包的测试用例
  12. 学习Globle和window对象学习心得+jquery中的$(#id)与document.getElementById(id)的区别+成功激活win10的方法
  13. python 隐藏excel的列和行
  14. 既往不恋,当下不杂,未来不迎
  15. 四六级阅读难点--选词填空题的一般解题思路
  16. favicon.ico图标在线制作+ico下载
  17. 我想用人工智能留住父亲
  18. python中base64库用法详解
  19. 关于卷积神经网络细节的深入理解
  20. 【设计模式7】代理模式

热门文章

  1. C语言 一维数组比二维运算快吗,二维数组与一维数组的性能
  2. 小程序中where条件查询
  3. 【javaScript】Object.prototype.toString.call() 、 instanceof 以及 Array.isArray() 区别与优化层面的比较
  4. ASP.NET C# 用后台代码实现 跨域名访问 返回HTML代码
  5. xml java jaxb_【Java】JAXB操作XML用法详解
  6. 大数据天才马晓东,发明了健康码行程码
  7. Android 音乐频谱实现
  8. Selenium实战脚本集(4)--简单的开发者头条客户端
  9. pushlet java_Pushlet实现后台信息推送(一)
  10. 看完这篇文章,过年抢车票再也不是问题!