本集定位:
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組件.