Vue入坑史喜命,插件系統(tǒng)詳解

越是讓你感覺到害怕的事情沟沙,就越要去面對它。

在談主題插件之前壁榕,我想先引出兩個關(guān)于Vue的問題矛紫,這也是我存在的兩個疑問,希望有人能夠幫忙解答牌里。

如果你比較心急颊咬,可以直接跳到Vue.use源碼解讀

有幾個疑問

  1. 如果子組件沒有在html文檔中通過{{ value }}形式使用父組件傳遞的props參數(shù)牡辽,那么就算父組件改變了props參數(shù)喳篇,子組件也不會自動更新。

    比如子組件通過props參數(shù)只是用來控制自己的CSS樣式的時候:

    <!-- 示例1 -->
    <template>
        <!-- 沒有在html中使用 height -->
        <div class="wrap"></div>
    </template>
    <script>
    export default {
        props: ['height'],
        mounted () {
            this.$el.style.height = this.height + 'px'
        },
        updated () {
            this.$el.style.height = this.height + 'px'
        }
    }
    </script>
    
    <!-- 示例2 -->
    <template>
        <!-- 在自定義指令使用 height -->
        <div class="wrap" v-style="height"></div>
    </template>
    <script>
    export default {
        props: ['height'],
        directives: {
            style: {
                bind (el, { value }) {
                    el.style.height = value + 'px'
                },
                update (el, { value, oldValue}) {
                    if (value === oldValue) {
                        return
                    }
                    el.style.height = value + 'px'
                }
            }
        }
    }
    </script>
    

    我不知道是我使用的姿勢不對态辛,還是我忽略了什么東西麸澜。我的解決辦法是父組件調(diào)用this.$forceUpdate()來強制更新,但總感覺這樣會影響性能奏黑。

  2. 針對上面的示例2炊邦,自定義局部指令該如何簡寫?

    我們知道攀涵,為了簡單铣耘,自定義全局指令時可以使用簡寫(只關(guān)心bindupdate鉤子函數(shù)時):

    Vue.directive('style', function (el, { value }) {
        el.style.height = value + 'px'
    })
    

    那么洽沟,自定義局部指令時該如何簡寫呢以故?還是說Vue本身就不支持局部指令簡寫?因為很多時候裆操,bindupdate鉤子函數(shù)中的代碼都是一樣的怒详。

這兩個是我最近在開發(fā)過程中遇到的問題炉媒,也沒有找到相關(guān)的答案,希望有誰能夠幫忙解答昆烁,在此先行謝過了吊骤。

什么是插件

Vue的插件一般就是用來擴展Vue的功能。比如静尼,當(dāng)需要Vue實現(xiàn)Ajax請求功能白粉,我們希望通過this.$get(url)的形式就可以發(fā)送一個get請求。那么鼠渺,我們就需要給Vue的實例添加一個$get方法鸭巴,Vue實例本身是沒有這個方法的。

Vue的一些插件:

  • vuex:官方狀態(tài)管理插件拦盹;
  • vue-router:官方路由插件鹃祖;
  • vue-resource:Ajax請求插件;
  • vue-element:餓了么出品的組件庫普舆。

如何使用插件

在創(chuàng)建Vue實例之前恬口,通過全局方法Vue.use()來使用插件:

// 使用 MyPlugin 插件
Vue.use(MyPlugin)

// 或 傳入一個選項參數(shù),options 一般是一個對象
Vue.use(MyPlugin, options)

是不是很簡單沼侣,好像也沒有什么好說的祖能。

有時候,我們看到某些插件使用起來可能有些不一樣蛾洛。比如使用vuex

// store.js 文件中
import Vuex from 'vuex'
import Vue from 'vue'
import state from './state'
import mutations from './mutations'
import actions from './actions'

// 注冊插件
Vue.use(Vuex)

const store = new Vuex.Store({
    state,
    mutations,
    actions
})

export default store
// main.js 文件中
import Vue form 'vue'
import App from './App'
import store from './store'

new Vue({
    el: '#app',
    store,
    render: h => h(App)
})

其實本質(zhì)上還是一樣的芯杀,也是通過Vue.use()方法注冊插件。只不過它有一個store對象雅潭,然后并將store對象作為Vue根實例的屬性揭厚,以便組件通過this.$store這種形式來訪問。

自定義插件

其實當(dāng)通過Vue.use()注冊插件時扶供,內(nèi)部會自動調(diào)用插件的install()方法筛圆。也就是說,插件必須要提供一個公開的install()方法椿浓,作為接口太援。該方法第一個參數(shù)是Vue,第二個參數(shù)是可選的options對象扳碍。

總結(jié)起來說提岔,插件是一個對象。該對象要有一個公開的install()方法笋敞,那么寫起來可能是這樣的:

const MyPlugin = {}
MyPlugin.install = function (Vue, options) {
    // ...
}

export default MyPlugin

install()方法中碱蒙,我們通過參數(shù)可以拿到Vue對象,那么,我們就可以對它做很多事情赛惩。

MyPlugin.install = function (Vue, options) {
    // 1. 添加全局方法和屬性
    Vue.myProperty = 'Hello Vue',
    
    // 2. 添加全局的自定義指令
    Vue.directive('name', function (el, binding) {
        // ...
    })
    
    // 3. 混合
    Vue.mixin({
        created () {
            // ...
        }
    })
    
    // 4. 添加實例方法
    // 通過原型為一個對象添加實例方法
    // 在 Vue 實例中哀墓,通過 this.$get() 就可以調(diào)用該方法
    Vue.prototype.$get = function () {
        // ...
    }
}

插件的幾種寫法

這里直接就看幾個插件的源碼吧,看看他們是怎么寫的喷兼,其實我也是參照了這些源碼才真正弄明白了插件是怎么一回事篮绰。源碼很長,這里只說一些關(guān)鍵點季惯。

vue-touch

// 外面是一個立即執(zhí)行函數(shù)吠各,控制作用域
// 前面的分號應(yīng)該是為了更好的兼容其他js源碼吧
;(function () {
    var vueTouch = {}
    
    // 這里沒有接收第二個參數(shù)
    vueTouch.install = function (Vue) {
        // ...
    }
    
    // 導(dǎo)出 vueTouch 插件
    if (typeof exports == "object") {
        module.exports = vueTouch
    } else if (typeof define == "function" && define.amd) {
        define([], function(){ return vueTouch })
    } else if (window.Vue) {
        // 如果 Vue 作為全局對象,則自動使用插件
        // 也就是說勉抓,當(dāng)我們在HTML文檔中通過script直接引用 vue 和 vueTouch 時走孽,不需要手動注冊
        window.VueTouch = vueTouch
        Vue.use(vueTouch)
    }
})()

vue-router

import { install } from './install'

export default class VueRouter {
    // 定義了一個靜態(tài)方法, ES6 新增的語法
    // 用其他語言理解也就相當(dāng)于一個類方法琳状,通過類名調(diào)用
    static install: () => void
}

// 這里 install 是一個從外部引入的函數(shù)
VueRouter.install = install

// 自動注冊插件
if (inBrowser && window.Vue) {
    window.Vue.use(VueRouter)
}

vuex

import { Store, install } from './store'

export default {
    install,
    // ...
}
// 這個最簡單直觀磕瓷,沒啥好說的

vue-resource(重點)

// 這個。念逞。困食。
// 我也不知道如何解釋
// 這里好像直接用 plugin 代替了 install 方法
// 當(dāng)使用 Vue.use(vueResource) 時,會調(diào)用該函數(shù) 翎承?硕盹??
// 先暫且這么認(rèn)為吧
function plugin(Vue) {
    // 避免重復(fù)注冊
    if (plugin.installed) {
        return
    }
    
    // ...
}

// 自動注冊插件
if (typeof window !== 'undefined' && window.Vue) {
    window.Vue.use(plugin);
}

export default plugin;

<span id="use">Vue.use源碼解讀(一定要看)</span>

針對vue-resource插件問題叨咖,我查看了一下vue的源碼瘩例,它的源碼是這樣的:

Vue.use = function (plugin: Function | Object) {
    /* istanbul ignore if */
    // 保證同一個插件只安裝一次
    if (plugin.installed) {
        return
    }
    // additional parameters
    // 這句的作用應(yīng)該是去掉第一個參數(shù),然后轉(zhuǎn)換成數(shù)組
    const args = toArray(arguments, 1)
    // 將Vue作為數(shù)組的第一個元素甸各,以便傳入插件中
    args.unshift(this)
    // 插件有install接口垛贤,并且是一個函數(shù)
    if (typeof plugin.install === 'function') {
        // 在plugin作用域下調(diào)用 install ,并傳入拼接好的參數(shù)
        // 在 install 中趣倾,this 指向 plugin
        plugin.install.apply(plugin, args)
        
    // 插件本身是一個函數(shù)
    // 解釋vue-resource寫法的關(guān)鍵點在這
    } else if (typeof plugin === 'function') {
        // 在全局作用域下調(diào)用 plugin 函數(shù)
        // plugin 中聘惦,this 指向 window
        plugin.apply(null, args)
    }
    plugin.installed = true
    return this
}

通過源碼,我們知道儒恋,Vue插件可以是一個對象或者是一個函數(shù)善绎。只有當(dāng)插件實現(xiàn)了 install 接口時(有install這個函數(shù)時),才會調(diào)用插件的install方法诫尽;否則再判斷插件本身是否是一個函數(shù)禀酱,如果是,就直接調(diào)用它牧嫉。

現(xiàn)在就能很好的解釋vue-resource插件的寫法了剂跟。好吧,我也是剛剛得知,又長了一點見識浩聋。

其實官網(wǎng)也有說明

Vue.use(plugin)

后記

寫一篇文章观蜗,對別人來說是一種分享臊恋,其實對自己來說更是一種提高衣洁。因為你要寫好一篇文章,一方面你得盡量保證其正確性抖仅,有時候你不得不親自去驗證坊夫,另一方面也是對自己所學(xué)的知識進行一遍系統(tǒng)的復(fù)習(xí)和整理。

如果你有時間撤卢,我建議你多寫一些技術(shù)類文章环凿;如果你實在沒時間寫,那也要多讀讀別人寫的文章放吩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末智听,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子渡紫,更是在濱河造成了極大的恐慌到推,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惕澎,死亡現(xiàn)場離奇詭異莉测,居然都是意外死亡,警方通過查閱死者的電腦和手機唧喉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門捣卤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人八孝,你說我怎么就攤上這事董朝。” “怎么了干跛?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵益涧,是天一觀的道長。 經(jīng)常有香客問我驯鳖,道長闲询,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任浅辙,我火速辦了婚禮扭弧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘记舆。我一直安慰自己鸽捻,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著御蒲,像睡著了一般衣赶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上厚满,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天府瞄,我揣著相機與錄音,去河邊找鬼碘箍。 笑死遵馆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丰榴。 我是一名探鬼主播货邓,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼四濒!你這毒婦竟也來了换况?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤盗蟆,失蹤者是張志新(化名)和其女友劉穎戈二,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姆涩,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡挽拂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了骨饿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亏栈。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宏赘,靈堂內(nèi)的尸體忽然破棺而出绒北,到底是詐尸還是另有隱情,我是刑警寧澤察署,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布闷游,位于F島的核電站,受9級特大地震影響贴汪,放射性物質(zhì)發(fā)生泄漏脐往。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一扳埂、第九天 我趴在偏房一處隱蔽的房頂上張望业簿。 院中可真熱鬧,春花似錦阳懂、人聲如沸梅尤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巷燥。三九已至赡盘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缰揪,已是汗流浹背陨享。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留邀跃,地道東北人霉咨。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓蛙紫,卻偏偏與公主長得像拍屑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坑傅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容