本集定位:
toast组件的实现, 争取一章就说完, 更多弹出相关的东西会在alert组件里面.
下章loading组件.
很开心昨天去现场录制'脱口秀大会',心情愉悦😁.css
一: toast需求分析 vue
二: 基础结构的搭建 node
话很少少, 导出文件 Toast/index.js.
导出默认的引入项, 是能够以下简写的.ios
export { default } from './main/toast.js';
// 因为这个js文件内容比较重要, 咱们放在后面讲, 先写结构
toast.vue文件git
<template> // toast弹框没有动画的话就太寒掺了. // 我这边定义的是一个从上到下掉出来的效果. <transition name="cc-toast-fade"> // 命名规则仍是老样子, bem原则. <div class="cc-toast" // 上面提到的 z-index属性, 对于这种组件来讲, 用户必须可控. :style="'zIndex:'+zIndex"> // 用户能够随意插入内容 // 写成变量形式也能够, 这里用slot是为了见名知意. <slot /> // 下面的是关闭的 'X' icon, <i class="cc-toast__closeButton" @click="deleteEl" v-if="showButton"> <ccIcon name='cc-close'/> </i> </div> </transition> </template>
js部分github
import ccIcon from "@/components/Icon/main/icon.vue"; export default { name: "toast", components: { ccIcon }, props: { zIndex: { type: Number, default: 10 }, showButton: { // 是否要关闭的x type: Boolean, default: true }, } }
三: 本人z-index的一些见解
从z-index的数值设定上就能看出一我的的作事风格, 好比我以前工做时候带过的几我的, 我甚至看到有些人常常写 999999 , 我不让他这样写, 以后他还跟我反应说, 不这样写不习惯🤷♀️, 我说若是要写比他等级更高的怎么办? 他说: 确定不可能有那种状况.
其综合分析也就明白了, 喜欢这样去书写的人,
一是没有太长远的规划, 没有想到将来的多样性,
二是并无为团队配合而考虑, 没有为他人书写代码'留有余地'而只顾本身.编程
本人独立负责的实际项目中试验过, 1-10的z-index已经能够应付绝大多数工程了, 一个中小型项目分红10层真的算不少了, 这里面也有一些技巧, 好比说, 每次设置的时候要设置 1, 3, 5, 7, 9, 这是为了若是之后有特殊状况要放到某两个中间的, 能够留一个插入位, 随意约束好z-index是一个合格工程的开端.axios
四: scss基本样式的设定
vue-cc-ui/src/style/Toast.scss数组
@import './common/mixin.scss'; @import "./common/animation.scss"; @include b(toast) { position: fixed; display: inline-block; background-color: white; top: 6px; // 上方, 居中 left: 50%; padding: 6px 20px; transition: all .3s; transform: translateX(-50%); // 本套ui的精髓😏黑边. @include commonShadow($--color-black); // 定义了动画的样子 @at-root { @include commonType(cc-toast--); .#{$namespace}-toast--big { padding: 6px 35px 6px 20px; } .#{$namespace}-toast-fade-enter, .#{$namespace}-toast-fade-leave-active { opacity: 0; transform: translate(-50%, -100%); } }; }
五: 提示框的type颜色
提示框须要让用户可以很直观的分辨它的意义, 好比用户没有看到提示文字的时候, 但他看到了红色的提示框, 那他也会知道本身不该该进行刚才的操做.app
属性固然就是type了
<div class="cc-toast" :class="[toastType]" // 这里接收 :style="'zIndex:'+zIndex">
props: { type: { type: String, default: "nomal" },
计算属性里面咱们还要规定一些合法值, 以及用户传错了的时候的默认值.
computed: { toastType() { // 这里我作了 正常, 成功, 失败, 警告 let ary = ["nomal", "success", "warning", "danger"]; // 这里写三元也能够, 但不方便扩展, 万一之后要改那,,,, // 其实无所谓, 我只是不想写那种长长的代码段 if (ary.includes(this.type)) { return "cc-toast--" + this.type; } return "cc-toast--nomal"; } },
scss
@at-root { @include commonType(cc-toast--); };
以前写的util类, 循环加上彩色边框
@mixin commonType($name) { @each $type in (success, warning, danger) { .#{$name}#{$type} { @include commonShadow($type); } } }
效果展现
6: 渲染到页面上(说了这个才能引出如何关闭它)
vue-cc-ui/src/components/Toast/main/toast.js
这里我会写很详细的注释!!!
// 1: 把写好的组件引进来, 根据打包的规则, 其实引得已是处理好的js代码了 import toast from './toast.vue'; export default { // 2: 配合use方法 install(Vue) { // 3: 为了保证全局都能控制弹出toast, 咱们只能原型编程 // 这个options就是未来咱们调用他的时候, 传的参数. Vue.prototype.$ccToast = function(options) { // 4: 使用基础 Vue 构造器,建立一个“子类”。 let Constructor = Vue.extend(toast),node; // 5: 咱们传入了配置项 if (typeof options === 'object' && options instanceof Object) { // 6: 实例化这个组件 node = new Constructor({ // 7: 至关于你在定义data里面的数据 propsData: options }); // 8: 提取须要弹出的信息到默认插槽 // 这里为何要用数组我解释一下 // 由于这里咱们传入的是'结构' // 因此他可能多个, 也多是嵌套, // 因此vue 分析它必需要用数组的形式 // 也能够说他要的是 '子元素的集合'. // 这方面有兴趣能够去看render的实现. node.$slots.default = [options.message]; // 5: 咱们传入一串字符串 } else if (typeof options === 'string') { // 6: 直接实例化便可 node = new Constructor(); node.$slots.default = [options]; } // 9: 调用函数的生命周期 node.vm = node.$mount(); // 10: 让他显示出出来 node.vm.visible = true // 11: 无情插入 // 若是有特殊需求的同窗, 不想让他插入body, 而是经过传入来定也是不错的. document.body.appendChild(node.$el); }; } };
7: 清除组件, 也能够说是关闭
x 绑定click事件.
<i class="cc-toast__closeButton" @click="deleteEl" v-if="showButton"> <ccIcon name='cc-close'/> </i>
// 专门负责清理工做的函数 deleteEl() { // 先触发动画效果, 不要直接rm, 否则效果会不好. this.visible = false; setTimeout(() => { // 下这样才能清理干净一个组件👇 // 这里是去除元素 this.$el.remove(); // 彻底销毁一个实例。清理它与其它实例的链接,解绑它的所有指令及事件监听器。 this.$destroy(); }, 300); },
scss
// 这个'x'还须要调节一下样式, // 而且浪一下, 赋予hover时候疯狂旋转的动画, 充满朝气 .#{$namespace}-toast__closeButton { display: flex; cursor: pointer; position: absolute; align-items: center; justify-content: center; top: 0; right: 0; bottom: 0; padding: 0 5px; &:hover { animation: rotating .5s infinite linear; } }
用户设置了自动关闭, 过时时间,
props: { closeTime: { // 关闭的延时 type: Number, default: 2000 }, } // 初始化是否设置了时间的操做 initAutoClose() { // 万一用户传了个负数, 仍是替他abs一下吧. if (this.autoClose && Math.abs(this.closeTime) > 0) { // 这里要用变量接一下, 方便中止他 this.timer = setTimeout(() => { this.deleteEl(); }, Math.abs(this.closeTime)); } },
用户想详细看看提示内容, 把鼠标放在了toast上, 并不点击关闭
<div class="cc-toast" :class="[ toastType,{ 'cc-toast--big':showButton }]" :style="'zIndex:'+zIndex" // 移入进来就别关闭倒计时了 @mousemove="clearTimer" // 移出就重新走一遍倒计时初始化 @mouseleave="initAutoClose" v-show="visible">
clearTimer() { clearTimeout(this.timer); },
用户想点击键盘的esc键, 关闭这个组件
mounted() { this.initAutoClose(); // 开始就要添加对用户键盘事件的监听 document.addEventListener("keydown", this.keydown); }, keydown(e) { if (e.keyCode === 27) { this.deleteEl(); } }
// 既然有document的操做, 固然就要有去除的操做.
beforeDestroy() { // 万一用户写了个很长的时间, 虽然element没有对此进行处理, 但我仍是想写一下. this.clearTimer() document.removeEventListener('keydown', this.keydown); }
end 到这里为止, 一个健康的toast也就能够正常使用了
感谢您的阅读, 喜欢的话就收藏吧, 让咱们一块儿终生学习.
下章loading组件.