手把手教你撸个vue2.0弹窗组件

  • 内容
  • 相关

在开始之前需要了解一下开发vue插件的前置知识,推荐先看一下vue官网的插件介绍(https://cn.vuejs.org/v2/guide/plugins.html)

预览地址:http://haogewudi.me/kiko/index.html

源码地址:https://github.com/rascalHao/kiko

搭建项目

1.vue-cli将你的vue项目初始化建好 vue init webpack my-project

2.平常我们引入插件的流程是:

 npm i <package> -S

   import Vue from 'vue'
   import xxx from 'xxx'
   Vue.use(xxx)

所以可以在node_modules下面新建一个你的开发目录,我这里命名为kiko,所以现在大概引入我们的开发插件的步骤为(项目最终构建完会采取发布npm包的形式)

   import Vue from 'vue'
   import Kiko from '../node_modules/kiko/index.js'
   Vue.use(Kiko)

3.在你的项目目录下通过npm init指令来初始化一个package.json文件,默认指定你的入口文件index.js,并在你的项目根目录下新建一个index.js入口文件

4.这里会构建4中类型的弹窗组件(message、toolTip、confirm、loading),基本的结构如图所示:

入口文件(可以先略过)

Vue.js 的插件应当有一个公开方法 install 。这个方法的第一个参数是 Vue 构造器 , 第二个参数是一个可选的选项对象;通过全局方法 Vue.use() 使用插件;可以再次看下vue官网的插件介绍

   import KikoMessage from './packages/message/index.js'
   import KikoToolTip from './packages/tips/index.js'
   import KikoConfirm from './packages/confirm/index.js'
   import KikoLoading from './packages/loading/index.js'

   const install = function(Vue) {
     Vue.component(KikoMessage.name, KikoMessage)
     Vue.component(KikoToolTip.name, KikoToolTip)
     Vue.component(KikoConfirm.name, KikoConfirm)
     Vue.component(KikoLoading.name, KikoLoading)

     Vue.prototype.$kiko_tooltip = KikoToolTip.installToolTip
     Vue.prototype.$kiko_message = KikoMessage.installMessage
   }
   export default install

message

在项目的根目录创建message组件,通过

Vue.prototype.$kiko_message = function (methodOptions) {
   // 逻辑...
 }

来添加实例方法全局以调用this.$kiko_message()的方式来调用message

message组件结构

main.vue
   <template>
     <transition name="fade">
       <div class="kiko-message" v-if="isShow">
         {{message}}
       </div>
     </transition>
   </template>

   <script type="text/javascript">
     export default {
       name: 'kiko-message',
       data () {
         return {
           message: '',
           time: 3000,
           isShow: true
         }
       },
       mounted () {
         this.close()
       },
       methods: {
         close () {
           var that = this
           window.setTimeout(function() {
             that.isShow = false
           }, this.time);
         }
       }
     }
   </script>
index.js
   import Vue from 'vue'
   import Message from './src/main.vue'

   Message.installMessage = function(options) {
     if (options === undefined || options === null) {
       options = {
         message: ''
       }
     } else if (typeof options === 'string' || typeof options === 'number') {
       options = {
         message: options
       }
     }
     var message = Vue.extend(Message)

     var component = new message({
       data: options
     }).$mount()
     document.querySelector('body').appendChild(component.$el)
   }

   export default Message

到这里的时候可以看下前面的入口文件介绍,你需要通过Vue.component注册为组件,并把Message.installMessage方法绑定到Vue.prototype.$kiko_message上。

toolTip

没有选择通过固化在页面中的方式来引入toolTip,因为考虑到可能在页面中的任何地方引入toolTip,如果固化了,将会大大限制toolTip的使用场景。所以采用了一个绑定到Vue.prototype的this.$kiko_tooltip全局方法来触发,这样就可以自定义触发方式,只需要通过传入$event就可以自动地定位任何有需要的元素了。

toolTip组件结构

同message组件结构

main.vue
 <template>
   <div v-if="isShow" id="kiko_tool_tip" class="kiko-tool-tip" :class="{'left': direction === 'left', 'right': direction === 'right', 'top': direction === 'top', 'bottom': direction === 'bottom'}" :style="{'background-color': background, 'color': color, 'top': top, 'left': left}">
     {{content}}
     <div class="arrow" :style="arrowStyleObject"></div>
   </div>
 </template>

 <script type="text/javascript">
   export default {
     name: 'kikoToolTip',
     data () {
       return {
         isShow: true,
         time: 3000,
         content: '',
         direction: 'right',
         background: 'red',
         color: '#fff',
         arrowStyleObject: ''
       }
     },
     beforeMount () {
       let node = document.querySelector('#kiko_tool_tip')
       if (node && node.parentNode) {
         node.parentNode.removeChild(node)
       }
     },
     computed: {
       top () {
         switch (this.direction) {
           case 'top':
             return (this.rect.top - 12) + 'px'
           case 'bottom':
             return (this.rect.top + 12) + 'px'
           case 'left':
             return (this.rect.top + this.rect.height / 2) + 'px'
           case 'right':
             return (this.rect.top + this.rect.height / 2) + 'px'
         }
       },
       left () {
         switch (this.direction) {
           case 'top':
             return (this.rect.left + this.rect.width / 2) + 'px'
           case 'bottom':
             return (this.rect.left + this.rect.width / 2) + 'px'
           case 'left':
             return (this.rect.left - 12) + 'px'
           case 'right':
             return (this.rect.left + this.rect.width + 12) + 'px'
         }
       }
     },
     mounted () {
       this.initColor()
       this.hidden()
     },
     methods: {
       initColor () {
         switch (this.direction.toLowerCase()) {
           case 'left':
             this.arrowStyleObject = {
               borderLeftColor: this.background
             }
             break;
           case 'right':
             this.arrowStyleObject = {
               borderRightColor: this.background
             }
             break;
           case 'top':
             this.arrowStyleObject = {
               borderTopColor: this.background
             }
             break;
           case 'bottom':
             this.arrowStyleObject = {
               borderBottomColor: this.background
             }
             break;
         }

       },
       hidden () {
         let that = this
         window.setTimeout(function(){
           that.isShow = false
         }, this.time)
       }
     }
   }
 </script>

 <style type="text/css">
   .kiko-tool-tip {
     display: block;
     position: absolute;
     position: fixed;
     background-color: #3695CC;
     padding: 10px 10px;
     border-radius: 5px;
     color: #fff;
     white-space: nowrap;
     z-index: 99999999
   }
   .kiko-tool-tip.left {
     transform: translate(-100%, -50%);
   }
   .kiko-tool-tip.right {
     transform: translate(0, -50%);
   }
   .kiko-tool-tip.top {
     transform: translate(-50%, -100%);
   }
   .kiko-tool-tip.bottom {
     transform: translate(-50%, 100%);
   }
   .kiko-tool-tip.right .arrow {
     display: inline-block;
     position: absolute;
     content: '';
     width: 0;
     height: 0;
     top: 50%;
     left: -10px;
     border-top: 10px solid transparent;
     border-right: 15px solid #3695CC;
     border-bottom: 10px solid transparent;
     transform: translate(0, -50%);
   }
   .kiko-tool-tip.left .arrow {
     display: inline-block;
     position: absolute;
     content: '';
     width: 0;
     height: 0;
     top: 50%;
     right: -10px;
     border-top: 10px solid transparent;
     border-left: 15px solid #3695CC;
     border-bottom: 10px solid transparent;
     transform: translate(0, -50%);
   }
   .kiko-tool-tip.top .arrow {
     display: inline-block;
     position: absolute;
     content: '';
     width: 0;
     height: 0;
     left: 50%;
     bottom: -10px;
     border-top: 15px solid #3695CC;
     border-left: 10px solid transparent;
     border-right: 10px solid transparent;
     transform: translate(-50%, 0);
   }
   .kiko-tool-tip.bottom .arrow {
     display: inline-block;
     position: absolute;
     content: '';
     width: 0;
     height: 0;
     left: 50%;
     top: -10px;
     border-bottom: 15px solid #3695CC;
     border-left: 10px solid transparent;
     border-right: 10px solid transparent;
     transform: translate(-50%, 0);
   }
 </style>
index.js
 import Vue from 'vue'
 import ToolTip from './src/main.vue'

 ToolTip.installToolTip = function(event, opt) {
   var options = opt

   var rect = {};
   ['top', 'left'].forEach(function(property) {
     var scroll = property === 'top' ? 'scrollTop' : 'scrollLeft'
     rect[property] = event.target.getBoundingClientRect()[property] +
       document.body[scroll] +
       document.documentElement[scroll]
   });
   ['height', 'width'].forEach(function(property) {
     rect[property] = event.target.getBoundingClientRect()[property]
   });
   options.rect = rect
   var toolTip = Vue.extend(ToolTip)

   var component = new toolTip({
     data: options
   }).$mount()
   event.target.appendChild(component.$el)
 }

 export default ToolTip

通过Element.getBoundingClientRect()方法获取元素的大小及其相对于视口的位置,之后对提示信息进行fixed定位。

confirm

confirm在保留页面的情况下会弹出一个对话框,适合一些场景更大的情况。可以用来进行一些复杂带校验的弹窗信息展示,也可以只用于简单信息的展示。可以通过title属性来显示任意标题,通过width属性来修改显示区域的宽度。

confirm组件结构

同message组件

main.vue
 <template>
   <transition name="bounce">
     <div class="kiko-confirm" v-if="visible">
       <div class="bg"></div>
       <div class="kiko-container" :style="{width: width}">
         <div class="header">
           {{title}}
           <i @click="close" class="icon-remove icon-large kiko-close-btn" v-if="closeVisible"></i>
         </div>
         <div class="content">
           <slot></slot>
         </div>
         <slot name="footer">
           <!-- <div class="kiko-footer" slot="footer">
             <a href="javscript:void(0)" class="kiko-btn make-sure">确定</a>
             <a href="javscript:void(0)" class="kiko-btn cancel">取消</a>
           </div> -->
         </slot>
       </div>
     </div>
   </transition>
 /template>

 <script type="text/javascript">
   import '../../../lib/icon/css/font-awesome.css'
   export default {
     name: 'kiko-confirm',
     props: {
       width: {
         type: String,
         default: '260px'
       },
       title: {
         type: String,
         default: '信息'
       },
       visible: {
         type: Boolean,
         default: false
       },
       closeVisible: {
         type: Boolean,
         default: true
       }
     },
     data () {
       return {
       }
     },
     methods: {
       close () {
         this.$emit('update:visible', false)
       }
     }
   }
 </script>
index.js
 import Vue from 'vue'
 import Confirm from './src/main.vue'

 export default Confirm

这里通过组件的方式进行引入,可以只是简单地信息提示,也可以自己进行一些复杂的校验。对组件的显示与隐藏这里引用了.sync修饰符,也可以通过v-if指令。运用slot来分发内容。

loading

考虑到可能不需要整屏渲染,只需要局部加载loading,在指定的位置可以按需通过自定义指令来实现,通过Element.getBoundingClientRect()方法根据需要的元素位置、区域大小自动定位;若想整屏渲染,则需要加个.fullscreen修饰符。

loading组件结构

同message组件

main.vue
 <template>
   <div class="kiko-loading" :style="{'top': top, 'left': left, 'width': width, 'height': height}">
     <div class="bg"></div>
     <div class="kiko-container">
       <i class="icon-spinner icon-spin icon-4x"></i>
     </div>
   </div>
 </template>

 <script type="text/javascript">
   export default {
     name: 'kiko-loading',
     data () {
       return {
         top: 0,
         left: 0,
         width: '100%',
         height: '100%'
       }
     }
   }
 </script>

index.js

 import Vue from 'vue'
 import Loading from './src/main.vue'

 const loadingConstructor = Vue.extend(Loading)

 Vue.directive('kiko-loading', {
   update: function(el, binding) {
     if (binding.oldValue != binding.value) {
       const options = {}
       options.fullScreen = binding.modifiers.fullscreen ? true : false;
       if (options.fullScreen) {
         options.top = 0
         options.left = 0
         options.width = '100%'
         options.height = '100%'
       } else {
         ['top', 'left'].forEach(function(property) {
           var scroll = property === 'top' ? 'scrollTop' : 'scrollLeft'
           options[property] = el.getBoundingClientRect()[property] +
             document.body[scroll] +
             document.documentElement[scroll] +
             'px'
         });
         ['height', 'width'].forEach(function(property) {
           options[property] = el.getBoundingClientRect()[property] + 'px'
         });
       }
       var component = new loadingConstructor({
         data: options
       }).$mount()
       var node = document.querySelector('.kiko-loading')
       if (node && node.parentNode) {
         node.parentNode.removeChild(node)
       }
       if (binding.value === true) {
         document.querySelector('body').appendChild(component.$el)
       } else {
         var node = document.querySelector('.kiko-loading')
         if (node && node.parentNode) {
           node.parentNode.removeChild(node)
         }
       }
     }
   }
 })

 export default Loading

npm 发包

  1. 确保你的项目的根目录的package.json文件已经建好

  2. 登录npm官网注册

  3. 在你的项目目录下登录npm login(之后按提示填写信息)

  4. 发包npm publish

如果执行npm publish出现错误,可能是你的包名已经被注册过,在npm 官网上搜索一下是否已被注册了。若发包成功,你就可以在npm官网上搜索到自己的包。

发包成功后,就可以通过

   import Vue from 'vue'
   // 我的npm包是kiko-rascalhao
   import Kiko from 'kiko-rascalhao'

引入你的插件啦。

由于本人学识有限,有很多需要提升的地方,望大家多多指教。

本文转载自:前端大学

< 六十二博客 >

本文标签:

版权声明: 若无特殊注明,本文皆为《 六十二丶 》原创,转载请保留文章出处。

本文链接: 手把手教你撸个vue2.0弹窗组件 》 - https://prm612.top/post-11.html

解压说明: 如本站部分资源使用时需输入解压密码,本站所有资源的解压密码均默认为: prm612

收录情况: 百度暂未收录

站长QQ: 597782047 点击这里给我发消息 本网站的资源部分来源于网络,如有侵权,请 联系站长 进行删除处理。

本站留言 - 投诉建议: Email:597782047@qq.com 链接失效?( 点击提交 )请说明失效链接地址+名称

本站网址: https://prm612.top/

发表评论

电子邮件地址不会被公开。 必填项已用*标注

未显示?请点击刷新

允许邮件通知

正在加载...

点击↑关闭 2019年,祝大家
“诸”事吉祥!
网站备案相关信息
域名
主办方名称
主办单位性质
主体备案号
ICP备案号
首页地址 https://prm612.top/ [备案查询]
网站名称
审核时间