Vue自定義指令directive,外掛的封裝以及混合mixins
一.自定義指令directive
除了核心功能預設內建的指令 (v-model
和 v-show
),Vue 也允許註冊自定義指令。注意,在 Vue2.0
中,程式碼複用和抽象的主要形式是元件。然而,有的情況下,你仍然需要對普通 DOM 元素進行底層操作,這時候就會用到自定義指令。
來個例項,當頁面載入時,該input元素將獲得焦點:
// 註冊一個全域性自定義指令 `v-focus` Vue.directive('focus', { // 當被繫結的元素插入到 DOM 中時…… inserted: function (el) { // 聚焦元素 el.focus() } })
如果想註冊區域性指令,元件中也接受一個 directives
的選項:
directives: { focus: { // 指令的定義 inserted: function (el) { el.focus() } } }
然後你可以在模板中任何元素上使用新的 v-focus 屬性,如下:
<input v-focus>
鉤子函式
一個指令定義物件可以提供如下幾個鉤子函式 (均為可選):
bind
:只調用一次,指令第一次繫結到元素時呼叫。在這裡可以進行一次性的初始化設定。inserted
update
:所在元件的 VNode 更新時呼叫,但是可能發生在其子 VNode 更新之前。指令的值可能發生了改變,也可能沒有。但是你可以通過比較更新前後的值來忽略不必要的模板更新 (詳細的鉤子函式引數見下)。componentUpdated
:指令所在元件的 VNode 及其子 VNode 全部更新後呼叫。unbind
:只調用一次,指令與元素解綁時呼叫。
接下來我們來看一下鉤子函式的引數 (即 el
、binding
、vnode
和 oldVnode
)。
鉤子函式引數
指令鉤子函式會被傳入以下引數:
el
:指令所繫結的元素,可以用來直接操作 DOM 。-
binding
:一個物件,包含以下屬性:name
:指令名,不包括 v- 字首。value
:指令的繫結值,例如:v-my-directive="1 + 1"
中,繫結值為2
。oldValue
:指令繫結的前一個值,僅在update
和componentUpdated
鉤子中可用。無論值是否改變都可用。expression
:字串形式的指令表示式。例如v-my-directive="1 + 1"
中,表示式為"1 + 1"
。arg
:傳給指令的引數,可選。例如v-my-directive:foo
中,引數為"foo"
。modifiers
:一個包含修飾符的物件。例如:v-my-directive.foo.bar
中,修飾符物件為{ foo: true, bar: true }
。
vnode
:Vue
編譯生成的虛擬節點。移步 VNode API 來了解更多詳情。oldVnode
:上一個虛擬節點,僅在update
和componentUpdated
鉤子中可用。
除了
el
之外,其它引數都應該是隻讀的,切勿進行修改。如果需要在鉤子之間共享資料,建議通過元素的 dataset 來進行。
這是一個使用了這些屬性的自定義鉤子樣例:
<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>
Vue.directive('demo', { bind: function (el, binding, vnode) { var s = JSON.stringify el.innerHTML = 'name: ' + s(binding.name) + '<br>' + 'value: ' + s(binding.value) + '<br>' + 'expression: ' + s(binding.expression) + '<br>' + 'argument: ' + s(binding.arg) + '<br>' + 'modifiers: ' + s(binding.modifiers) + '<br>' + 'vnode keys: ' + Object.keys(vnode).join(', ') } }) new Vue({ el: '#hook-arguments-example', data: { message: 'hello!' } })
結果:
name: "demo" value: "hello!" expression: "message" argument: "foo" modifiers: {"a":true,"b":true} vnode keys: tag, data, children, text, elm, ns, context, fnContext, fnOptions, fnScopeId, key, componentOptions, componentInstance, parent, raw, isStatic, isRootInsert, isComment, isCloned, isOnce, asyncFactory, asyncMeta, isAsyncPlaceholder
在很多時候,你可能想在 bind 和 update 時觸發相同行為,而不關心其它的鉤子。比如這樣寫:
Vue.directive('color-swatch', function (el, binding) { el.style.backgroundColor = binding.value })
物件字面量
如果指令需要多個值,可以傳入一個 JavaScript 物件字面量。記住,指令函式能夠接受所有合法的 JavaScript 表示式。
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Vue.directive('demo', function (el, binding) { console.log(binding.value.color) // => "white" console.log(binding.value.text) // => "hello!" })
二.外掛
外掛通常會為 Vue 新增全域性功能。外掛的範圍沒有限制——一般有下面幾種:
- 1.新增全域性方法或者屬性,如: vue-custom-element
- 2.新增全域性資源:指令/過濾器/過渡等,如 vue-touch
- 3.通過全域性 mixin 方法新增一些元件選項,如: vue-router
- 4.新增 Vue 例項方法,通過把它們新增到 Vue.prototype 上實現。
- 5.一個庫,提供自己的 API,同時提供上面提到的一個或多個功能,如 vue-router
Vue.js 的外掛應當有一個公開方法 install
。這個方法的第一個引數是 Vue
構造器,第二個引數是一個可選的選項物件:
MyPlugin.install = function (Vue, options) { // 1. 新增全域性方法或屬性 Vue.myGlobalMethod = function () { // 邏輯... } // 2. 新增全域性資源 Vue.directive('my-directive', { bind (el, binding, vnode, oldVnode) { // 邏輯... } ... }) // 3. 注入元件 Vue.mixin({ created: function () { // 邏輯... } ... }) // 4. 新增例項方法 Vue.prototype.$myMethod = function (methodOptions) { // 邏輯... } }
怎樣使用外掛
通過全域性方法 Vue.use()
使用外掛:
// 呼叫 `MyPlugin.install(Vue)` Vue.use(MyPlugin)
也可以傳入一個選項物件:
Vue.use(MyPlugin, { someOption: true })
Vue.use
會自動阻止多次註冊相同外掛,屆時只會註冊一次該外掛。
Vue.js
官方提供的一些外掛 (例如 vue-router) 在檢測到 Vue 是可訪問的全域性變數時會自動呼叫 Vue.use()
。然而在例如 CommonJS 的模組環境中,你應該始終顯式地呼叫 Vue.use()
:
// 用 Browserify 或 webpack 提供的 CommonJS 模組環境時 var Vue = require('vue') var VueRouter = require('vue-router') // 不要忘了呼叫此方法 Vue.use(VueRouter)
簡單例子
封裝一個全域性的外掛,如下:
在src下的components資料夾下新建一個countdown資料夾,新建一個countdown.vue檔案:
<template> <div>封裝一個最簡單的外掛</div> </template> <script> export default{ name:'count-down' } </script>
在放countdown.vue的同級目錄下新建一個index.js:
import countDown from './countdown'; countDown.install = function(Vue){ Vue.component(countDown.name,countDown) }; export default countDown;
在main.js中:
import countDown from './components/countdown/index.js' Vue.use(countDown)
在元件中就可以這樣使用了:
<count-down></count-down>
三.混合mixins
混合 (mixins
) 是一種分發 Vue 元件中可複用功能的非常靈活的方式。混合物件可以包含任意元件選項。當元件使用混合物件時,所有混合物件的選項將被混入該元件本身的選項。
// 定義一個混合物件 var myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log('hello from mixin!') } } } // 定義一個使用混合物件的元件 var Component = Vue.extend({ mixins: [myMixin] }) var component = new Component() // => "hello from mixin!"
選項合併
當元件和混合物件含有同名選項時,這些選項將以恰當的方式混合。比如,同名鉤子函式將混合為一個數組,因此都將被呼叫。另外,混合物件的 鉤子將在元件自身鉤子 之前 呼叫 :
var mixin = { created: function () { console.log('混合物件的鉤子被呼叫') } } new Vue({ mixins: [mixin], created: function () { console.log('元件鉤子被呼叫') } }) // => "混合物件的鉤子被呼叫" // => "元件鉤子被呼叫"
值為物件的選項,例如 methods
, components
和 directives
,將被混合為同一個物件。兩個物件鍵名衝突時,取元件物件的鍵值對。
var mixin = { methods: { foo: function () { console.log('foo') }, conflicting: function () { console.log('from mixin') } } } var vm = new Vue({ mixins: [mixin], methods: { bar: function () { console.log('bar') }, conflicting: function () { console.log('from self') } } }) vm.foo() // => "foo" vm.bar() // => "bar" vm.conflicting() // => "from self"
注意:
Vue.extend()
也使用同樣的策略進行合併。
全域性混合
也可以全域性註冊混合物件。注意使用! 一旦使用全域性混合物件,將會影響到 所有 之後建立的 Vue 例項。使用恰當時,可以為自定義物件注入處理邏輯。
// 為自定義的選項 'myOption' 注入一個處理器。 Vue.mixin({ created: function () { var myOption = this.$options.myOption if (myOption) { console.log(myOption) } } }) new Vue({ myOption: 'hello!' }) // => "hello!"
謹慎使用全域性混合物件
,因為會影響到每個單獨建立的 Vue 例項 (包括第三方模板)。大多數情況下,只應當應用於自定義選項,就像上面示例一樣。也可以將其用作 Plugins 以避免產生重複應用
自定義選項合併策略
自定義選項將使用預設策略,即簡單地覆蓋已有值。如果想讓自定義選項以自定義邏輯合併,可以向 Vue.config.optionMergeStrategies
新增一個函式:
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) { // return mergedVal }
對於大多數物件選項,可以使用 methods 的合併策略:
var strategies = Vue.config.optionMergeStrategies strategies.myOption = strategies.met