原文链接: Using Mixins in Vue.js
vue文档: mixins文档链接
一个很常见场景:
你有两个非常相似的组件, 它们拥有非常相似的基本功能, 但是它们之间又有足够的不同的地方, 该如何选择呢? 我们是应该将它们分成两个完全不同的组件呢? 还是创建一个基础组件, 然后定义足够多的props
以方便区分使用场景?
这两种方式都不是完美的: 如果你将它们分成两个完全不同的组件, 在需求变化(功能变化)时, 可能会增加需要同时修改两个组件的风险, 这违反了"DRY"
的前提. 另一方面, 太多的props
很快会让人变得凌乱, 并且, 迫使维护人员, 甚至是你自己, 要首先理解这些props
的上下文才能使用它, 这会让人非常失望.
Vue
的Mixins
是非常实用的编程方式, 因为最终实用的编程是通过不断减少运动部件(moving parts)使代码变得容易理解.一个mixin
允许你封装一个功能, 以便你能在整个应用程序中的不同组件中使用它.
基础实例
假设我们有一些不同的组件, 它们的工作是切换状态boolean
, 一个模态(modal
)和一个提示(tooltip
). 这些tooltips
和modals
没有很多共同之处, 除了这个功能: 它们看起来不一样, 它们使用起来也不尽相同, 但是它们的逻辑是相似的.
//modal
const Modal = {
template: '#modal',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
},
components: {
appChild: Child
}
}
//tooltip
const Tooltip = {
template: '#tooltip',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
},
components: {
appChild: Child
}
}
我们可以从中提取逻辑, 并创建可以复用的部分:(在CODEPEN中查看效果)
const toggle = {
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
}
}
const Modal = {
template: '#modal',
mixins: [toggle],
components: {
appChild: Child
}
};
const Tooltip = {
template: '#tooltip',
mixins: [toggle],
components: {
appChild: Child
}
};
查看 Sarah Drasner(@sdras) 在CodePen上编写 混合的例子
为了更容易理解混合,这个例子故意编写的简单一些。真实应用中使用混合的有,包含但不限于:获取视窗和组件的尺寸,采集特定的鼠标事件和图表的基本元素。
使用
您可以设置您的目录结构以任何您喜欢的方式, 但我喜欢创建一个单独的mixin目录以方便管理它们. 我们将创建的文件将具有".js"扩展名(而不是.vue, 就像我们其他的文件), 然后我们将导出(export)一个对象为mixin:
![](https://img.haomeiwen.com/i4307840/b1c945bf61196462.png)
然后, 在modal.vue里, 我们可以通过import刚才的toggle来访问它, 就像这样:
import { toggle } from './mixins/toggle'
export default {
name: 'modal',
mixins: [toggle],
components: {
appChild: Child
}
}
很重要的需要意识到的一点是, 尽管如此, 我们使用的是一个对象(object)而不是一个组件(component), 生命周期(lifecycle)内的方法(methods)是可用的. 我们可以挂载(hook)到mounted(), 这样它就会被应用于组件的生命周期, 使得这种方法非常的灵活和强大
合并(Merging)
看看最后一个例子, 我们发现, 我们不仅仅拥有我们的功能, 生命周期的钩子(hooks)也可用于mixin, 所以, 当我们将它应用与具有重叠进程(overlapping processes)的时候, 顺序很重要. 默认情况下, mixins将首先被调用, 然后是组件, 所以我们可以根据需要来覆盖它(override). 就是说, 组件有最后的发言权. 这只有当冲突发生时才变得非常重要, 在这个时候, 组件必须决定哪一个胜出, 否则一切将被放置在一个数组中执行, mixins相关的放在前面, 然后是组件相关的.
//mixin
const hi = {
mounted() {
console.log('hello from mixin!')
}
}
//vue instance or component
new Vue({
el: '#app',
mixins: [hi],
mounted() {
console.log('hello from Vue instance!')
}
});
//Output in console
> hello from mixin!
> hello from Vue instance!
如果两个冲突, Vue实例和组件将胜出:
//mixin
const hi = {
methods: {
sayHello: function() {
console.log('hello from mixin!')
}
},
mounted() {
this.sayHello()
}
}
//vue instance or component
new Vue({
el: '#app',
mixins: [hi],
methods: {
sayHello: function() {
console.log('hello from Vue instance!')
}
},
mounted() {
this.sayHello()
}
})
// Output in console
> hello from Vue instance!
> hello from Vue instance!
您可能已经注意到, 在Vue实例中, 我们有两个console.log打印而不是一个. 这是因为, mixin解析以后, 在Vue实例中的mounted()里面会有两个this.sayHello()调用, 但是由于mixin中的sayHello函数会在Vue实例的sayHello函数之前, 因此被Vue实例中的sayHello函数覆盖了. 所以, mounted()中调用的两次sayHello实际上都是Vue实例中的sayHello函数.
全局Mixins
当我们使用术语global参考mixins时, 我们不是指能够在每个组件访问它们, 就像过滤器一样. 我们已经可以在组件中访问mixins以这种方式: mixins: [toggle].
Global mixins如字面上的意思一样, 会被应用于所有的组件. 因此, 它们的用例非常有限, 应该慎重考虑. 我可以想到的能够合理使用的一个场景是像插件一样的东西, 你可能需要访问一切. 但是, 即使在这种情况下, 我也会对您正在应用的内容感到担心, 特别是当您将功能扩展到可能是黑匣子的应用程序时.
要创建全局实例, 我们应将其放在Vue实例之前. 在典型的Vue-cli构建中, 这将在您的main.js文件中.
Vue.mixin({
mounted() {
console.log('hello from mixin!')
}
})
new Vue({
...
})
还是那句话, 谨慎使用这种方式的mixin! 现在, mixin中的console.log将会出现在每一个单独的组件中. 在这种情况下还不是很糟(除了控制台里面多了许多噪音外). 但是您会发现, 如果使用不当, 将会带来多大的危害.
结论
混合对于封装一小段想要复用的代码来讲是有用的。对你来说它们当然不是唯一可行的选择:高阶组件,例如,允许组合相似函数,这只是实现的一种方式。我喜欢混合,因为我不需要传递状态,但是这种模式当然也可能会被滥用,所以,仔细思考哪种选择对你的应用最有意义。
网友评论