Vue 面试题常问:了解 Vue 中的 Mixin 吗?

413 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

什么是 Mixin?

  • 官方解释

    混入(mixin)提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

  • 自己理解

    将组件的公共逻辑或者配置提取出来,哪个组件需要用到时,直接将提取的这部分混入到组件内部即可。这样既可以减少代码冗杂度,也可以让后期维护起来更加容易。

    需要注意的是:提取的是逻辑或者配置,而不是 HTML 代码和 CSS 代码。可以将 Mixin 简单理解为组件中的组件,Vue 组件化让代码复用性更高,那么组件与组件之间还有重复部分,就可以用 Mixin 再抽离一遍。

  • 举例

    当我们在使用 Vue 开发后台管理系统的时候,一定会使用到表格(用户数据,订单数据等等)来对数据进行展示,但是每一个表格的数据都需要向后台发送请求来获取表格的数据,而发送请求的逻辑代码如果每一个 Table 页面都去写一下的话,重复的代码就会有很多,这是我们就可以将发送请求获取数据的逻辑代码抽离出来,放到 Mixin 中,从而混入到需要获取数据的 Table 页面中。

     export const TableListMixin = {
       data() {
         return {
           // 数据源
           dataSource: [],
           // table加载状态
           loading: false,
           // 分页参数
           ipagination: {
             current: 1,
             pageSize: 10,
             currentTotal: 0,
             pageSizeOptions: ['10', '20', '30'],
             showTotal: (total, range) => {
               return range[0] + "-" + range[1] + " 共" + total + "条"
             },
             showQuickJumper: true,
             showSizeChanger: true,
             total: 0,
             onShowSizeChange: (current, pageSize) => this.handleSizeChange(current, pageSize),
           },
         }
       },
       loadData(arg) {
         let that = this
         if (!this.url.list) {
           this.$message.error("请设置url.list属性!")
           return
         }
         //加载数据 若传入参数1则加载第一页的内容
         if (arg === 1) {
           this.ipagination.current = 1;
         }
         var params = this.getQueryParams();//查询条件
         // getQueryParams() 是获取查询条件的方法,此处就不具体列出了
         this.loading = true;
         if (this.isNoPageTotal) {
           params.pageSize = 99999999
         }
         getAction(this.url.list, params).then((res) => {
           if (res.success) {
             this.dataSource = res.result.records || res.result;
             if (res.result.total) {
               this.ipagination.total = res.result.total;
             } else {
               this.ipagination.total = 0;
             }
           } else {
             this.$message.warning(res.message)
           }
         }).finally(() => {
           this.loading = false
         })
       },
     }
    

    在项目中的 Mixin 中添加了上述代码,我们就可以在 Table 页面获取数据的时候直接混入,并在 data 中添加一个对象:url: { list: '' } 对象,list 的值对应需要调用的接口地址即可。一套代码适用所有 Table 页面,是不是很方便?

Mixin 和 Vuex 的区别?

  • Mixin 具有抽离公共部分的作用。在 Vue 中,Vuex 状态管理似乎也有这样的功能,Vuex 也是将组件之间可能共享的数据抽离出来。两者看似一样,实则还是有细微的区别,区别如下:

    • Vuex 公共状态管理,如果在一个组件中更改了 Vuex 中的某个数据,那么其他所有引用了 Vuex 中该数据的组件也会跟着变化。
    • Mixin 中的数据和方法都是独立的,组件之间使用后是互相不影响的。

如何使用?

Mixin 定义

  • 定义 Mixin 也非常简单,它就是一个对象而已,只不过这个对象里面可以包含 Vue 组件中的一些常见配置,如 datamethodscreated 等等。

  • 在项目的 src 目录下新建 mixin 文件夹,然后新建 index.js 文件,该文件存放 Mixin 代码。

     // src/mixin/index.js
     export const mixins = {
         data() {
             return{}
         },
         computed: {},
         created() {},
         mounted() {},
         methods: {}
     }
    

    可以看到 Mixin 非常简单,主要包含了一个 Vue 组件的常见的逻辑结构。

局部混入

 <template>
   <div>
     App 组件
   </div>
 </template>
 ​
 <script>
 import { mixins } from 'src/mixin/index.js'
 export default {
   name: "App",
   mixins: [mixins],
   components: {},
   data() {},
   methods: {}
 }
 </script>
 ​
 <style>
 </style>
  • 上段代码中引入 Mixin 的方法也非常简单,直接使用 Vue 提供的 mixins: [mixins]

  • 总结

    • mixin 中的生命周期函数会和组件的生命周期函数一起合并执行。
    • mixin 中的 data 数据在组件中也可以使用。
    • mixin 中的方法在组件内部可以直接调用。
    • 生命周期函数合并后执行顺序:先执行 mixin 中的,后执行组件的。
  • 问题:一个组件中改动了 mixin 中的数据,另一个引用了 mixin 的组件会受影响吗?

    • 答案是不会的

全局混入

  • 上一点使用 mixin 是在需要的时候在组件中引入,其实也可以在全局先把它注册好,这样就可以在任何组件中直接使用了。

     // main.js
     import Vue from 'vue'
     import App from './App'
     import { mixins } from './mixin/index.js'
     Vue.mixin(mixins)
     ​
     Vue.config.productionTip = false
     ​
     new Vue({
       render: (h) => h(App),
     }).$mount('#app')
    

    虽然全局引入很方便,但是不推荐使用。

    请谨慎使用全局混入,因为它会影响每个单独创建的 Vue 实例(包括第三方组件)。大多数情况下,只应当应用于自定义选项,就像上面的示例一样。推荐将其作为插件发布,以避免重复应用混入。

选项合并

  • mixin 中定义的属性或方法的名称与组件中定义的名称没有冲突。

  • 这里的冲突主要分为以下几种情况:

    • 生命周期函数

      • 确切来说不算冲突,因为生命周期函数的名称都是固定的,默认的合并策略:先执行 mixin 中生命周期函数中的代码,然后执行组件内部的代码。
    • data 数据冲突

      • 当 mixin 中的 data 数据与组件中的 data 数据冲突时,组件中的 data 数据会覆盖 mixin 中的数据
    • 方法冲突

      • 这种冲突很容易遇到,因为协作开发中,很容易命名相同。当方法发生冲突时,默认调用的是组件的方法

Mixin 的优缺点

  • Mixin 虽然好处多多,但是凡是都有两面性。

优点

  • 提高代码复用性
  • 无需传递状态
  • 维护方便,只需要修改一个地方即可

缺点

  • 命名冲突
  • 滥用的话后期很难维护
  • 不好追溯源,排查问题稍显麻烦
  • 不能轻易的重复代码

总结

Mixin 提供了方便的同时也带来了灾难,所以很多时候不建议滥用它,但是在有些场景下使用它又非常合适,这就要自己去判断了。所以在很多时候需要考虑用公共组件还是使用 Mixin 。