vue2 mixin
定义
Mixin
是一种在 Vue 组件中复用代码的方式。我们可以将一个对象(即 mixin 对象)中的数据、方法和生命周期钩子等混入到 Vue 组件中。这样,多个组件就可以共享同一份逻辑代码。
首先,我们定义一个 mixin 对象,其中包含(data
、methods
、computed
)等。比如,我们可以创建一个 commonMixin.js
文件来定义一个 mixin:
javascript
// 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']
xml
<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)
javascript
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 中的钩子会在组件中的钩子之前调用。
javascript
// 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 中:
javascript
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 中:
javascript
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 共享父组件传递的数据或方法:
javascript
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 中。例如,处理表单提交、数据清理等:
javascript
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>
vue 3 hook
定义
本质上它就是一个函数,有点类似于vue2的mixin技术,都是将代码混入组件中。
作用
将重复的逻辑抽离出来,提高代码复用率,在组件里使用hook时,相当于函数里的相关api方法都移入了组件里,让setup的逻辑更加简洁清晰。
使用
useSum.ts
中内容如下:
csharp
```
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
中内容如下:
csharp
```
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}
}
```
组件中具体使用:
xml
```
<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>
```
csharp
// 引入组合式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):
javascript// userMixin.js export default { data() { return { username: 'Alice' }; }, methods: { login() { /* ... */ } } }; // 组件中使用 export default { mixins: [userMixin], methods: { // 可能覆盖 Mixin 的同名方法! login() { /* ... */ } } };
-
Composition API(Vue 3):
javascript// 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 的命名冲突:
javascript// Mixin 定义 const mixin = { data() { return { count: 0 }; } }; // 组件定义 export default { mixins: [mixin], data() { return { count: 42 }; // 覆盖 Mixin 的 count! } };
-
Composition API 的显式命名:
javascript// 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:
javascript// 组件中使用多个 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 的类型定义:
typescript// 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
javascript// Mixin export default { created() { console.log('Mixin created'); } }; // 组件 export default { mixins: [mixin], created() { console.log('Component created'); } }; // 输出顺序:Mixin created → Component created
-
Composition API 的钩子注册:
javascript
复制
javascriptimport { 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 实现更灵活、安全的代码复用。