簡單的例子實現vue插件

一直都以爲vue的插件生澀難懂,可是又很好奇,在看了幾篇文章,試着寫了寫以後以爲也沒那麼難,這篇文就是總結一下這個過程,加深記憶,也能夠幫助後來的人。css

why

在學習以前,先問問本身,爲何要編寫vue的插件。html

在一個項目中,尤爲是大型項目,有不少部分須要複用,好比加載的loading動畫,彈出框。若是一個一個的引用也稍顯麻煩,並且在一個vue文件中引用的組件多了,會顯得代碼臃腫,因此纔有了封裝vue插件的需求。vue

說完需求,就來看看具體實現。目前我嘗試了兩種不同的插件編寫的方法,逐個介紹。web

這是個人項目目錄,大體的結構解釋這樣,儘可能簡單,容易理解。app

一個是loading插件,一個是toast插件,不一樣的地方在於:loading插件是做爲組件引入使用,而toast插件是直接添加在掛載點裏,經過方法改變狀態調用的。ssh

目前使用起來是醬紫的:函數

toast插件

toast文件下有兩個文件,後綴爲vue的文件就是這個插件的骨架,js文件一個是將這個骨架放入Vue全局中,並寫明操做邏輯。工具

能夠看一下toast.vue的內容:學習

<template>
    <transition name="fade">
        <div class="toast" v-show="show">
            {{message}}
        </div>

    </transition>
</template>

<script>
export default {
  data() {
    return {
      show: false,
      message: ""
    };
  }
};
</script>

<style lang="scss" scoped>
.toast {
  position: fixed;
  top: 40%;
  left: 50%;
  margin-left: -15vw;
  padding: 2vw;
  width: 30vw;
  font-size: 4vw;
  color: #fff;
  text-align: center;
  background-color: rgba(0, 0, 0, 0.8);
  border-radius: 5vw;
  z-index: 999;
}

.fade-enter-active,
.fade-leave-active {
  transition: 0.3s ease-out;
}
.fade-enter {
  opacity: 0;
  transform: scale(1.2);
}
.fade-leave-to {
  opacity: 0;
  transform: scale(0.8);
}
</style>

這裏面主要的內容只有兩個,決定是否顯示的show和顯示什麼內容的message動畫

粗看這裏,有沒有發現什麼問題?

這個文件中並無props屬性,也就是不管是show也好,message也好,就沒有辦法經過父子組件通訊的方式進行修改,那他們是怎麼正確處理的呢。別急,來看他的配置文件。

index.js:

import ToastComponent from './toast.vue'

const Toast = {};

// 註冊Toast
Toast.install = function (Vue) {
    // 生成一個Vue的子類
    // 同時這個子類也就是組件
    const ToastConstructor = Vue.extend(ToastComponent)
    // 生成一個該子類的實例
    const instance = new ToastConstructor();

    // 將這個實例掛載在我建立的div上
    // 並將此div加入全局掛載點內部
    instance.$mount(document.createElement('div'))
    document.body.appendChild(instance.$el)
    
    // 經過Vue的原型註冊一個方法
    // 讓全部實例共享這個方法 
    Vue.prototype.$toast = (msg, duration = 2000) => {
        instance.message = msg;
        instance.show = true;

        setTimeout(() => {
            
            instance.show = false;
        }, duration);
    }
}

export default Toast

這裏的邏輯大體能夠分紅這麼幾步:

  1. 建立一個空對象,這個對象就是往後要使用到的插件的名字。此外,這個對象中要有一個install的函數。
  2. 使用vue的extend方法建立一個插件的構造函數(能夠看作建立了一個vue的子類),實例化該子類,以後的全部操做均可以經過這個子類完成。
  3. 以後再Vue的原型上添加一個共用的方法。

這裏須要着重提的是Vue.extend()。舉個例子,咱們平常使用vue編寫組件是這個樣子的:

Vue.component('MyComponent',{
    template:'<div>這是組件</div>'
})

這是全局組件的註冊方法,但其實這是一個語法糖,真正的運行過程是這樣的:

let component = Vue.extend({
    template:'<div>這是組件</div>'
})

Vue.component('MyComponent',component)

Vue.extend會返回一個對象,按照大多數資料上說起的,也能夠說是返回一個Vue的子類,既然是子類,就沒有辦法直接經過他使用Vue原型上的方法,因此須要new一個實例出來使用。

在代碼裏console.log(instance)

得出的是這樣的結果:

能夠看到

$el:div.toast

也就是toast組件模板的根節點。

疑惑的是,我不知道爲何要建立一個空的div節點,並把這個實例掛載在上面。我嘗試註釋這段代碼,可是運行會報錯。

查找這個錯誤的緣由,貌似是由於

document.body.appendChild(instance.$el)

這裏面的instance.$el的問題,那好,咱們console下這個看看。WTF!!!!結果竟然是undefined

那接着

console.log(instance)

和上一張圖片比對一下,發現了什麼?對,$el消失了,換句話說在我註釋了

instance.$mount(document.createElement('div'))

這句話以後,掛載點也不存在了。接着我試着改了一下這句:

instance.$mount(instance.$el)

$el又神奇的回來了………………

暫時沒有發現這種改動有什麼問題,能夠和上面同樣運行。但不管如何,這也就是說instance實例必須掛載在一個節點上才能進行後續操做。

以後的代碼就簡單了,無非是在Vue的原型上添加一個改變插件狀態的方法。以後導出這個對象。

接下來就是怎麼使用的問題了。來看看main.js是怎麼寫的:

import Vue from 'vue'
import App from './App'
// import router from './router'
import Toast from './components/taost'
Vue.use(Toast)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({

  // router,
  render: h => h(App)
}).$mount('#app')

這樣就能夠在其餘vue文件中直接使用了,像這樣:

// app.vue
<template>
  <div id="app">
    <loading duration='2s' :isshow='show'></loading>
    <!-- <button @click="show = !show">顯示/隱藏loading</button> -->
    <button @click="toast">顯示taost彈出框</button>
  </div>
</template>

<script>
export default {
  name: "app",
  data() {
    return {
      show: false
    };
  },
  methods: {
    toast() {
      this.$toast("你好");
    }
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

經過在methods中增長一個方法控制寫在Vue原型上的$toast對toast組件進行操做。

這樣toast組件的編寫過程就結束了,能夠看到一開始gif圖裏的效果。

loading插件

通過上一個插件的講解,這一部分就不會那麼細緻了,畢竟大多數都沒有什麼不一樣,我只指出不同的地方。

<template>
    <div class='wrapper' v-if="isshow">
        <div class='loading'>
            <img src="./loading.gif" alt="" width="40" height="40">
        </div>
    </div>
</template>

<script>
export default {
  props: {
    duration: {
      type: String,
      default: "1s" //默認1s
    },
    isshow: {
      type: Boolean,
      default: false
    }
  },
  data: function() {
    return {};
  }
};
</script>

<style lang="scss" scoped>

</style>

這個就只是一個模板,傳入兩個父組件的數據控制顯示效果。

那再來看一下該插件的配置文件:

import LoadingComponent from './loading.vue'

let Loading = {};

Loading.install = (Vue) => {
    Vue.component('loading', LoadingComponent)
}

export default Loading;

這個和taoat的插件相比,簡單了不少,依然是一個空對象,裏面有一個install方法,而後在全局註冊了一個組件。

比較

那介紹了這兩種不一樣的插件編寫方法,貌似沒有什麼不同啊,真的是這樣麼?

來看一下完整的main.js和app.vue這兩個文件:

// main.js
import Vue from 'vue'
import App from './App'
// import router from './router'
import Toast from './components/taost'
import Loading from './components/loading'

Vue.use(Toast)

Vue.use(Loading)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({

  // router,
  render: h => h(App)
}).$mount('#app')
// app.vue
<template>
  <div id="app">
    <loading duration='2s' :isshow='show'></loading>
    <!-- <button @click="show = !show">顯示/隱藏loading</button> -->
    <button @click="toast">顯示taost彈出框</button>
  </div>
</template>

<script>
export default {
  name: "app",
  data() {
    return {
      show: false
    };
  },
  methods: {
    toast() {
      this.$toast("你好");
    }
  }
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

能夠看出來,loading是顯示的寫在app.vue模板裏的,而toast並無做爲一個組件寫入,僅僅是經過一個方法控制顯示。

來看一下html結構和vue工具給出的結構:

看出來了麼,toast插件沒有在掛載點裏面,而是獨立存在的,也就是說當執行

vue.use(toast)

以後,該插件就是生成好的了,以後的全部操做無非就是顯示或者隱藏的問題了。

相關文章
相關標籤/搜索