title: vue可复用性&组合
date: 2019-10-14 15:08:18
tags: # 这里写的分类会自动汇集到 categories 页面上,分类可以多级
- Vue.js # 一级分类
- Vue.js进阶 # 二级分类
混入 mixin
一个混入对象可以包含任意组件选项.当组件使用混入对象时,所有混入对象的选项将被”混合”进组件本身的选项.
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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'
|
选项合并
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行”合并”.
比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| var mixin = { data: function () { return { message: "hello", foo: "abc", }; }, }; new Vue({ mixins: [mixin], data: function () { return { message: "goodbye", bar: "def", }; }, created: function () { console.log(this.$data); }, });
|
同名钩子函数将合并成一个数组,因此都将被调用.另外,混入对象的钩子将在组件自身钩子之前调用.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var mixin = { created: function () { console.log("混入对象钩子被调用"); }, };
new Vue({ mixins: [mixin], created: function () { console.log("组件钩子被调用"); }, });
|
值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| var mixin = { methods: { foo: function () { console.log("foo"); }, conficting: function () { console.log("from mixin"); }, }, };
var vm = new Vue({ mixins: [mixin], methods: { bar: function () { console.log("bar"); }, conficting: function () { console.log("from self"); }, }, });
vm.foo(); vm.bar(); vm.conflicting();
|
注意:Vue.extend() 也使用同样的策略进行合并。
全局混入
混入也可以进行全局注册,使用时需要小心.一但使用全局混入,他将影响每一个之后创建的 Vue 实例.
使用恰当时还可以用来为自定义选项注入处理逻辑.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Vue.mixin({ created: function () { var myOption = this.$options.myOption; if (myOption) { console.log(myOption); } }, });
new Vue({ myOption: "hello!", });
|
请谨慎使用全局混入,因为它会影响每个单独创建的 Vue 实例 (包括第三方组件)。大多数情况下,只应当应用于自定义选项,就像上面示例一样。推荐将其作为插件发布,以避免重复应用混入。
自定义选项合并策略
自定义选项将使用默认策略,即简单地覆盖已有值。如果想让自定义选项以自定义逻辑合并,可以向 Vue.config.optionMergeStrategies 添加一个函数:
1 2 3
| Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) { };
|
对于多数值为对象的选项,可以使用与 methods 相同的合并策略:
1 2
| var strategies = Vue.config.optionMergeStrategies; strategies.myOption = strategies.methods;
|
可以在 Vuex 1.x 的混入策略里找到一个更高级的例子:
1 2 3 4 5 6 7 8 9 10
| const merge = Vue.config.optionMergeStrategies.computed; Vue.config.optionMergeStrategies.vuex = function (toVal, fromVal) { if (!toVal) return fromVal; if (!fromVal) return toVal; return { getters: merge(toVal.getters, fromVal.getters), state: merge(toVal.state, fromVal.state), actions: merge(toVal.actions, fromVal.actions), }; };
|
自定义指令
输入框聚焦
当页面加载时,该元素将获得焦点 (注意:autofocus 在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:
1 2 3 4 5 6 7 8
| Vue.directive("focus", { inserted: function (el) { el.focus(); }, });
|
局部指令注册,组件中接受directive:
1 2 3 4 5 6 7 8
| directives: { focus: { inserted: function(el){ el.focus() } } }
|
然后在模板中任何元素上使用v-focus属性,例如:
钩子函数
一个指令对象可以提供以下几个钩子函数:
bind: 只调用一次,指令第一次绑定到元素时调用.在这里进行一次性的初始化设置.
inserted: 被绑定元素插入到父节点时调用.
update: 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前.指令的值可能发生改变,也可能没有.
componentUpdated: 指令所在组件的 VNode 及其子 VNode 全部更新后调用.
unbind: 只调用一次,指令与元素解绑时调用.
钩子函数的参数
指令钩子会被传入以下参数:
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 编译生成的虚拟节点.
oldVNode: 上一个虚拟节点.
除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。
范例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <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!' } })
|
动态指令参数
在 v-mydirective:[argument]="value" 中,argument 参数可以根据组件实例数据进行更新!
例如创建一个自定义指令,通过固定布局将元素固定在页面上.
1 2 3 4
| <div id="baseexample"> <p>Scroll down this page</p> <p v-pin="200">Stick me 200px from the top of the page<p> </div>
|
自定义事件
1 2 3 4 5 6 7 8 9 10
| Vue.directive('pin', { bind: function(el, bingding, vnode){ el.style.position = 'fixed' el.style.top = binding.value + 'px' } })
new Vue({ el: '#baseexample" })
|
这会把元素固定在距离页面顶部 200px 的位置.
如果需要将元素固定在左侧呢?
1 2 3 4
| <div id="dynamicexample"> <p>Scroll down this page</p> <p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.<p> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Vue.directive("pin", { bind: function (el, binding, vnode) { el.style.position = "fixed"; var s = binding.arg == "left" ? "left" : "top"; el.style[s] = binding.value + "px"; }, });
new Vue({ el: "#dynamicexample", data: function () { return { direction: "left", }; }, });
|
函数简写
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:
1 2 3
| Vue.directive("color-swatch", function (el, binding) { el.style.backgroundColor = binding.value; });
|
对象字面量
如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。
1 2 3 4 5 6 7
| <div v-demo="{ color: 'white', text: 'hello!' }"></div>;
Vue.directive("demo", function (el, binding) { console.log(binding.value.color); console.log(binding.value.text); });
|
渲染函数&JSX
渲染函数在 Vue.js 进阶里有讲到,JSX 先不看了.
插件
插件用来为 Vue 添加全局功能.
插件常用功能:
- 添加全局方法或属性.
- 添加全局资源: 指令,过滤器,过渡
- 通过全局混入来添加一些组件选项.
- 添加 Vue 实例方法,通过把他们添加到 Vue.prototype 上实现.
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能.如 Vue-router
使用插件
通过全局方法Vue.use()使用插件.需要在new Vue()之前调用.
也可以传入一个可选的对象.
1
| Vue.use(MyPlugin, { someOption: true });
|
Vue.use 会自动阻止多次注册相同插件,届时即使多次调用也只会注册一次该插件。
Vue.js 官方提供的一些插件 (例如 vue-router) 在检测到 Vue 是可访问的全局变量时会自动调用 Vue.use()。然而在像 CommonJS 这样的模块环境中,你应该始终显式地调用 Vue.use()
开发插件
Vue.js 的插件暴露一个 install 的方法.这个方法的第一个参数是 Vue 构造器,第二个参数是可选的选项对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| MyPlugin.install = function (Vue, options) { Vue.myGlobalMethod = function () { };
Vue.directive("my-directive", { bind(el, binding, vnode, oldVnode) { }, });
Vue.mixin({ created: function () { }, });
Vue.prototype.$myMethod = function (methodOptions) { }; };
|
过滤器
过滤器可以用在:双花括号插值和 v-bind 表达式
1 2 3 4 5
| <!-- 在双花括号中 --> {{ message | capitalize }}
<!-- 在 `v-bind` 中 --> <div v-bind:id="rawId | formatId"></div>
|
在组件的选项中定义本地的过滤器
1 2 3 4 5 6 7
| filters: { capitalize: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } }
|
或者在创建 Vue 实例之前全局定义过滤器:
1 2 3 4 5 6 7 8 9
| Vue.filter("capitalize", function (value) { if (!value) return ""; value = value.toString(); return value.charAt(0).toUpperCase() + value.slice(1); });
new Vue({ });
|
当全局过滤器和局部过滤器重名时,会采用局部过滤器。
过滤器可以串联
1 2 3 4 5
| { { message | filterA | filterB; } }
|
在这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到 filterB 中。
过滤器可以接收参数:
1 2 3 4 5
| { { message | filterA("arg1", arg2); } }
|
这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 ‘arg1’ 作为第二个参数,表达式 arg2 的值作为第三个参数。