中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

vue引用組件的幾種方式

vue
沐橙
1918
2021-04-20 16:09:51
欄目: 編程語言

vue引用組件有3種方式:1、通過v-model或者.sync顯式控制組件顯示隱藏;2、使用js代碼進行引用組件;3、使用Vue指令進行引用組件。

vue引用組件的幾種方式

具體分析如下:

Dialog

我習慣把這個東西叫做對話框,實際上還有叫做modal(彈窗)組件的 叫法。其實就是在頁面里,彈出一個小窗口,這個小窗口里的內容可以定制。通常可以用來做登錄功能的對話框。

這種組件就很適合通過v-model或者.sync的方式來顯式的控制出現和消失。它可以直接寫在頁面里,然后通過data去控制——這也是最符合Vue的設計思路的組件。

為此我們可以寫一個組件就叫做Dialog.vue

<template>

  <div class="dialog">

    <div class="dialog__wrapper" v-if="visble" @clcik="closeModal">

      <div class="dialog">

        <div class="dialog__header">

          <div class="dialog__title">{{ title }}</div>

        </div>

        <div class="dialog__body">

          <slot></slot>

        </div>

        <div class="dialog__footer">

          <slot name="footer"></slot>

        </div>

      </div>

    </div>

    <div class="modal" v-show="visible"></div>

  </div>

</template>

<script>

  export default {

    name: 'dialog',

    props: {

      title: String,

      visible: {

        type: Boolean,

        default: false

      }

    },

    methods: {

      close() {

        this.$emit('update:visible', false) // 傳遞關閉事件

      },

      closeModal(e) {

        if (this.visible) {

          document.querySelector('.dialog').contains(e.target) ? '' : this.close(); // 判斷點擊的落點在不在dialog對話框內,如果在對話框外就調用this.close()方法關閉對話框

        }

      }

    }

  }

</script>

CSS什么的就不寫了,跟組件本身關系比較小。不過值得注意的是,上面的dialog__wrapper這個class也是全屏的,透明的,主要用于獲取點擊事件并鎖定點擊的位置,通過DOM的Node.contains()方法來判斷點擊的位置是不是dialog本身,如果是點擊到了dialog外面,比如半透明的modal層那么就派發關閉事件,把dialog給關閉掉。

當我們在外部要調用的時候,就可以如下調用:

<template>

  <div class="xxx">

    <dialog :visible.sync="visible"></dialog> 

    <button @click="openDialog"></button>

  </div>

</template>

<script>

  import Dialog from 'Dialog'

  export default {

    components: {

      Dialog

    },

    data() {

      return {

        visible: false

      }

    },

    methods: {

      openDialog() {

        this.visible = true // 通過data顯式控制dialog

      }

    }

  }

</script>

為了Dialog開啟和關閉好看點,你可試著加上<transition></transition>組件配合上過渡效果,簡單的一點過渡動效也將會很好看。


Notice

這個組件類似于element-ui的 message(消息提示)。它吸引我的最大的地方在于,它不是通過顯式的在頁面里寫好組件的html結構通過v-model去調用的,而是通過在js里通過形如this.$message()這樣的方法調用的。這種方法雖然跟Vue的數據驅動的思想有所違背。不過不得不說在某些情況下真的特別方便。



對于Notice這種組件,一次只要提示幾個文字,給用戶簡單的消息提示就行了。提示的信息可能是多變的,甚至可以出現疊加的提示。如果通過第一種方式去調用,事先就得寫好html結構,這無疑是麻煩的做法,而且無法預知有多少消息提示框。而通過js的方法調用的話,只需要考慮不同情況調用的文字、類型不同就可以了。


而之前的做法都是寫一個Vue文件,然后通過components屬性引入頁面,顯式寫入標簽調用的。那么如何將組件通過js的方法去調用呢?


這里的關鍵是Vue的extend方法。


文檔里并沒有詳細給出extend能這么用,只是作為需要手動mount的一個Vue的組件構造器說明了一下而已。


通過查看element-ui的源碼,才算是理解了如何實現上述的功能。


首先依然是創建一個Notice.vue的文件


<template>

  <div class="notice">

    <div class="content">

      {{ content }}

    </div>

  </div>

</template>

<script>

  export default {

    name: 'notice',

    data () {

      return {

        visible: false,

        content: '',

        duration: 3000

      }

    },

    methods: {

      setTimer() {

        setTimeout(() => {

          this.close() // 3000ms之后調用關閉方法

        }, this.duration)

      },

      close() {

        this.visible = false

        setTimeout(() => {

          this.$destroy(true)

          this.$el.parentNode.removeChild(this.$el) // 從DOM里將這個組件移除

        }, 500)

      }

    },

    mounted() {

      this.setTimer() // 掛載的時候就開始計時,3000ms后消失

    }

  }

</script>



上面寫的東西跟普通的一個單文件Vue組件沒有什么太大的區別。不過區別就在于,沒有props了,那么是如何通過外部來控制這個組件的顯隱呢?


所以還需要一個js文件來接管這個組件,并調用extend方法。同目錄下可以創建一個index.js的文件。


import Vue from 'vue'

const NoticeConstructor = Vue.extend(require('./Notice.vue')) // 直接將Vue組件作為Vue.extend的參數

let nId = 1

const Notice = (content) => {

  let id = 'notice-' + nId++

  const NoticeInstance = new NoticeConstructor({

    data: {

      content: content

    }

  }) // 實例化一個帶有content內容的Notice

  NoticeInstance.id = id

  NoticeInstance.vm = NoticeInstance.$mount() // 掛載但是并未插入dom,是一個完整的Vue實例

  NoticeInstance.vm.visible = true

  NoticeInstance.dom = NoticeInstance.vm.$el

  document.body.appendChild(NoticeInstance.dom) // 將dom插入body

  NoticeInstance.dom.style.zIndex = nId + 1001 // 后插入的Notice組件z-index加一,保證能蓋在之前的上面

  return NoticeInstance.vm

}

export default {

  install: Vue => {

    Vue.prototype.$notice = Notice // 將Notice組件暴露出去,并掛載在Vue的prototype上

  }

}




這個文件里我們能看到通過NoticeConstructor我們能夠通過js的方式去控制一個組件的各種屬性。最后我們把它注冊進Vue的prototype上,這樣我們就可以在頁面內部使用形如this.$notice()方法了,可以方便調用這個組件來寫做出簡單的通知提示效果了。


當然別忘了這個相當于一個Vue的插件,所以需要去主js里調用一下Vue.use()方法:


// main.js

// ...

import Notice from 'notice/index.js'

Vue.use(Notice)

// ...


Loading

在看element-ui的時候,我也發現了一個很有意思的組件,是Loading,用于給一些需要加載數據等待的組件套上一層加載中的樣式的。這個loading的調用方式,最方便的就是通過v-loading這個指令,通過賦值的true/false來控制Loading層的顯隱。這樣的調用方法當然也是很方便的。而且可以選擇整個頁面Loading或者某個組件Loading。這樣的開發體驗自然是很好的。



其實跟Notice的思路差不多,不過因為涉及到directive,所以在邏輯上會相對復雜一點。


平時如果不涉及Vue的directive的開發,可能是不會接觸到modifiers、binding等概念。參考文檔


簡單說下,形如:v-loading.fullscreen="true"這句話,v-loading就是directive,fullscreen就是它的modifier,true就是binding的value值。所以,就是通過這樣簡單的一句話實現全屏的loading效果,并且當沒有fullscreen修飾符的時候就是對擁有該指令的元素進行loading效果。組件通過binding的value值來控制loading的開啟和關閉。(類似于v-model的效果)


其實loading也是一個實際的DOM節點,只不過要把它做成一個方便的指令還不是特別容易。


首先我們需要寫一下loading的Vue組件。新建一個Loading.vue文件


<template>

  <transition

    name="loading"

  @after-leave="handleAfterLeave">

    <div

      v-show="visible"

      class="loading-mask"

      :class={'fullscreen': fullscreen}>

      <div class="loading">

        ...

      </div>

      <div class="loading-text" v-if="text">

        {{ text }}

      </div>

    </div>

  </transition>

</template>

<script>

export default {

  name: 'loading',

  data () {

    return {

      visible: true,

      fullscreen: true,

      text: null

    }

  },

  methods: {

    handleAfterLeave() {

      this.$emit('after-leave');

    }

  }

}

</script>

<style>

.loading-mask{

  position: absolute; // 非全屏模式下,position是absolute

  z-index: 10000;

  background-color: rgba(255,235,215, .8);

  margin: 0;

  top: 0;

  right: 0;

  bottom: 0;

  left: 0;

  transition: opacity .3s;

}

.loading-mask.fullscreen{

  position: fixed; // 全屏模式下,position是fixed

}

// ...

</style>



Loading關鍵是實現兩個效果:

    1.全屏loading,此時可以通過插入body下,然后將Loading的position改為fixed,插入body實現。

    2.對所在的元素進行loading,此時需要對當前這個元素的的position修改:如果不是absolute的話,就將其修改為relatvie,并插入當前元素下。此時Loading的position就會相對于當前元素進行絕對定位了。

所以在當前目錄下創建一個index.js的文件,用來聲明我們的directive的邏輯。


import Vue from 'vue'

const LoadingConstructor = Vue.extend(require('./Loading.vue'))

export default {

  install: Vue => {

    Vue.directive('loading', { // 指令的關鍵

      bind: (el, binding) => {

        const loading = new LoadingConstructor({ // 實例化一個loading

          el: document.createElement('div'),

          data: {

            text: el.getAttribute('loading-text'), // 通過loading-text屬性獲取loading的文字

            fullscreen: !!binding.modifiers.fullscreen 

          }

        })

        el.instance = loading; // el.instance是個Vue實例

        el.loading = loading.$el; // el.loading的DOM元素是loading.$el

        el.loadingStyle = {};

        toggleLoading(el, binding);

      },

      update: (el, binding) => {

        el.instance.setText(el.getAttribute('loading-text'))

        if(binding.oldValue !== binding.value) {

          toggleLoading(el, binding)

        }   

      },

      unbind: (el, binding) => { // 解綁

        if(el.domInserted) {

          if(binding.modifiers.fullscreen) {

              document.body.removeChild(el.loading);

          }else {

            el.loading &&

            el.loading.parentNode &&

            el.loading.parentNode.removeChild(el.loading);

          }

        }

      }

    })

    const toggleLoading = (el, binding) => { // 用于控制Loading的出現與消失

      if(binding.value) { 

        Vue.nextTick(() => {

          if (binding.modifiers.fullscreen) { // 如果是全屏

            el.originalPosition = document.body.style.position;

            el.originalOverflow = document.body.style.overflow;

            insertDom(document.body, el, binding); // 插入dom

          } else {

            el.originalPosition = el.style.position;

            insertDom(el, el, binding); // 如果非全屏,插入元素自身

          }

        })

      } else {

        if (el.domVisible) {

          el.instance.$on('after-leave', () => {

            el.domVisible = false;

            if (binding.modifiers.fullscreen && el.originalOverflow !== 'hidden') {

              document.body.style.overflow = el.originalOverflow;

            }

            if (binding.modifiers.fullscreen) {

              document.body.style.position = el.originalPosition;

            } else {

              el.style.position = el.originalPosition;

            }

          });

          el.instance.visible = false;

        }

      }

    }

    const insertDom = (parent, el, binding) => { // 插入dom的邏輯

      if(!el.domVisible) {

        Object.keys(el.loadingStyle).forEach(property => {

          el.loading.style[property] = el.loadingStyle[property];

        });

        if(el.originalPosition !== 'absolute') {

          parent.style.position = 'relative'

        }

        if (binding.modifiers.fullscreen) {

          parent.style.overflow = 'hidden'

        }

        el.domVisible = true;

        parent.appendChild(el.loading) // 插入的是el.loading而不是el本身

        Vue.nextTick(() => {

          el.instance.visible = true;

        });

        el.domInserted = true;

      }

    }

  }

}

同樣,寫完整個邏輯,我們需要將其注冊到項目里的Vue下:

// main.js

// ...

import Loading from 'loading/index.js'

Vue.use(Loading)

// ...

至此我們已經可以使用形如

<div v-loading.fullscreen="loading" loading-text="正在加載中">

這樣的方式來實現調用一個loading組件了。

0
福贡县| 登封市| 广饶县| 黔南| 康保县| 沧州市| 韩城市| 嘉峪关市| 台山市| 黄浦区| 瓮安县| 青浦区| 南华县| 晋州市| 金乡县| 汪清县| 和平县| 泸定县| 铜梁县| 西城区| 句容市| 四川省| 武冈市| 蓬溪县| 民权县| 鄱阳县| 肥西县| 高阳县| 贵德县| 壤塘县| 新建县| 左云县| 宕昌县| 马山县| 江山市| 滦南县| 尼木县| 金华市| 察隅县| 方正县| 新乡市|