vue3 hooks

475 阅读3分钟

vue3为什么选用composition-api?
先说一下vue2的options-api存在的一些问题
1.随着功能的增长,复杂组件的代码变得越来越难以维护。 尤其发生你去新接手别人的代码时。 根本原因是 Vue 的现有 API 通过「选项」组织代码,但是在大部分情况下,通过逻辑考虑来组织代码更有意义。
2. 缺少一种比较「干净」的在多个组件之间提取和复用逻辑的机制。
vue2采用mixin的方式进行代码复用或抽离
mixin如何使用?
1.用一个js文件将vue的script部分抽离出来:

  data(){
    return {}
  },
  methods:{},
  computed:{},
  filters:{},
  created(){},
  mounted(){
    console.log("我是mixins");
  }
}

2.在需要的组件引入:

<script>
  import mixin from "./mixin";
  export default {
    mixins: [mixin]
  }
</script>

mixins的一些特性:

1.mixins中的生命周期会与引入mixins的组件的生命周期整合在一起调用(mixin中的生命周期调用的比组件的快)
2.组件的data、methods、filters会覆盖mixins里的同名data、methods、filters。
3.不同mixin里的同名方法,按照引进的顺序,最后的覆盖前面的同名方法。

[mixin1,mixin2]
mixin2中的同名方法会覆盖mixin1中的方法

mixins的缺点:

1.变量来源不明确(隐式传入),不利于阅读,使代码变得难以维护。
组件里可以引入多个mixin,并直接隐式调用mixin里的变量/方法,
这会让我们有时候混乱 这些变量/方法 分别是哪个mixin里的?

2.多个mixins的生命周期会融合到一起运行,但是同名属性、同名方法无法融合,可能会导致冲突。
比如两个mixin被同一组件使用,两个mixin中有同名属性,就会导致冲突,虽然可以避免,但很容易犯错。

3.mixins和组件可能出现多对多的关系,复杂度较高(即一个组件可以引用多个mixins,一个mixins也可以被多个组件引用)
注:VUE3提出的Composition API旨在解决这些问题。mixins 的缺点是 Composition API 背后的主要动因之一,Composition API 受到 React Hooks 的启发。

vue3 hooks

hooks就是函数。
vue3 composition api就是来改变vue2 options api存在的弊端的。
hooks把代码用逻辑组织起来。达到代码的拆分和复用的目的。 规避了mixin的不足。
下面来看一些例子

获取鼠标位置的hooks

import { ref, onMounted, onUnmounted } from "vue";
 
export function useMousePosition() {
  const x = ref(0);
  const y = ref(0);
 
  function update(e) {
    x.value = e.pageX;
    y.value = e.pageY;
  }
 
  onMounted(() => {
    window.addEventListener("mousemove", update);
  });
 
  onUnmounted(() => {
    window.removeEventListener("mousemove", update);
  });
 
  return { x, y };
}

页面中使用:

<template>
  <div>鼠标x坐标:{{x}};鼠标y坐标:{{y}}</div>
</template>

<script>
import { defineComponent } from 'vue'
import { useMousePosition } from './useMousePosition'
export default defineComponent({
   name: 'TestHooks',
   setup() {
     const { x, y } = useMousePosition();
     return {
       x,
       y
     }
   }
});
</script>

判断是否点击指定元素外的区域 的hooks

/* 功能:传入一个DOM对象,判断是否点击除本身以外的其他dom上 */
import { ref, onMounted, onUnmounted, computed } from "vue";

const useClickOutside = DOM => {
   // 是否点击到外部DOM
  const isClickOutside = ref(false);
  // 点击到dom元素上时背景色为蓝色,点击到外部时不设置背景色
  const backgroundColor = computed(() => {
    return isClickOutside.value ? 'blue' : 'none';
  })
  const hander = e => {
    // contains判断一个标签是否包含另一个标签
    if (DOM.value && DOM.value.contains(e.target)) {
      isClickOutside.value = false;
    } else {
      isClickOutside.value = true;
    }
  };
  onMounted(() => {
    document.addEventListener("click", hander);
  });
  onUnmounted(() => {
    document.removeEventListener("click", hander);
  });
  return {
    isClickOutside,
    backgroundColor
  };
};

export default useClickOutside;

使用:

<template>
  <div ref="DOM" class="dom">鼠标x坐标:{{x}};鼠标y坐标:{{y}}</div>
</template>

<script>
import { defineComponent, ref } from 'vue'
import { useMousePosition } from './useMousePosition'
import useClickOutside from './useClickOutside'
export default defineComponent({
   name: 'TestHooks',
   setup() {
     // 获取鼠标位置
     const { x, y } = useMousePosition();
     // 点击dom区域外
     const DOM = ref()
     const { isClickOutside, backgroundColor } = useClickOutside(DOM);
     return {
       x,
       y,
       DOM,
       isClickOutside,
       backgroundColor
     }
   }
});
</script>
<style scoped lang='scss'>
.dom{
  background: v-bind('backgroundColor')
}
</style>

上面的例子是以 逻辑来拆分代码的,每个功能写一个hooks,优点 比较明显;
变量来源清晰;功能划分清晰;不存在变量/方法命名冲突;复用方便;组织代码不会臃肿;

options-api和composition-api对比图

image.png 补充
React中hooks的命名规范 在 react 官方文档里,对 hooks 的定义和使用提出了 “一个假设、两个只在” 的思想

一个假设: 假设任何以 「use」 开头并紧跟着一个大写字母的函数就是一个 Hook。

第一个只在: 只在 React 函数组件中调用 Hook,而不在普通函数中调用 Hook。

第二个只在: 只在最顶层使用 Hook,而不要在循环,条件或嵌套函数中调用 Hook。