vue2 mixin
定义
Mixin 是一种在 Vue 组件中复用代码的方式。我们可以将一个对象(即 mixin 对象)中的数据、方法和生命周期钩子等混入到 Vue 组件中。这样,多个组件就可以共享同一份逻辑代码。
首先,我们定义一个 mixin 对象,其中包含(data、methods、computed)等。比如,我们可以创建一个 commonMixin.js 文件来定义一个 mixin:
// commonMixin.js
export default {
data() {
return {
message: 'Hello from mixin!'
};
},
methods: {
greet() {
console.log(this.message);
}
},
created() {
console.log('Mixin created hook called.');
}
};
组件使用mixin.js
局部使用 mixins:['xxx']
<template>
<div>
<p>{{ message }}</p>
<button @click="greet">Greet</button>
</div>
</template>
<script>
// 导入 mixin
import commonMixin from './commonMixin';
export default {
name: 'HelloWorld',
mixins: [commonMixin], // 使用 mixin
mounted() {
console.log('Component mounted hook called.');
}
};
</script>
在上面的示例中, 组件通过 mixins 选项引入了 commonMixin。这意味着 组件将拥有 commonMixin 中定义的数据、方法和生命周期钩子。
全局使用 Vue.mixin(xxx)
import Vue from 'vue'
import App from './App.vue'
import {commonMixin} from "./mixin/commonMixin.js"
Vue.mixin(commonMixin);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
Mixin 的冲突处理
如果组件和 mixin 中都定义了相同的选项,Vue 将遵循一定的优先级规则来处理这些冲突:
-
数据:如果组件和 mixin 中有相同的
data字段,组件中的data会覆盖 mixin 中的data。 -
方法:如果组件和 mixin 中有同名的方法,组件中的方法会覆盖 mixin 中的方法。
-
生命周期钩子:如果组件和 mixin 中有相同的生命周期钩子(如
created),它们都会被调用,且 mixin 中的钩子会在组件中的钩子之前调用。
// commonMixin.js
export default {
data() {
return {
message: 'Hello from mixin!'
};
},
methods: {
greet() {
console.log('Mixin greet');
}
},
created() {
console.log('Mixin created hook called.');
}
};
// HelloWorld.vue
<template>
<div>
<p>{{ message }}</p>
<button @click="greet">Greet</button>
</div>
</template>
<script>
import commonMixin from './commonMixin';
export default {
name: 'HelloWorld',
mixins: [commonMixin],
data() {
return {
message: 'Hello from component!'
};
},
methods: {
greet() {
console.log('Component greet');
}
},
created() {
console.log('Component created hook called.');
}
};
</script>
在这个例子中,组件中 message 的值会覆盖 mixin 中的值,greet 方法中的实现会覆盖 mixin 中的方法,created 钩子的调用顺序是 mixin 先调用,然后组件中的 created 钩子调用。
使用 Mixin 的注意事项
- 命名冲突:为了避免命名冲突,建议使用明确且独特的命名方式。
- 复杂性:过度使用 mixin 可能会导致代码难以跟踪和调试。可以考虑使用 Vue 的组合式 API 来替代 mixin,以提高代码的可读性和可维护性。
mixin 主要用于以下场景:
1. 共享功能和逻辑
当多个组件需要使用相同的功能或逻辑时,mixin 是一个有效的解决方案。通过将共享的逻辑提取到一个 mixin 中,我们可以避免重复代码。例如,多个组件可能都需要处理表单验证或数据格式化,这时可以将这些功能封装到一个 mixin 中:
js
代码解读
复制代码
// validationMixin.js
export default {
methods: {
validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+.[^\s@]+$/;
return re.test(email);
}
}
};
// UserForm.vue
<template>
<form @submit.prevent="handleSubmit">
<input v-model="email" placeholder="Enter your email" />
<button type="submit">Submit</button>
</form>
</template>
<script>
import validationMixin from './validationMixin';
export default {
mixins: [validationMixin],
data() {
return {
email: ''
};
},
methods: {
handleSubmit() {
if (this.validateEmail(this.email)) {
alert('Email is valid!');
} else {
alert('Email is invalid!');
}
}
}
};
</script>
2. 封装重复的生命周期钩子
有时候,多个组件可能需要在相同的生命周期阶段执行某些操作。例如,所有组件都需要在 created 钩子中初始化数据或进行 API 请求。可以将这些操作封装到 mixin 中:
js
代码解读
复制代码
// dataFetchMixin.js
export default {
created() {
this.fetchData();
},
methods: {
async fetchData() {
// 假设有一个 API 请求
try {
const response = await fetch('https://api.example.com/data');
this.data = await response.json();
} catch (error) {
console.error('Failed to fetch data:', error);
}
}
},
data() {
return {
data: null
};
}
};
// DataComponent.vue
<template>
<div>
<pre>{{ data }}</pre>
</div>
</template>
<script>
import dataFetchMixin from './dataFetchMixin';
export default {
mixins: [dataFetchMixin]
};
</script>
3. 跨组件通信
在 Vue 2 中,mixin 可以用来管理跨组件通信。例如,多个子组件可以通过 mixin 共享父组件传递的数据或方法:
js
代码解读
复制代码
// communicationMixin.js
export default {
methods: {
emitEvent(message) {
this.$emit('custom-event', message);
}
}
};
// ParentComponent.vue
<template>
<div>
<ChildComponent @custom-event="handleEvent" />
</div>
</template>
<script>
import communicationMixin from './communicationMixin';
import ChildComponent from './ChildComponent.vue';
export default {
components: { ChildComponent },
mixins: [communicationMixin],
methods: {
handleEvent(message) {
console.log('Received message:', message);
}
}
};
</script>
// ChildComponent.vue
<template>
<button @click="sendMessage">Send Message</button>
</template>
<script>
import communicationMixin from './communicationMixin';
export default {
mixins: [communicationMixin],
methods: {
sendMessage() {
this.emitEvent('Hello from ChildComponent');
}
}
};
</script>
4. 封装组件的默认行为
对于有相似默认行为的多个组件,可以将这些默认行为封装到 mixin 中。例如,处理表单提交、数据清理等:
js
代码解读
复制代码
// formMixin.js
export default {
methods: {
handleSubmit() {
console.log('Form submitted');
// 处理表单提交逻辑
},
clearForm() {
this.$data = this.$options.data();
}
}
};
// LoginForm.vue
<template>
<form @submit.prevent="handleSubmit">
<!-- 表单内容 -->
<button type="submit">Login</button>
</form>
</template>
<script>
import formMixin from './formMixin';
export default {
mixins: [formMixin]
};
</script>
mixin生命周期和组件生命周期的顺序
mixin beforeCreate
component beforeCreate
mixin created
component created
mixin beforeMount
component beforeMount
mixin mounted
component mounted
mixin beforeUpdate
component beforeUpdate
mixin updated
component updated
mixin beforeDestroy
component beforeDestroy
mixin destroyed
component destroyed
vue 3 hook
定义
本质上它就是一个函数,有点类似于vue2的mixin技术,都是将代码混入组件中。
作用
将重复的逻辑抽离出来,提高代码复用率,在组件里使用hook时,相当于函数里的相关api方法都移入了组件里,让setup的逻辑更加简洁清晰。
使用
useSum.ts中内容如下:
```
import {ref,onMounted} from 'vue'
export default function(){
let sum = ref(0)
const increment = ()=>{
sum.value += 1
}
const decrement = ()=>{
sum.value -= 1
}
onMounted(()=>{
increment()
})
//向外部暴露数据
return {sum,increment,decrement}
}
```
useDog.ts中内容如下:
```
import {reactive,onMounted} from 'vue'
import axios,{AxiosError} from 'axios'
export default function(){
let dogList = reactive<string[]>([])
// 方法
async function getDog(){
try {
// 发请求
let {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// 维护数据
dogList.push(data.message)
} catch (error) {
// 处理错误
const err = <AxiosError>error
console.log(err.message)
}
}
// 挂载钩子
onMounted(()=>{
getDog()
})
//向外部暴露数据
return {dogList,getDog}
}
```
组件中具体使用:
```
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="increment">点我+1</button>
<button @click="decrement">点我-1</button>
<hr>
<img v-for="(u,index) in dogList.urlList" :key="index" :src="(u as string)">
<span v-show="dogList.isLoading">加载中......</span><br>
<button @click="getDog">再来一只狗</button>
</template>
<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
name:'App',
})
</script>
<script setup lang="ts">
import useSum from './hooks/useSum'
import useDog from './hooks/useDog'
let {sum,increment,decrement} = useSum()
let {dogList,getDog} = useDog()
</script>
```
// 引入组合式api
import { reactive } from 'vue'
// 暴露hook函数
export default function () {
// 数据: 存储宽高
const point = reactive({
width: 0,
height: 0,
str: ''
})
// 函数: 设置宽高
function setWH (event) {
point.width = event.width
point.height = event.height
point.str = event.str
}
// 函数: 设置宽高
function getWH (event) {
screen.width = document.documentElement.clientWidth
screen.height = document.documentElement.clientHeight
point.str = event.str || '自动获取可视化宽高'
}
// 返回数据
return { point, setWH, getWH }
}
总结区别
1. 代码组织方式
| 特性 | Vue 2 Mixin | Vue 3 Composition API(Hook) |
|---|---|---|
| 代码结构 | 通过混入对象的选项(data、methods 等)复用逻辑。 | 通过函数(setup)和自定义 Hook 组织逻辑。 |
| 逻辑聚合 | 逻辑分散在组件的各个选项中(如 data 和 methods)。 | 相关逻辑集中在一个函数中(如 useUser)。 |
| 复用方式 | 静态混入,所有属性和方法自动合并到组件中。 | 动态组合,按需引入功能函数。 |
示例对比:
-
Mixin(Vue 2):
// userMixin.js export default { data() { return { username: 'Alice' }; }, methods: { login() { /* ... */ } } }; // 组件中使用 export default { mixins: [userMixin], methods: { // 可能覆盖 Mixin 的同名方法! login() { /* ... */ } } }; -
Composition API(Vue 3):
// useUser.js import { ref } from 'vue'; export function useUser() { const username = ref('Alice'); const login = () => { /* ... */ }; return { username, login }; } // 组件中使用 import { useUser } from './useUser'; export default { setup() { const { username, login } = useUser(); return { username, login }; } };
2. 作用域与命名冲突
| 特性 | Vue 2 Mixin | Vue 3 Composition API(Hook) |
|---|---|---|
| 作用域 | Mixin 的属性和方法与组件直接合并,共享同一作用域。 | 组合函数返回的属性和方法需在 setup 中显式暴露。 |
| 命名冲突 | 高:Mixin 和组件同名属性/方法会覆盖。 | 低:通过显式命名或解构赋值避免冲突。 |
示例:
-
Mixin 的命名冲突:
// Mixin 定义 const mixin = { data() { return { count: 0 }; } }; // 组件定义 export default { mixins: [mixin], data() { return { count: 42 }; // 覆盖 Mixin 的 count! } }; -
Composition API 的显式命名:
// useCounter.js export function useCounter() { const count = ref(0); return { count }; } // 组件中使用 export default { setup() { const { count: counter } = useCounter(); return { counter }; // 重命名避免冲突 } };
3. 灵活性与可维护性
| 特性 | Vue 2 Mixin | Vue 3 Composition API(Hook) |
|---|---|---|
| 逻辑组合 | 静态:混入的代码无法动态调整。 | 动态:可灵活组合多个 Hook,甚至条件复用。 |
| 依赖管理 | 隐式:Mixin 的依赖关系不透明。 | 显式:通过函数参数传递依赖,关系清晰。 |
| 调试难度 | 高:多个 Mixin 的代码合并后难以追踪来源。 | 低:每个 Hook 是独立模块,调试更直观。 |
示例:
-
动态组合多个 Hook:
// 组件中使用多个 Hook import { useUser, useCounter } from './hooks'; export default { setup() { const { username } = useUser(); const { count } = useCounter(); return { username, count }; } };
4. 类型支持(TypeScript)
| 特性 | Vue 2 Mixin | Vue 3 Composition API(Hook) |
|---|---|---|
| 类型推断 | 弱:Mixin 的属性和方法类型难以推导。 | 强:组合函数可明确定义类型,支持完整类型推断。 |
示例:
-
Composition API 的类型定义:
// useUser.ts import { ref } from 'vue'; interface User { username: string; login: () => void; } export function useUser(): User { const username = ref('Alice'); const login = () => { /* ... */ }; return { username, login }; }
5. 生命周期管理
| 特性 | Vue 2 Mixin | Vue 3 Composition API(Hook) |
|---|---|---|
| 生命周期钩子 | Mixin 的钩子与组件钩子合并,执行顺序不可控。 | 使用 onMounted 等函数显式注册,逻辑集中。 |
示例:
-
Mixin 的钩子合并:
j
// Mixin export default { created() { console.log('Mixin created'); } }; // 组件 export default { mixins: [mixin], created() { console.log('Component created'); } }; // 输出顺序:Mixin created → Component created -
Composition API 的钩子注册:
javascript
复制
import { onMounted } from 'vue'; export default { setup() { onMounted(() => { console.log('Component mounted'); }); } };
总结
| 维度 | Vue 2 Mixin | Vue 3 Composition API(Hook) |
|---|---|---|
| 代码组织 | 分散在组件选项中,逻辑碎片化。 | 逻辑集中,按功能组织为独立函数。 |
| 命名冲突 | 容易冲突,需手动管理。 | 通过作用域隔离和显式暴露避免冲突。 |
| 灵活性 | 静态混入,无法动态调整。 | 动态组合,按需复用逻辑。 |
| 类型支持 | 类型推导困难。 | 完整的 TypeScript 支持。 |
| 调试维护 | 隐式依赖,调试复杂。 | 显式依赖,模块化,易于调试。 |
| 生命周期 | 钩子自动合并,顺序不可控。 | 显式注册,逻辑集中管理。 |
| 选择建议: |
- Vue 2 项目:可继续使用 Mixin,但需注意命名冲突和代码规范。
- Vue 3 项目:优先使用 Composition API,通过自定义 Hook 实现更灵活、安全的代码复用。