在Vue.js中使用Mixin

有一种很常见的情况:有两个非常相似的组件,他们的基本功能是一样的,但他们之间又存在着足够的差异性,此时的你就像是来到了一个分岔路口:我是把它拆分成两个不同的组件呢?还是保留为一个组件,然后通过props传值来创造差异性从而进行区分呢?

两种解决方案都不够完美:如果拆分成两个组件,你就不得不冒着一旦功能变动就要在两个文件中更新代码的风险,这违背了 DRY
原则。反之,太多的props传值会很快变得混乱不堪,从而迫使维护者(即便这个人是你)在使用组件的时候必须理解一大段的上下文,拖慢写码速度。

使用Mixin。Vue 中的Mixin对编写函数式风格的代码很有用,因为函数式编程就是通过减少移动的部分让代码更好理解(引自
Michael Feathers
)。Mixin允许你封装一块在应用的其他组件中都可以使用的函数。如果使用姿势得当,他们不会改变函数作用域外部的任何东西,因此哪怕执行多次,只要是同样的输入你总是能得到一样的值,真的很强大!

基础实例

我们有一对不同的组件,它们的作用是通过切换状态(Boolean类型)来展示或者隐藏模态框或提示框。这些提示框和模态框除了功能相似以外,没有其他共同点:它们看起来不一样,用法不一样,但是逻辑一样。


  1. // 模态框 
  2.  
  3. const Modal = { 
  4.  
  5.   template: '#modal', 
  6.  
  7.   data() { 
  8.  
  9.     return { 
  10.  
  11.       isShowing: false 
  12.  
  13.     } 
  14.  
  15.   }, 
  16.  
  17.   methods: { 
  18.  
  19.     toggleShow() { 
  20.  
  21.       this.isShowing = !this.isShowing; 
  22.  
  23.     } 
  24.  
  25.   }, 
  26.  
  27.   components: { 
  28.  
  29.     appChild: Child 
  30.  
  31.   } 
  32.  
  33.  
  34.   
  35.  
  36. // 提示框 
  37.  
  38. const Tooltip = { 
  39.  
  40.   template: '#tooltip', 
  41.  
  42.   data() { 
  43.  
  44.     return { 
  45.  
  46.       isShowing: false 
  47.  
  48.     } 
  49.  
  50.   }, 
  51.  
  52.   methods: { 
  53.  
  54.     toggleShow() { 
  55.  
  56.       this.isShowing = !this.isShowing; 
  57.  
  58.     } 
  59.  
  60.   }, 
  61.  
  62.   components: { 
  63.  
  64.     appChild: Child 
  65.  
  66.   } 
  67.  
  68. }  

我们可以在这里提取逻辑并创建可以被重用的项:


  1. const toggle = { 
  2.  
  3.   data() { 
  4.  
  5.     return { 
  6.  
  7.       isShowing: false 
  8.  
  9.     } 
  10.  
  11.   }, 
  12.  
  13.   methods: { 
  14.  
  15.     toggleShow() { 
  16.  
  17.       this.isShowing = !this.isShowing; 
  18.  
  19.     } 
  20.  
  21.   } 
  22.  
  23.  
  24.   
  25.  
  26. const Modal = { 
  27.  
  28.   template: '#modal', 
  29.  
  30.   mixins: [toggle], 
  31.  
  32.   components: { 
  33.  
  34.     appChild: Child 
  35.  
  36.   } 
  37.  
  38. }; 
  39.  
  40.   
  41.  
  42. const Tooltip = { 
  43.  
  44.   template: '#tooltip', 
  45.  
  46.   mixins: [toggle], 
  47.  
  48.   components: { 
  49.  
  50.     appChild: Child 
  51.  
  52.   } 
  53.  
  54. };  

你可以点击这里,查看 Sarah Drasner(@sdras) 在CodePen上编写 Mixin 的例子

为了更容易理解Mixin,这个例子故意编写得简单一些。真实应用中Mixin有如下的应用,但是它的作用也不仅限于此:获取视窗和组件的尺寸,采集特定的鼠标事件和图表的基本元素。Paul
Pflugradt 有一个关于 Vue Mixins 的优秀项目,值得一提的是它是用 coffeescript 编写的。

用法

上面这个codepen的例子并没有告诉我们在一个真实的应用中如何使用Mixin,所以我们看看下面的这个。

你可以按照你喜欢的任意方式设置你的目录结构,但为了结构规整我喜欢新建一个mixin目录。我们创建的这个文件含有.js扩展名(跟.vue相对,就像我们的其他文件),为了使用Mixin我们需要输出一个对象。

接着我们可以在Modal.vue使用这样的写法,来引入这个Mixin:


  1. import Child from './Child' 
  2.  
  3. import { toggle } from './mixins/toggle' 
  4.  
  5.   
  6.  
  7. export default { 
  8.  
  9.   name: 'modal', 
  10.  
  11.   mixins: [toggle], 
  12.  
  13.   components: { 
  14.  
  15.     appChild: Child 
  16.  
  17.   } 
  18.  
  19. }  

即便我们使用的是一个对象而不是一个组件,生命周期函数对我们来说仍然是可用的,理解这点很重要。我们也可以这里使用mounted()钩子函数,它将被应用于组件的生命周期上。这种工作方式真的很灵活也很强大。

合并

在下面的这个例子,我们可以看到,我们不仅仅是实现了自己想要的功能,并且Mixin中的生命周期的钩子也同样是可用的。因此,当我们在组件上应用Mixin的时候,有可能组件与Mixin中都定义了相同的生命周期钩子,这时候钩子的执行顺序的问题凸显了出来。默认Mixin上会首先被注册,组件上的接着注册,这样我们就可以在组件中按需要重写Mixin中的语句。组件拥有最终发言权。当发生冲突并且这个组件就不得不“决定”哪个胜出的时候,这一点就显得特别重要,否则,所有的东西都被放在一个数组当中执行,Mixin将要被先推入数组,其次才是组件。


  1. //mixin 
  2.  
  3. const hi = { 
  4.  
  5.   mounted() { 
  6.  
  7.     console.log('hello from mixin!') 
  8.  
  9.   } 
  10.  
  11.  
  12.   
  13.  
  14. //vue instance or component 
  15.  
  16. new Vue({ 
  17.  
  18.   el: '#app', 
  19.  
  20.   mixins: [hi], 
  21.  
  22.   mounted() { 
  23.  
  24.     console.log('hello from Vue instance!') 
  25.  
  26.   } 
  27.  
  28. }); 
  29.  
  30.   
  31.  
  32. //Output in console 
  33.  
  34. > hello from mixin! 
  35.  
  36. > hello from Vue instance!  

如果这两个冲突了,我们看看 Vue实例或组件是如何决定输赢的:


  1. //mixin 
  2.  
  3. const hi = { 
  4.  
  5.   methods: { 
  6.  
  7.     sayHello: function() { 
  8.  
  9.       console.log('hello from mixin!') 
  10.  
  11.     } 
  12.  
  13.   }, 
  14.  
  15.   mounted() { 
  16.  
  17.     this.sayHello() 
  18.  
  19.   } 
  20.  
  21.  
  22.   
  23.  
  24. //vue instance or component 
  25.  
  26. new Vue({ 
  27.  
  28.   el: '#app', 
  29.  
  30.   mixins: [hi], 
  31.  
  32.   methods: { 
  33.  
  34.     sayHello: function() { 
  35.  
  36.       console.log('hello from Vue instance!') 
  37.  
  38.     } 
  39.  
  40.   }, 
  41.  
  42.   mounted() { 
  43.  
  44.     this.sayHello() 
  45.  
  46.   } 
  47.  
  48. }) 
  49.  
  50.   
  51.  
  52. // Output in console 
  53.  
  54. > hello from Vue instance! 
  55.  
  56. > hello from Vue instance!  

你可能已经注意到这有两个console.log而不是一个——这是因为第一个函数被调用时,没有被销毁,它只是被重写了。我们在这里调用了两次sayHello()函数。

全局Mixin

当我们使用“全局”来描述Mixin的时候,我们并不是说Mixin能够像filter,在每个组件都能被访问到。只是我们能够在组件通过mixins:[toggle]访问组件上的Mixin对象。

全局Mixin被注册到了每个单一组件上。因此,它们的使用场景极其有限并且在使用的时候我们需要非常小心。一个我能想到的用途就是类似于插件,你需要赋予它访问所有东西的权限。但即使在这种情况下,我也对你正在做事情的充满警惕,尤其当你打算为应用增加通能的时候,这样做可能对你来说是个潘多拉的盒子。

为了创建一个全局实例,我们可以把它放在Vue实例之上。在一个典型的 Vue-cli 初始化的项目中,它可能在你的main.js文件中。


  1. Vue.mixin({ 
  2.  
  3.   mounted() { 
  4.  
  5.     console.log('hello from mixin!') 
  6.  
  7.   } 
  8.  
  9. }) 
  10.  
  11.   
  12.  
  13. new Vue({ 
  14.  
  15.   ... 
  16.  
  17. })  

再次提醒,小心使用它!那个console.log将会出现在每个组件上,在这个案例里还不算坏(除了控制台上有多余的输出)。但如果全局Mixin被错误的使用,你将能看到它有多可怕。

结论

Mixin对于封装一小段想要复用的代码来讲是有用的。对你来说Mixin当然不是唯一可行的选择:比如说高阶组件就允许你组合相似函数,Mixin只是的一种实现方式。我喜欢Mixin,因为我不需要传递状态,但是这种模式当然也可能会被滥用,所以,仔细思考下哪种选择对你的应用最有意义吧!

作者:佚名

来源:51CTO

时间: 2024-11-03 18:07:37

在Vue.js中使用Mixin的相关文章

JavaScript Event Loop机制详解与Vue.js中nextTick的实践应用

本文依次介绍了函数调用栈.MacroTask 与 MicroTask 执行顺序.浅析 Vue.js 中 nextTick 实现等内容;本文中引用的参考资料统一声明在 JavaScript 学习与实践资料索引. 1. 事件循环机制详解与实践应用 JavaScript 是典型的单线程单并发语言,即表示在同一时间片内其只能执行单个任务或者部分代码片.换言之,我们可以认为某个同域浏览器上下中 JavaScript 主线程拥有一个函数调用栈以及一个任务队列(参考 whatwg 规范);主线程会依次执行代码

Vue.js中数组变动的检测详解_javascript技巧

前言 最近在尝试用Vue.js重构公司的现有业务代码,组件化的设计思路和MVVM的思想让我深深沉迷于其中.但是是踩到了不少坑,就比如这篇文章介绍的数组绑定后的更新检测. 相信大家都知道Observer,Watcher,vm 可谓 Vue 中比较重要的部分,检测数据变动后视图更新的重要环节.在 vue.js中$watch的用法示例 中,我们讨论了如何实现基本的 watch . 接下来,我们来看看如何实现数组变动检测. 例子: // 创建 vm let vm = new Vue({ data: {

学习vue.js中class与style绑定_javascript技巧

关于vue.js中class与style绑定的练习代码,分享给大家,供大家参考: html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vuejs中Class与Style 绑定</title> <link rel="stylesheet" href="css/vu

前端框架Vue.js中Directive知识详解_javascript技巧

Directive 看上去虽然和Angular中的定义类似,Directive 都是对DOM功能的一种拓展,但是 Vue 的 Directive 要弱的多.因为 Vue Component 其实本来就会包含对DOM的操作,所以大多数时候我们写一个通用组件都是一个Component 而不是一个 Directive,而 在 Angular 我们写一个通用的组件一般都是一个 Directive . 所以我说 Vue 的 Directive 相比于 Angular 要弱的多,也可以说纯粹的多,他就是对

vue.js中$watch的用法示例_javascript技巧

前言 vue.js是一个数据驱动的web界面库.Vue.js只聚焦于视图层,可以很容易的和其他库整合.代码压缩后只有24kb Vue.js 提供了一个方法 watch,它用于观察Vue实例上的数据变动.对应一个对象,键是观察表达式,值是对应回调.值也可以是方法名,或者是对象,包含选项. 在实例化时为每个键调用 $watch() ; <template> //观察数据为字符串或数组 <input v-model="example0"/> <input v-m

vue.js中sort-table使用示例

smart-table封装了table的几个基础功能.咋们还需要给它实现排序.ajax接口.分页呢,so继续改造smart-table.但是一味的往上面添加功能,a.b.c.d-吗?NO!NO!NO!那样做会弄出一个庞然大物.在以前,我会找一些开源的jq插件来快速开发页面.有些插件真的很强大而且好用,体积也大,它的大部分其他功能我用不上.这让天秤座的我很纠结,我不喜欢多余的代码. 自己怎么能让自己难受呢?我认为理想中的组件应该这样: 以功能的粒度用定义组件,让组件可以自由组合.比方说有组件a孙悟

vue.js中smart-table基础篇

第一步 分析 产品里面的table要满足如下功能: 固定的头部 按照头部列内容排列一行一行的展示数据 table需要实现分页查询 table可以根据条件查询内容 我们开发肯定知道源数据可能是1.2这种key,需要用数据字典把key转化为说明文字 特殊的操作列,内容是按钮,并且能触发动作 仔细想想,其实还有: 设置列的宽度 设置列的text-align 点击列的title排序table 其他 第二步 动手做 想再多,不去动手做肯定不行.切忌考虑太多,不需要考虑清楚怎么实现去所有功能,只需要按照你的

用v-html解决Vue.js渲染中html标签不被解析的问题_javascript技巧

前言 最近在工作中遇到一个问题,在网页中后台传来的json数据中包含html标签,将该json数据绑定到Vue.js中对象中,对该对象进行for循环,发现数据中的html标签不能被解析,而是当作字符显示出来. 问题如下所示: 解决方法: Vue.js中提供了v-html这个指令来解决这个问题,或者对数据对象使用{{{vm.data}}}三个大括号来包裹对象,就可以正常解析了. 代码改动如下: 总结 以上就是这篇文章的全部内容了,希望本文的内容对大家都 学习或者工作能带来一定的帮助,如果有疑问大家

怎么用v-html解决Vue.js渲染中html标签不被解析的教程

前言 最近在工作中遇到一个问题,在网页中后台传来的json数据中包含html标签,将该json数据绑定到Vue.js中对象中,对该对象进行for循环,发现数据中的html标签不能被解析,而是当作字符显示出来. 问题如下所示: 解决方法: Vue.js中提供了v-html这个指令来解决这个问题,或者对数据对象使用{{{vm.data}}}三个大括号来包裹对象,就可以正常解析了. 代码改动如下: 总结 以上就是这篇文章的全部内容了,希望本文的内容对大家都 学习或者工作能带来一定的帮助,如果有疑问大家