Vue 面试题常问:了解 Vue 中的 Mixin 吗?
什么是 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 组件中的一些常见配置,如
data
、methods
、created
等等。 - 在项目的 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 。