越是讓你感覺到害怕的事情沟沙,就越要去面對它。
在談主題插件之前壁榕,我想先引出兩個關(guān)于Vue
的問題矛紫,這也是我存在的兩個疑問,希望有人能夠幫忙解答牌里。
如果你比較心急颊咬,可以直接跳到Vue.use源碼解讀。
有幾個疑問
-
如果子組件沒有在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炊邦,自定義局部指令該如何簡寫?
我們知道攀涵,為了簡單铣耘,自定義全局指令時可以使用簡寫(只關(guān)心
bind
和update
鉤子函數(shù)時):Vue.directive('style', function (el, { value }) { el.style.height = value + 'px' })
那么洽沟,自定義局部指令時該如何簡寫呢以故?還是說
Vue
本身就不支持局部指令簡寫?因為很多時候裆操,bind
和update
鉤子函數(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)也有說明:
后記
寫一篇文章观蜗,對別人來說是一種分享臊恋,其實對自己來說更是一種提高衣洁。因為你要寫好一篇文章,一方面你得盡量保證其正確性抖仅,有時候你不得不親自去驗證坊夫,另一方面也是對自己所學(xué)的知識進行一遍系統(tǒng)的復(fù)習(xí)和整理。
如果你有時間撤卢,我建議你多寫一些技術(shù)類文章环凿;如果你實在沒時間寫,那也要多讀讀別人寫的文章放吩。