14、Vue Mixin 源码分析与使用场景详解
·
目录
一、Mixin 源码分析
1. 核心源码位置
Vue 2.x 中 mixin 的核心代码位于 src/core/global-api/mixin.js 和 src/core/util/options.js
2. 源码实现
// src/core/global-api/mixin.js
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
// 将传入的 mixin 对象与 Vue.options 进行合并
this.options = mergeOptions(this.options, mixin)
return this
}
}
3. 合并策略源码(核心)
// src/core/util/options.js
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
// ...
const options = {}
let key
// 遍历父选项
for (key in parent) {
mergeField(key)
}
// 遍历子选项
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key)
}
}
function mergeField (key) {
// 根据不同的选项使用不同的合并策略
const strat = strats[key] || defaultStrat
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
4. 不同选项的合并策略
// 生命周期钩子合并策略 - 合并成数组
strats.created =
strats.mounted =
strats.beforeMount = function (parentVal, childVal) {
return childVal
? parentVal
? parentVal.concat(childVal) // 都存在,合并成数组
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
}
// data 合并策略 - 递归合并,组件优先
strats.data = function (parentVal, childVal, vm) {
return mergeDataOrFn(parentVal, childVal, vm)
}
// methods、props、computed 合并策略 - 组件覆盖 mixin
strats.methods =
strats.props =
strats.computed = function (parentVal, childVal) {
if (!parentVal) return childVal
const ret = Object.create(null)
extend(ret, parentVal)
if (childVal) extend(ret, childVal) // 子组件会覆盖
return ret
}
// watch 合并策略 - 合并成数组
strats.watch = function (parentVal, childVal) {
// 合并成数组,都会执行
if (!childVal) return Object.create(parentVal || null)
if (!parentVal) return childVal
const ret = {}
extend(ret, parentVal)
for (const key in childVal) {
let parent = ret[key]
const child = childVal[key]
if (parent && !Array.isArray(parent)) {
parent = [parent]
}
ret[key] = parent
? parent.concat(child)
: Array.isArray(child) ? child : [child]
}
return ret
}
二、合并策略总结
| 选项类型 | 合并策略 | 执行顺序 |
|---|---|---|
| 生命周期钩子 | 合并成数组 | mixin 先执行,组件后执行 |
| data | 递归合并对象 | 组件数据优先(覆盖 mixin) |
| methods/computed/props | 对象合并 | 组件选项覆盖 mixin |
| watch | 合并成数组 | 都会执行 |
| components/directives/filters | 对象合并 | 组件选项覆盖 mixin |
三、使用场景
1. 提取公共逻辑
// 权限检查 mixin
export const permissionMixin = {
methods: {
hasPermission(permission) {
return this.$store.getters.permissions.includes(permission)
},
checkPermission(permission) {
if (!this.hasPermission(permission)) {
this.$message.error('无权限访问')
this.$router.push('/403')
}
}
}
}
2. 页面埋点统计
export const trackMixin = {
mounted() {
// 页面访问埋点
this.$track('page_view', {
page: this.$route.path,
title: document.title
})
},
beforeDestroy() {
// 页面离开埋点
this.$track('page_leave', {
page: this.$route.path,
duration: Date.now() - this.enterTime
})
},
data() {
return {
enterTime: Date.now()
}
}
}
3. 列表页通用逻辑
export const listMixin = {
data() {
return {
list: [],
loading: false,
pagination: {
current: 1,
pageSize: 10,
total: 0
}
}
},
methods: {
async fetchList() {
this.loading = true
try {
const { data, total } = await this.getListApi(this.pagination)
this.list = data
this.pagination.total = total
} finally {
this.loading = false
}
},
handlePageChange(page) {
this.pagination.current = page
this.fetchList()
}
},
mounted() {
this.fetchList()
}
}
4. 表单验证
export const formMixin = {
data() {
return {
formLoading: false
}
},
methods: {
async submitForm(formName) {
return new Promise((resolve, reject) => {
this.$refs[formName].validate((valid) => {
if (valid) {
resolve()
} else {
reject(new Error('表单验证失败'))
}
})
})
},
resetForm(formName) {
this.$refs[formName].resetFields()
}
}
}
四、面试回答模板
问题1:什么是 Vue Mixin?
回答:
Mixin 是 Vue 提供的一种代码复用机制,可以将组件的公共逻辑抽离出来。Mixin 对象可以包含任意组件选项(data、methods、生命周期等),当组件使用 mixin 时,mixin 的选项会被"混入"到组件自身的选项中。
问题2:Mixin 的合并策略是什么?
回答:
Vue 对不同类型的选项采用不同的合并策略:
- 生命周期钩子:合并成数组,mixin 的钩子先执行,组件的后执行
- data 数据:递归合并,组件数据优先,会覆盖 mixin 中同名属性
- methods/computed/props:对象合并,组件选项会覆盖 mixin
- watch:合并成数组,都会执行
源码中通过
mergeOptions函数实现,针对不同选项类型使用strats策略对象中定义的合并函数。
问题3:Mixin 的优缺点?
回答:
优点:
- 提高代码复用性,减少重复代码
- 便于维护,公共逻辑集中管理
- 灵活性高,可以混入多个 mixin
缺点:
- 命名冲突:多个 mixin 可能有同名属性/方法
- 依赖不明确:组件依赖的数据来源不清晰
- 滥用导致维护困难:过多 mixin 会让代码难以追踪
现代替代方案:
- Vue 3 推荐使用 Composition API(组合式 API)
- 使用
composables函数替代 mixin,依赖关系更清晰
问题4:实际项目中如何使用 Mixin?
回答:
在实际项目中,我主要在以下场景使用 mixin:
- 列表页通用逻辑:分页、加载、刷新等
- 权限控制:统一的权限检查方法
- 埋点统计:页面访问、用户行为追踪
- 表单处理:验证、提交、重置等通用方法
但现在新项目中,我更倾向使用 Vue 3 的 Composition API,通过自定义 hooks(composables)来实现代码复用,因为它更灵活、依赖更清晰。
问题5:全局 Mixin 和局部 Mixin 的区别?
回答:
// 全局 mixin - 影响所有组件
Vue.mixin({
created() {
console.log('全局 mixin')
}
})
// 局部 mixin - 只影响当前组件
export default {
mixins: [myMixin],
// ...
}
全局 mixin 会影响所有 Vue 实例,包括第三方组件,应谨慎使用。通常只在插件开发或全局配置时使用。局部 mixin 只影响引入它的组件,更加安全可控。
五、Vue 3 替代方案
// 使用 Composition API 替代 mixin
import { ref, onMounted } from 'vue'
export function useList(api) {
const list = ref([])
const loading = ref(false)
const pagination = ref({
current: 1,
pageSize: 10,
total: 0
})
const fetchList = async () => {
loading.value = true
try {
const { data, total } = await api(pagination.value)
list.value = data
pagination.value.total = total
} finally {
loading.value = false
}
}
onMounted(() => {
fetchList()
})
return {
list,
loading,
pagination,
fetchList
}
}更多推荐


所有评论(0)