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


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

    在开始之前需要了解一下开发vue插件的前置知识,推荐先看一下vue官网的插件介绍

    预览地址 http://haogewudi.me/kiko/inde...

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

    搭建项目

    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)
    1. 在你的项目目录下通过npm init指令来初始化一个package.json文件,默认指定你的入口文件index.js,并在你的项目根目录下新建一个index.js入口文件
    2. 这里会构建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: '';
           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: '';
           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: '';
           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: '';
           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}">
              <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: {
             {
              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,
               '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'
    Vue.use(Kiko)
    `
    引入你的插件啦 
    

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

  • 相关阅读:
    论文阅读 | Adversarial Example Generation with Syntactically Controlled Paraphrase Networks
    Textual Entailment(自然语言推理-文本蕴含)
    论文阅读 | Probing Neural Network Understanding of Natural Language Arguments
    BiLSTM-CRF模型理解
    论文阅读 | Text Processing Like Humans Do: Visually Attacking and Shielding NLP Systems
    论文阅读 | Universal Adversarial Triggers for Attacking and Analyzing NLP
    QA 中的对抗攻击和防御
    2010 年 10 年前的网文【我的10年计算机之路】
    IntelliJ 的搜索和全局搜索怎么用
    Spring Data 的 Pivotal GemFire 参考指南前言
  • 原文地址:https://www.cnblogs.com/10manongit/p/12871093.html
Copyright © 2020-2023  润新知