前提回顾
在项目开发中我们经常使用的组件注册分为两种,一个是全局注册和另一个是局部注册,假设我们的业务场景是用户在浏览注册页面时,点击页面中的注册按钮后,前端根据用户的注册信息先做一次简单的验证,并根据验证弹出一个对应消息提示弹框
我们拿到这个需求后,便开始着手准备要通过局部注册消息弹框组件的方法来实现这个场景,在通过局部注册消息弹框组件的方法解决完这个需求后,自然是沾沾自喜,紧接着又迎来了一个需求,该需求是用户在点击该注册按钮时,点击几次就要出现几次这个消息弹框,你开始犯了难,并思考难道我要在页面中提前插入n个组件标签,不然我怎么知道用户要点击几次注册按钮?
在你还没有解决第二个需求的时候,又一个需求来了,第三个需求是不仅仅是注册页面需要用到这个消息弹框组件,在其他多个页面中也需要用到这个消息弹框组件。
基于上述的业务需求,我们可以通过vue.extend编程式的使用组件,从而实现功能性的动态的消息提示弹框
局部注册消息弹框组件
先通过局部注册的方法来实现消息弹框组件
效果图如下:
构造目录如下:
'src/main.js'文件的代码:
import Vue from 'vue' import App from './App.vue' //全局引入样式文件 import './assets/css.css'; Vue.config.productionTip = false new Vue({ render: h => h(App) }).$mount('#app')
'src/bus/bus.js'文件的代码:
import vue from 'vue'; var bus=new vue() export default bus;
'src/App.vue'文件的代码:
<template> <div id="app"> <button @click="handleShowMessage">点击出现弹框</button> <TMessage :offsetTop='50'></TMessage> <TMessage :offsetTop='100'></TMessage> <TMessage :offsetTop='150'></TMessage> <!-- 我是不是得在这里埋下几万个消息弹框组件??? --> </div> </template> <script> import TMessage from './components/TMessage/TMessage.vue'; import bus from './bus/bus'; export default { name:'app', data() { return { } }, components: { TMessage, }, methods: { handleShowMessage(){ //打印查看消息弹框的组件对象 console.log(TMessage); //点击按钮后出现消息弹框 bus.$emit('showMessage') } }, } </script> <style> #app { display: flex; justify-content: center; } #app button{ margin-top: 250px; } </style>
'src/components/TMessage/TMessage.vue'文件的代码:
<template> <transition name="message-fade"> <div :class="[ 'message', 'message-' + type, center " :style="{top: offset + 'px'}" v-if="!closed" > <p class="message-content">提示信息:{{message}}</p> <i class="icon icon-close"></i> </div> </transition> </template> <script> export default { name: 'TMessage', data() { return { message: '这是默认信息', //弹框的提示内容 type: 'success', //弹框的样式 success、warning、error center: true, //弹框是否居中显示 offset: 20, //弹框默认的偏移量 closed: true, //弹框默认隐藏 通过v-if="!closed"控制 duration: 1000, //弹框消失的时间 timer: null, //准备一个定时器 } }, mounted() { this.offset=this.offsetTop bus.$on('showMessage',()=>{ this.closed=false; this.timer = setTimeout(() => { //如果弹框是显示状态的话在duration后会变为隐藏状态 if (!this.closed) { this.close(); } }, this.duration); }) }, props:['offsetTop'], methods: { close() { this.closed = true; } } } </script>
写到这里,我们实现的效果为(动图如下):
'src/assets/css.css'文件的代码:
/* 样式重点解析: 1.'message' 2.'message-' + type: 2.1:message-success 2.2:message-warning 2.3:message-error 3.'is-center' //决定了弹框居中显示 4.'message-fade-enter' //4和5决定了弹框的的过渡效果 5.'message-fade-leave-active' 6. .message { top:20px; //决定了弹框的偏移量 } */ .message { min-width: 380px; -webkit-box-sizing: border-box; box-sizing: border-box; border-radius: 4px; border-width: 1px; border-style: solid; border-color: #EBEEF5; position: fixed; left: 50%; top: 20px; z-index: 999999999; 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 } .message.is-center { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center } .message p { margin: 0 } .message-info .message-content { color: #909399 } .message-success { background-color: #f0f9eb; border-color: #e1f3d8 } .message-success .message-content { color: #67C23A } .message-warning { background-color: #fdf6ec; border-color: #faecd8 } .message-warning .message-content { color: #E6A23C } .message-error { background-color: #fef0f0; border-color: #fde2e2 } .message-error .message-content { color: #F56C6C } .message-content { padding: 0; font-size: 14px; line-height: 1 } .message-content:focus { outline-width: 0 } .message .icon-close { position: absolute; top: 50%; right: 15px; -webkit-transform: translateY(-50%); transform: translateY(-50%); cursor: pointer; color: #C0C4CC; font-size: 16px } .message .icon-close:focus { outline-width: 0 } .message .icon-close:hover { color: #909399 } .message-fade-enter, .message-fade-leave-active { opacity: 0; transform: translate(-50%, -100%) }
编程式的使用组件
上方在通过局部注册消息弹框组件时体现的局限性:灵活性低、可复用性低、代码观感较差
紧接着我们就要使用vue.extend来实现消息提示弹框,做到编程式的使用组件
该构造目录为:
'src/App.vue'文件的代码:
<template> <div id="app"> <button @click="handleShowMessage">点击出现弹框</button> </div> </template> <script> import Message from './components/TMessage/TMessage.js'; export default { name:'app', data() { return { } }, methods: { handleShowMessage(){ /** * 每点击一次按钮就调用一次该工厂函数 * 每调用一次该工厂函数就创建一个弹框组件对象 */ return Message('我好帅啊我好帅啊我好帅啊') } }, } </script> <style> #app { display: flex; justify-content: center; } #app button{ margin-top: 250px; } </style>
'src/components/TMessage/TMessage.vue'文件的代码:
<template> <transition name="message-fade"> <div :class="[ 'message', 'message-' + type, center " :style="{top: offset + 'px'}" v-if="!closed" > <p class="message-content">提示信息:{{message}}</p> <i class="icon icon-close"></i> </div> </transition> </template> <script> export default { name: 'TMessage', data() { return { message: '这是默认信息', //弹框的提示内容 type: 'success', //弹框的样式 success、warning、error center: true, //弹框是否居中显示 offset: 20, //弹框默认的偏移量 closed: false, //弹框默认隐藏 通过v-if="!closed"控制 duration: 1000, //弹框消失的时间 timer: null, //准备一个定时器 } }, mounted() { /*为了方便演示先不让弹框消失 this.timer = setTimeout(() => { //在规定的this.duration后该消息弹框消失 if (!this.closed) { this.close(); } }, this.duration); */ }, methods: { close() { this.closed = true; } } } </script>
'src/components/TMessage/TMessage.js'文件的代码:
import Vue from 'vue'; import TMessage from "./TMessage.vue"; function Message(data) { data = data || {}; if (typeof data === 'string') { data = { message: data } } const TMessageClass = Vue.extend(TMessage); //得到的是一个组件对象VueComponent实例 //new TMessageClass接收的是一个包含组件选项的对象 覆盖 let instance = new TMessageClass({ data }); instance.$mount(); console.log(instance.$el,'现在才可以访问$el'); /* instance.$el的打印结果如下: <div class="message message-success is-center" style="top: 20px;"> <p class="message-content">提示信息:我好帅啊我好帅啊我好帅啊</p> <i class="icon icon-close"></i> </div> */ document.body.appendChild(instance.$el); } export default Message
写到这里,我们来看一下效果,如下图:
解决消息弹框覆盖问题
我们已经做到了每点击一次按钮就出现一个消息弹框组件,但是因为定位的问题出现了相互覆盖,所以得再接着去'TMessage.js'文件中去完善逻辑:
//file:'src/components/TMessage/TMessage.js' /* 解决方法: 通过维护一个队列来存储每一个消息弹框组件对象 在每一次生成消息弹框组件时都需要重新计算其top值 通过该队列来计算上一个消息弹框组件对象的top值 */ import Vue from 'vue'; import TMessage from "./TMessage.vue"; //装有instance消息弹框组件对象的容器 let instances = []; function Message(data) { data = data || {}; if (typeof data === 'string') { data = { message: data } } data.onClose = function() { console.log('onClose'); // 每消失一个消息弹框就会触发一个onClose //instance是消息弹框组件的实例对象即VueComponent console.log(instance,'instance'); //每消失一个就得把在instances容器中对应的该组件对象给删除掉 Message.close(instance); }; const TMessageClass = Vue.extend(TMessage); let instance = new TMessageClass({ data }); instance.$mount(); document.body.appendChild(instance.$el); //如果data数据中有设置偏移量则使用该偏移量 //否则使用默认的偏移量值20 let offset = data.offset || 20; //规定每一个消息弹框的间隔 //这里直接使用offset值做为间隔 let offsetTop = offset; /**思路如下: * let offsetTop=20; * [].forEach(()=>{offsetTop+=10}); * console.log(offsetTop) //还是20 * * [{a:'1'}].forEach(()=>{offsetTop+=10}); * console.log(offsetTop) //才是30 */ /*这里是在循环之后才去push 因为生成的第一个消息弹框是不需要计算offsetTop的 生成的第一个消息弹框直接使用offset值即可 */ //从第一个起instances里有值了(组件对象)以后再去循环计算offsetTop值 instances.forEach( item => { //根据上一个计算的offsetTop+自身的高度+规定的间隔 offsetTop += item.$el.offsetHeight + offset; }); //当前生成的消息弹框的高度为offsetTop //offsetTop是根据上一个生成的消息弹框的三个值计算得到的 //instances容器中第0个是不需要参与计算的即采用默认的offset值 instance.$el.style.top = offsetTop + 'px'; instances.push(instance); } Message.close = function(instance) { //每消失一个就得把在instances容器中对应的该组件对象给删除掉 instances = instances.filter( item => item !== instance ); }; export default Message
'src/components/TMessage/TMessage.vue'文件的代码:
//file:'src/components/TMessage/TMessage.vue' <template> <transition name="message-fade"> <div :class="[ 'message', 'message-' + type, center " :style="{top: offset + 'px'}" v-if="!closed" > <p class="message-content">提示信息:{{message}}</p> <i class="icon icon-close"></i> </div> </transition> </template> <script> export default { name: 'TMessage', data() { return { message: '这是默认信息', //弹框的提示内容 type: 'success', //弹框的样式 success、warning、error center: true, //弹框是否居中显示 offset: 20, //弹框默认的偏移量 closed: false, //弹框默认隐藏 通过v-if="!closed"控制 duration: 1000, //弹框消失的时间 timer: null, //准备一个定时器, onClose: null //扩充一个功能 弹框消失后触发 } }, mounted() { //在规定的this.duration后该消息弹框消失 //消息框消失后会触发this.close()函数方法 this.timer = setTimeout(() => { if (!this.closed) { this.close(); } }, this.duration); }, methods: { close() { this.closed = true; //如果该组件可以接收到this.onClose方法则调用该方法 //该方法是在该消息弹框消失的时候被触发 if (typeof this.onClose === 'function') { this.onClose(); } } } } </script>
写到这里,我们来看一下效果,如下图:
优化消息弹框消失的效果
我们可以进一步的优化消息弹框消失的效果,效果图如下:
'src/components/TMessage/TMessage.vue'文件的代码:
<template> <transition name="message-fade"> <div :class="[ 'message', 'message-' + type, center " :style="{top: offset + 'px'}" v-if="!closed" > <p class="message-content">提示信息:{{message}}</p> <i class="icon icon-close"></i> </div> </transition> </template> <script> export default { name: 'TMessage', data() { return { message: '这是默认信息', type: 'success', center: true, offset: 20, closed: false, duration: 1000, timer: null, onClose: null //扩充一个功能 弹框消失后触发 } }, mounted() { this.timer = setTimeout(() => { if (!this.closed) { this.close(); } }, this.duration); }, methods: { close() { this.closed = true; //当弹框消失时会调用this.onClose()该函数方法 if (typeof this.onClose === 'function') { this.onClose(); } } } } </script>
'src/components/TMessage/TMessage.js'文件的代码:
import Vue from 'vue'; import TMessage from "./TMessage.vue"; //装有instance的容器 let instances = []; function Message(data) { data = data || {}; if (typeof data === 'string') { data = { message: data } } const TMessageClass = Vue.extend(TMessage); let instance = new TMessageClass({ data }); instance.$mount(); document.body.appendChild(instance.$el); data.onClose = function() { console.log('onClose'); // 每消失一个弹框就会触发一个onClose方法 Message.close(instance); }; //如果data数据中有设置偏移量则使用该偏移量 //否则使用默认的偏移量20 let offset = data.offset || 20; //规定每一个消息弹框的间隔 let offsetTop = offset; instances.forEach( item => { //上一个实例对象的offsetTop+自身的高度+规定的间隔 offsetTop += item.$el.offsetHeight + offset; }); instance.$el.style.top = offsetTop + 'px'; instances.push(instance); } Message.close = function(instance) { /* 每次弹窗关闭都会调用一次这个函数 * 获取当前这个instance的高度 * 把这个instance后面的所有实例的top减去这个高度,再减去偏移 * */ let removeHeight = instance.$el.offsetHeight + instance.offset; //把传递进来的instance在容器instances中删除 let index = instances.findIndex( item => item === instance ); instances = instances.filter( item => item !== instance ); //对应的消息弹框消失后在该消息弹框后面的消息弹框会依次出现顶上来的效果 //原理是找到对应的消息弹框在instances容器中的下标位置 //通过循环改变对应的消息弹框后面的所有消息弹框的高度 for (let i = index; i<instances.length; i++) { instances[i].$el.style.top = parseFloat(instances[i].$el.style.top) - removeHeight + 'px'; } }; export default Message
'src/App.vue'文件的代码:
<template> <div id="app"> <button @click="handleShowMessage">点击出现弹框</button> </div> </template> <script> import Message from './components/TMessage/TMessage.js'; export default { name:'app', data() { return { } }, methods: { handleShowMessage(){ /** * 调用一次就创建一个弹框组件对象 */ return Message('我好帅啊我好帅啊我好帅啊') } }, } </script> <style> #app { display: flex; justify-content: center; } #app button{ margin-top: 250px; } </style>
终极版实现版
我们在上方'src/App.vue'文件中是通过引入TMessage.js后再通过Message()的方式调用使用该组件的,还可以将调用方式挂载到Vue全局上,来看看怎么操作:
'src/main.js'文件的代码
import Vue from 'vue' import App from './App.vue' import './assets/css.css'; import Message from '../src/components/TMessage/TMessage'; Vue.config.productionTip = false //挂载到全局 Vue.prototype.$message = Message; new Vue({ render: h => h(App) }).$mount('#app')
'src/App.vue'文件的代码
<template> <div id="app"> <button @click="handleShowMessage">点击出现弹框</button> </div> </template> <script> export default { name:'app', data() { return { } }, methods: { handleShowMessage(){ this.$message.error('我好帅啊我好帅啊我好帅啊') this.$message.success('我好帅啊我好帅啊我好帅啊') this.$message.info('我好帅啊我好帅啊我好帅啊') this.$message.warning('我好帅啊我好帅啊我好帅啊') } }, } </script> <style> #app { display: flex; justify-content: center; } #app button{ margin-top: 250px; } </style>
'src/components/TMessage/TMessage.vue'文件的代码:
<template> <transition name="message-fade"> <div :class="[ 'message', 'message-' + type, center " :style="{top: offset + 'px'}" v-if="!closed" > <p class="message-content">提示信息:{{message}}</p> <i class="icon icon-close"></i> </div> </transition> </template> <script> export default { name: 'TMessage', data() { return { message: '这是默认信息', type: 'success', center: true, offset: 20, closed: false, duration: 1000, timer: null, onClose: null //扩充一个功能 弹框消失后触发 } }, mounted() { this.timer = setTimeout(() => { if (!this.closed) { this.close(); } }, this.duration); }, methods: { close() { this.closed = true; //当弹框消失时会调用this.onClose()该函数方法 if (typeof this.onClose === 'function') { this.onClose(); } } } } </script>
'src/components/TMessage/TMessage.js'文件的代码:
import Vue from 'vue'; import TMessage from "./TMessage.vue"; let instances = []; function Message(data) { data = data || {}; if (typeof data === 'string') { data = { message: data } } data.onClose = function() { console.log('onClose'); //instance是消息弹框组件的实例对象即VueComponent Message.close(instance); }; const TMessageClass = Vue.extend(TMessage); let instance = new TMessageClass({ data }); instance.$mount(); // console.log(instance.$el,'现在才可以访问$el'); document.body.appendChild(instance.$el); let offset = data.offset || 20; //规定每一个消息弹框的间隔 let offsetTop = offset; //第一个弹框是不需要计算偏移量的 //从第一个以后再去循环 instances.forEach( item => { //上一个实例对象的offsetTop+自身的高度+规定的间隔 offsetTop += item.$el.offsetHeight + offset; }); instance.$el.style.top = offsetTop + 'px'; instances.push(instance); } Message.close = function(instance) { let removeHeight = instance.$el.offsetHeight + instance.offset; let index = instances.findIndex( item => item === instance ); instances = instances.filter( item => item !== instance ); for (let i = index; i<instances.length; i++) { instances[i].$el.style.top = parseFloat(instances[i].$el.style.top) - removeHeight + 'px'; } }; ['info', 'success', 'error', 'warning'].forEach( type => { Message[type] = function(data) { if (typeof data === 'string') { data = { message: data } } data.type = type; //整合data后再次去调用Message() return Message(data); }; } ); // Message.error=function(data){ // if (typeof data === 'string') { // data = { // message: data // } // } // return Message({ // ...data, // type:'error' // }) // } export default Message
完结撒花,最后来看一下效果图:
总结
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]