手把手教你擼個vue2.0彈窗組件

手把手教你擼個vue2.0彈窗組件

在開始以前須要瞭解一下開發vue插件的前置知識,推薦先看一下vue官網的插件介紹

預覽地址 http://haogewudi.me/kiko/inde...javascript

源碼地址 https://github.com/rascalHao/...css

搭建項目

  1. vue-cli將你的vue項目初始化建好 vue init webpack my-project
  2. 日常咱們引入插件的流程是:html

    npm i <package> -S
    
    import Vue from 'vue'
    import xxx from 'xxx'
    Vue.use(xxx)

因此能夠在node_modules下面新建一個你的開發目錄,我這裏命名爲kiko,
因此如今大概引入咱們的開發插件的步驟爲(項目最終構建完會採起發佈npm包的形式)vue

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官網的插件介紹java

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組件,經過node

Vue.prototype.$kiko_message = function (methodOptions) {webpack

// 邏輯...

}
來添加實例方法全局以調用this.$kiko_message()的方式來調用messagegit

  • 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上。github

toolTip

沒有選擇經過固化在頁面中的方式來引入toolTip,由於考慮到可能在頁面中的任何地方引入toolTip,若是固化了,將會大大限制toolTip的使用場景。因此採用了一個綁定到Vue.prototype的this.$kiko_tooltip全局方法來觸發,這樣就能夠自定義觸發方式,只須要經過傳入$event就能夠自動地定位任何有須要的元素了。web

  • 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'
Vue.use(Kiko)
`
引入你的插件啦

因爲本人學識有限,有不少須要提高的地方,望你們多多指教。

相關文章
相關標籤/搜索