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 实现更灵活、安全的代码复用。