Skip to content
本页目录

vue3中hooks


0.前置知识

0.1 mixin是什么?

mixin,翻译过来就是混入,不仅仅在vue框架中存在mixin,确切的说mixin是一种思想,一种混入的思想。混入的内容就是可以在被混入的地方使用,他会自动的将混入的东西准确的分配到指定的组件中。在vue中,mixin相当于指定混入的变量&函数放入他不混入时候该放的地方。可以认为,vue中的mixin就是相当于组件中的组件。组件A中的watch中处理的逻辑hanldleParams,在组件B中的watch中同样需要这样的逻辑hanldleParams。

那么有两种方法:这两种方法的区别就代表了mixin和utils的区别
1.抽函数:hanldleParams函数抽出来,然后在watch中调用hanldleParams;
2.mixin:上一种抽函数但是我们还是需要在组件中写watch,毕竟两个组件都是在watch这个钩子中调用,如果每个组件都写watch,那么watch也是重复的东西,因此mixin就是将watch钩子都可以抽出来的组件,也就是说,mixin抽出来不仅仅是纯函数逻辑,还可以将vue组件特有的钩子等逻辑也可以抽出来,达到进一步复用,这就是mixin的作用。那么组件A\B通过mixin共用一个watch,导入即可,不需要开发人将其放置在指定位置。
特点:Mixin中的数据和方法都是独立的,组件之间使用后是互相不影响的

0.2 mixin解决了什么问题?

mixin解决了逻辑复用 & vue配置复用 注意:组件配置复用是指,组件中的选项式API(例如:data,computed,watch)或者组件的生命周期钩子(created、mounted、destroyed)

0.3 使用&使用场景?

在vue中,mixin定义的就是一个对象,对象中放置的vue组件相应的选项式API和对应的生命周期钩

export const mixins = {
  data() {
    return {};
  },
  computed: {},
  created() {},
  mounted() {},
  methods: {},
};

记住:mixin中一般都是存在vue组件中的选项API和组件生命周期钩子,因为函数的抽象也是要放在组件中特定的API或者钩子中,因此mixin考虑了这一点,直接配置好了所有的API,只要在函数中放置即可。例如抽象一个hanldleParams函数,我们一般是在config文件中导出,然后在组件中引入使用,data中method中使用处理data,并且都要设置在data中变量b,那么这段逻辑就可以抽离出来在mixin中放置。而对于与组件业务有关的数据或者逻辑一般都是写在组件中的。

例如:

// 定义一个mixin
export const mixins = {
  data() {
    return {
      msg: "我是小猪课堂",
    };
  },
  computed: {},
  created() {
    console.log("我是mixin中的created生命周期函数");
  },
  mounted() {
    console.log("我是mixin中的mounted生命周期函数");
  },
  methods: {
    clickMe() {
      console.log("我是mixin中的点击事件");
    },
  },
};
//

// src/App.vue中使用导出的mixin
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <button @click="clickMe">点击我</button>
  </div>
</template>

<script>
import { mixins } from "./mixin/index";
export default {
  name: "App",
  mixins: [mixins], // 注册mixin,这样mixin中所有的钩子函数等同于组件中钩子
  components: {},
  created(){
    console.log("组件调用minxi数据",this.msg);
  },
  mounted(){
    console.log("我是组件的mounted生命周期函数")
  }
};
</script>

注意:mixin中和vue组件中相同的钩子的优先级:

  • mixin中的生命周期函数会和组件的生命周期函数一起合并执行。
  • mixin中的data数据在组件中也可以使用。
  • mixin中的方法在组件内部可以直接调用。
  • 生命周期函数合并后执行顺序:先执行mixin中的,后执行组件的。

此外,mixin对于不同组件的导入,相互之间数据是不会影响的,例如:

// mixin文件
export const mixins = {
  data() {
    return {
      msg: "我是小猪课堂",
    };
  },
  computed: {},
  created() {
    console.log("我是mixin中的created生命周期函数");
  },
  mounted() {
    console.log("我是mixin中的mounted生命周期函数");
  },
  methods: {
    clickMe() {
      console.log("我是mixin中的点击事件");
    },
  },
};

在文件app中使用mixin,并通过changeMsg函数来更改mixin中data的变量msg,此时对于其他组件使用的这个msg变量是不变的。

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <button @click="clickMe">点击我</button>
    <button @click="changeMsg">更改mixin数据</button>
    <demo></demo>
  </div>
</template>

<script>
import { mixins } from "./mixin/index";
import demo from "./components/demo.vue";
export default {
  name: "App",
  mixins: [mixins],
  components: { demo },
  created() {
    console.log("组件调用minxi数据", this.msg);
  },
  mounted() {
    console.log("我是组件的mounted生命周期函数");
  },
  methods: {
    changeMsg() {
      this.msg = "我是变异的小猪课堂";
      console.log("更改后的msg:", this.msg);
    },
  },
};
</script>

0.4 存在的缺点是什么

mixin
优点:组件中钩子函数的注册复用
缺点:
相同钩子中注册的函数名相同会发生冲突(vue中冲突的解决方案是本组件中优先级高于mixin)
定位错误需要花费时间
滥用会造成维护问题

1.hooks是什么

一般来说,我们开发中会自动抽象出逻辑函数放在utils中,utils中放的纯逻辑,不存在属于组件的东西,例如狗子函数等。而hooks就是在utils的基础上再包一层组件级别的东西(钩子函数等)。例如:我们每次点击button都会弹出一个弹窗,自动显示当前日期。但是我将函数放在util中,每次复用都需要click=handleClick 函数放入日期函数,通过handleClick函数管理utils,那么我不如直接将handleClick也封装起来,下次直接调用,复用了methods注册的环节

2.为什么会出现hooks

hooks与mixin的区别
mixin是options API的体现,一个是composition API的体现

1.mixin

  • 在vue2 中有一个东西:Mixins 可以实现这个功能
  • mixins就是将这些多个相同的逻辑抽离出来,各个组件只需要引入mixins,就能实现代码复用
  • 弊端一: 会涉及到覆盖的问题
  • 组件的data、methods、filters会覆盖mixins里的同名data、methods、filters
  • 弊端二:隐式传入,变量来源不明确,不利于阅读,使代码变得难以维护
  • 弊端三:mixin无法传入灵活的传入参数,例如(弊端3的举例): 我需要定一个一个变量name,但是name的初始值是随机的,那么name定义在mixin中的时候,他的初始化一定是固定的,我们如果要改只能再method中注册一个方法来修改name的值:
// 混入文件:name-mixin.js
export default {
  data() {
    return {
      name: 'zhng'
    }
  },
  methods: {
    setName(name) {
      this.name = name
    }
  }
}

// 组件:my-component.vue
<template>
  <div>{{ name }}</div>
<template>
<script>
import nameMixin from './name-mixin';
export default {
  mixins: [nameMixin],
  mounted() {
    setTimeout(() => {
      this.setName('Tom') // 通过在组件中调用setName传入参数值,无法传入参数,限制了Mixin的灵活性
    }, 3000)
  }
}
<script>

而使用hooks的方法

import { computed, ref, Ref } from "vue"
// 定义hook方法
type CountResultProps = {
    count:Ref<number>;
    multiple:Ref<number>;
    increase:(delta?:number)=>void;
    decrease:(delta?:number)=> void;
}
export default function useCount(initValue = 1):CountResultProps{
    const count = ref(initValue)
    const increase = (delta?:number):void =>{
        if(typeof delta !== 'undefined'){
            count.value += delta
        }else{
            count.value += 1
        }
    }
    const multiple  = computed(()=>count.value * 2)
    const decrease = (delta?:number):void=>{
        if(typeof delta !== "undefined"){
            count.value -= delta
        }else{
            count.value -= 1
        }
    }
    return {
        count,
        increase,
        decrease,
        multiple
    }
 
}

// 在组件中的使用
<template>
   <p>count:{{count}}</p>
   <p>倍数:{{multiple}}</p>
   <div>
     <button @click="increase(10)">加一</button>
     <button @click="decrease(10)">减一</button> // 在模版中直接使用hooks中的方法作为回调函数
   </div>
</template>
<script setup lang="ts">
import useCount from "../views/Hook"
const {count,multiple,increase,decrease}  = useCount(10)
</script>
<style>
 
</style> 
        

2.hooks:

  • Vue3中我们可以: 自定义Hook
  • Vue3 的 hook函数 相当于 vue2 的 mixin, 但是: hooks 是函数
  • Vue3 的 hook函数 可以帮助我们提高代码的复用性, 让我们能在不同的组件中都利用 hooks 函数

3.hooks使用场景-自定义hook

3.1 hooks中常见的业务使用场景

重复造轮子的组件,除开一些毫无必要的重复以外,有一些功能组件确实需要封装一下,比如说,一些需要请求后端字典到前端展示的下来选择框,点击之后要展示loading状态的按钮,带有查询条件的表单,这些非常常用的业务场景,我们就可以封装成组件,但是封装成组件就会遇到前面说的问题,每个人的使用习惯和封装习惯不一样,很难让每个人都满意,这种场景,就可以让hook来解决。

3.2 自定义hook需要满足的规范

image.png

3.3 自定义hooks使用举例

例子1

hooks-1自定义:useCut

import {ref,watch} from "vue"
export function useCut({num1,num2}){
   const cutNum = ref(0);
   watch([num1,num2],(num1,num2)=>{
    cutFunc(num1,num2)
  })
  const cutFunc = (num1,num2)=>{
    cutNum.value = num1+num2
  }
  return {
    cutNum,
    cutFunc
  }
}

hooks2自定义

import {ref,watch} from "vue"
const useAdd = ({num1,num2})=>{
  const addNum = ref(0);
  watch([num1,num2],(num1,num2)=>{
    addFunc(num1,num2)
  })
  const addFunc = (num1,num2)=>{
    addNum.value = num1+num2
  }
  return {
    addNum,
    addFunc
  }
}
export default useAdd



组件中使用自定义hooks

<template>
    <div>
        num1:<input v-model.number="num1" style="width:100px" />
        <br />
        num2:<input v-model.number="num2" style="width:100px" />
    </div>
    <span>加法等于:{{ addNum }}</span>
    <br />
    <span>减法等于:{{ cutNum }}</span>
</template>
import { ref } from 'vue'
import useAdd from './addHook.js'     //引入自动hook 
import { useCut } from './cutHook.js' //引入自动hook 
const num1 = ref(2)
const num2 = ref(1)
const { addNum, addFunc } = useAdd({ num1, num2 })  // 加法功能-自定义Hook(将响应式变量或者方法形式暴露出来)
// 因为hooks是函数,不像mixin是对象形式,所以更方便的传入组件中的data变量,交给抽象逻辑使用
addFn(num1.value, num2.value)
const { cutNum, cutFunc } = useCut({ num1, num2 }) // 减法功能-自定义Hook (将响应式变量或者方法形式暴露出来)
subFn(num1.value, num2.value)


参考文献:juejin.cn/post/711358… blog.csdn.net/longxiaobao…

blog.csdn.net/qq_39197547…