Vue中的Mixins
在项目开发的时候,常会碰到这样的一种现象:有两个组件非常相似,比如较为熟悉的 Modal 、 Tooltip 和 Popover ,它们都具有同样的基本函数,而且它们之前也有足够的不同。很多时候,就让人很难做出选择:是把它们拆会成多个不同的组件呢?还是只使用一个组件,创建足够的属性来改变不同的情况。
这些解决方案都不够完美。如果拆会成多个组件,就不得不冒着如果功能变动你要在多个文件中更新它的风险。另一方面,太多的属笥会很快变得混乱,难维护,甚至对于组件开发者自已面言,也是件难事。
在Vue中,对于这样的场景,官方提供了一种叫混入 (mixins) 的特性。使用 mixins 允许你封装一块在应用的其他组件中都可以使用的函数。如果被正确的使用,他们不会改变函数作用域外部的任何东西,所以多次执行,只要是同样的输入,总是能得到一样的值。
既然Vue的 mixins 这么优秀,那应该怎么使用呢?今天我们的目的就是学习如何在Vue的项目中使用 mixins 。
什么是Mixins
Vue的官方文档是这样描述 mixins 的:mixins
是一种分发Vue组件中可复用功能的一种灵活方式。
mixins 是一个JavaScript对象,可以包含组件中的任意选项,比如Vue实例中生命周期的各个钩子函数,也可以是 data 、 components 、 methods 或 directives 等。在Vue中, mixins 为我们提供了在Vue组件中共用功能的方法。使用方式很简单,将共用的功能以对象的方式传入 mixins 选项中。当组件使用 mixins 对象时,所有 mixins 对象的选项都将被混入该组件本身的选项。
如何创建Mixins
在Vue中,创建 mixins 方法很简单。简单一点的,在Vue组件中,我们将 mixins 对象指定为:
1 const myMixin = { 2 created () { 3 console.log(`来自Mixins中的消息`) 4 } 5 } 6 7 let app = new Vue({ 8 el: '#app', 9 mixins: [myMixin] 10 })
上面看到的方式是在组件中声明一个 mixins 对象。如果组件多起来,这样的方式,感觉还是没有解决我们文章提到的需求一样。更好的做法,我们可以像下面这样做。比如在项目的src
下面创建一个存放 mixins 的目录,比如一个叫 mixin 目录。然后这个目录中创建你想要的.js
文件,比如这里创建了一个叫 mixins.js :
为了方便演示,把上面创建的 mixins 相关的代码移入 mixins.js 文件中:
1 // src/mixins/mixins.js 2 export const myMixin = { 3 created() { 4 console.log(`来自Mixins中的消息`); 5 } 6 };
在需要使用该 mixins 的组件中先引入 mixins.js 文件,接着在组件中的 mixins 选择中调用在 mixins.js 创建的 mixins ,比如:
// HelloWorld.vue <script> import { myMixin, myMixin2 } from "../mixins/mixins.js"; export default { name: "HelloWorld", data() { return { msg: "From Vue Component" }; }, mixins: [myMixin] }; </script>
这种方式和前面使用 mixins 方式最终得到的结果是一样的。
使用 mixins ,在 mixins 中指定的任何选项都会立即与Vue组件的选项合并。可以在组件的 mixins 数组中指定多个 mixin 。一旦指定了,所有的 mixin 选项都会被导入到组件中。比如:
1 // mixins.js 2 export const mixObj = { 3 created() { 4 this.hello() 5 }, 6 methods: { 7 hello() { 8 console.log(`Hello from Mixin`) 9 } 10 } 11 } 12 export const minObjAnother = { 13 created () { 14 this.prompt() 15 }, 16 methods: { 17 prompt() { 18 console.log(`Prompt from Mixin Another`) 19 }, 20 hello() { 21 console.log(`Hello from Mixin Another`) 22 } 23 } 24 }
如果在组件中要同时调用 mixObj 和 minObjAnother 两个 mixins 时,需要在组件的 mixins 选项的值添加这两个 mixins ,比如:
1 <!-- HelloWorld.vue --> 2 <script> 3 import { mixObj, minObjAnother } from "../mixins/mixins.js"; 4 5 export default { 6 name: "HelloWorld", 7 mixins: [mixObj, minObjAnother], 8 }; 9 </script>
此时在 console 中输出的结果如下:
从输出的结果中我们可以看出, mixObjAnother 中的 hello() 方法取替了 mixObj 中的 hello() 方法。
是不是感到疑惑。如果是,请继续往下阅读。
合并选项和冲突
当组件和混入对象含有同名选项时,这些选项将以恰当的方式混合。
什么是“ 恰当的方式 ”?对于我这样的初学者而言,有点难于理解。上例中我们看到了,每个 mixins 对象中都具有生命周期的钩子(Vue生命周期)和方法(甚至其他的),而且这些东西都是可用的。另外在Vue中的组件,同样也有这些生命周期的钩子函数和方法。所以当在组件上注册重复的过程时,顺序显得尤其重要。
默认是 mixins 上会首先被注册,组件上的接着注册!
这样的做主要是因为必要时可以重写。
组件拥有最终发言权: 当有一个冲突并且这个组件不得不决定哪个胜出的时候,这真的变得很重要,否则,所有东西都被放在一个数组中执行, mixins 中的先执行,组件中的接着执行。
在Vue中 mixins 常见的合并主要有三种情形:数据 data 、生命周期中的钩子函数和值为对象的选项。接下来,咱们分别来看看这三种情形:
数据合并
数据对象在内部分进行浅合并(一层属性深度),在和组件的数据发生冲突时, 以组件数据优先 。
1 // mixins.js 2 export const mixObj = { 3 data() { 4 return { 5 msg: "Message from Mixin Object" 6 }; 7 } 8 }; 9 export const minObjAnother = { 10 data() { 11 return { 12 msg: "Message from Mixin Object Another", 13 name: "Foo" 14 }; 15 } 16 }; 17 18 <!-- HelloWorld.vue --> 19 <script> 20 import { mixObj, minObjAnother } from "../mixins/mixins.js"; 21 22 export default { 23 name: "HelloWorld", 24 data() { 25 return { 26 msg: "Message from Vue Component", 27 name: 'Bar', 28 age: 20 29 }; 30 }, 31 mixins: [mixObj, minObjAnother], 32 created () { 33 console.log(this.$data) 34 } 35 }; 36 </script>
最终的结果是, HelloWorld
组件中的 msg
和 name
取替了 mixins
中的,另外 age
字段被合并进行。
钩子函数合并
当组件使用的 mixins 对象有相同的选项时,比如钩子函数,就会全部被合并到一个数组中,因此都会被执行,并且
mixins `对象中的钩子会先被执行。
1 const myMixin = { 2 created () { 3 console.log(`来自Mixins中的消息`) 4 } 5 } 6 7 new Vue({ 8 el: '#app', 9 mixins: [myMixin], 10 created () { 11 console.log(`来自组件中的消息`) 12 } 13 })
值为对象选项合并
当 mixins
对象和组件中的选项的值为对象时,比如 methods 、 components 和 directives ,将被混合为同一个对象。当两个对象键名冲突时, 组件选项优先 。
1 const myMixin = { 2 data () { 3 return { 4 msg: 'From Vue Mixins' 5 } 6 }, 7 created() { 8 console.log(`来自Mixins的消息`) 9 }, 10 methods: { 11 message () { 12 console.log(this.msg) 13 } 14 } 15 }; 16 export default { 17 name: "HelloWorld", 18 data() { 19 return { 20 msg: "From Vue Component" 21 }; 22 }, 23 mixins: [myMixin], 24 created() { 25 console.log(`来自组件的消息`) 26 }, 27 methods: { 28 message () { 29 console.log(this.msg) 30 } 31 } 32 };
全局Mixins
当我们使用全局混合时,我们不是指能够在每个组件上访问它们,就像是过滤器一样。我们能够通过 mixins:[myMixins] 访问组件上的混合对象。
全局混合被注册到了每个单一组件上。因此,它们的使用场景极其有限并且要非常的小心。一个我能想到的用途就是它像一个插件,你需要赋予它访问所有东西的权限。但即使在这种情况下,我也对你正在做的保持警惕,尤其是你在应用中扩展的函数,可能对你来说是不可知的
为了创建一个全局实例,我们可以把它放在 Vue 实例之上。在一个典型的 Vue-cli 初始化的项目中,它可能在你的 main.js 文件中。
1 Vue.mixin({ 2 mounted() { 3 console.log('hello from mixin!') 4 } 5 }) 6 7 new Vue({ 8 ... 9 })
谨慎使用全局混入对象,因为会影响到每个单独创建的 Vue 实例 (包括第三方模板)。大多数情况下,只应当应用于自定义选项,就像上面示例一样。也可以将其用作 Plugins 以避免产生重复应用。
使用Mixins实例
经过上面的介绍,我们对Vue中的 mixins 应该有了一定的了解。接下来通过简单的实例来向大家阐述在项目中怎么使用上面介绍的 mixins 。
先来看一个简单的示例,也就是文章中提到的 Modal 和 Tooltip 组件。虽然这两个组件是不同的组件,但实际上它们的作用是类似的: 切换一个状态布尔值 。换句话说,这两个组件表面上看起来不一样,用法不一样,但逻辑是一样的。
1 // 模态框 2 const Modal = { 3 template: '#modal', 4 data() { 5 return { 6 isShowing: false 7 } 8 }, 9 methods: { 10 toggleShow() { 11 this.isShowing = !this.isShowing; 12 } 13 }, 14 components: { 15 appChild: Child 16 } 17 } 18 19 // 提示框 20 const Tooltip = { 21 template: '#tooltip', 22 data() { 23 return { 24 isShowing: false 25 } 26 }, 27 methods: { 28 toggleShow() { 29 this.isShowing = !this.isShowing; 30 } 31 }, 32 components: { 33 appChild: Child 34 } 35 }
我们可以提取出这个逻辑并创建可以被重用的项:
1 // toggle.js 2 export const toggle = { 3 data() { 4 return { 5 isShowing: false 6 }; 7 }, 8 methods: { 9 toggleShow() { 10 this.isShowing = !this.isShowing; 11 } 12 } 13 }; 14 15 <!-- Modal.vue --> 16 <template> 17 <div> 18 <h3>Let's trigger this here modal!</h3> 19 <button @click="toggleShow"> 20 <span v-if="isShowing">Hide child</span> 21 <span v-else>Show child</span> 22 </button> 23 <Child v-if="isShowing" class="modal"> 24 <button @click="toggleShow">Close</button> 25 </Child> 26 </div> 27 </template> 28 29 <script> 30 import Child from "./Child"; 31 import { toggle } from "../mixins/toggle.js"; 32 33 export default { 34 name: "Modal", 35 mixins: [toggle], 36 components: { 37 Child 38 } 39 }; 40 </script> 41 42 <!-- ToolTip.vue --> 43 <template> 44 <div class="tooltip-demo"> 45 <h3 @click="toggleShow"> 46 <span v-if="isShowing">Click Me Again</span> 47 <span v-else>Click Me Please</span> 48 </h3> 49 <Child v-if="isShowing" class="tooltip"> 50 <p>I'm a tooltip this time</p> 51 </Child> 52 </div> 53 </template> 54 55 <script> 56 import { toggle } from '../mixins/toggle.js' 57 import Child from './Child' 58 59 export default { 60 name: 'ToolTip', 61 mixins: [toggle], 62 components: { 63 Child 64 } 65 } 66 </script>
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
原文: https://www.w3cplus.com/vue/vue-mixins.html © w3cplus.com著作权归作者所有。