04-Hooks封装

7/24/2022

# 一、Hooks

# 1,为什么要封装Hooks

  • 我们都知道,在Vue2中,在同一个.vue组件中,当 data、methods、computed、watch 的体量较大时,代码将变得臃肿。为了解决代码臃肿问题,我们除了拆分组件外,别无它法。
  • 在Vue3中,同样存在这样的问题:当我们的组件开始变得更大时,逻辑关注点将越来越多,这会导致组件难以阅读和理解。但是,在Vue3中,我们除了可以拆分组件,还可以使用 Hooks封装来解决这一问题。
  • 所谓 Hooks封装,就是把不同的逻辑关注点抽离出来,以达到业务逻辑的独立性。这一思路,也是Vue3 对比Vue2的最大亮点之一。

# 2,如何封装Hooks

  • 在 setup 组合的开发模式下,把具体某个业务功能所用到的 ref、reactive、watch、computed、watchEffect 等,提取到一个以 use* 开头的自定义函数中去。
  • 封装成 use* 开头的Hooks函数,不仅可以享受到封装带来的便利性,还有利于代码逻辑的复用。Hooks函数的另一个特点是,被复用时可以保持作用域的独立性,即,同一个Hooks函数被多次复用,彼此是不干扰的。

# 3,在哪些情况下需要封装Hooks

  • 两种场景:一种是功能类Hooks,即为了逻辑复用的封装;另一种是业务类Hooks,即为了逻辑解耦的封装。下面我给两组代码,说明这两种使用场景。

# 二,实践

# 1,useCounter

// src/hooks/useCounter.ts

import { ref } from "vue"

export default function(){
    let counter = ref(0)
    let add = ()=>counter.value++
    let sub = ()=>counter.value--

    return {
        counter, 
        add,
        sub
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

组件中使用之,如下:

// Demo.vue

<template>
  <h1>useCounter</h1>
  <h4>{{ counter }}</h4>
  <button @click="add">+1</button>
  <button @click="sub">-1</button>
</template>

<script setup>
import useCounter from "./hooks/useCounter";

let { counter, add, sub } = useCounter();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2,useTitle

// src/hooks/useTitle.ts

import {ref,watch} from "vue"

export default function(title="默认的title"){
    let titleRef = ref(title)
    watch(titleRef,(newValue,oldNew)=>{
        document.title = newValue;
    },{
        immediate:true
    })
    return titleRef;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

组件中使用之,如下:

// Demo.vue

<template>
  <h1>useTitle</h1>
</template>

<script setup>
import useTitle from "./hooks/useTitle";

let titleRef = useTitle();
setTimeout(() => {
  titleRef.value = "hi vue3";
}, 3000);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 3,useScrollPosition

// src/hooks/useScrollPosition.ts

import { ref } from "vue"
export default function(){
    let scrollX = ref(0)
    let scrollY = ref(0)
    document.addEventListener("scroll",()=>{
      scrollX.value = window.scrollX
      scrollY.value = window.scrollY
    })
    return {
      scrollX,
      scrollY
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

组件中使用之,如下:

// Demo.vue

<template>
  <h1>useScrollPosition</h1>
  <div class="content"></div>
  <div class="scroll">
    <div class="scroll-x">scrollX:{{ scrollX }}</div>
    <div class="scroll-y">scrollY:{{ scrollY }}</div>
  </div>
</template>

<script setup>
import useScrollPosition from "./hooks/useScrollPosition";

let { scrollX, scrollY } = useScrollPosition();
</script>

<style scoped>
.content {
  width: 3000px;
  height: 5000px;
}
.scroll {
  position: fixed;
  right: 30px;
  bottom: 30px;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 4,useMousePosition

// src/hooks/useMousePosition.ts

import { ref } from "vue"
export default function(){
    let mouseX = ref(0)
    let mouseY = ref(0)

    window.addEventListener("mousemove",(e)=>{
      mouseX.value = e.pageX;
      mouseY.value = e.pageY;
    })

    return {
      mouseX,
      mouseY,
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

组件中使用之,如下:

// Demo.vue

<template>
  <h1>useMousePosition</h1>
  <div class="mouse">
    <div class="mouse-x">mouseX:{{ mouseX }}</div>
    <div class="mouse-y">mouseY:{{ mouseY }}</div>
  </div>
</template>

<script setup>
import useMousePosition from "./hooks/useMousePosition";

let { mouseX, mouseY } = useMousePosition();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 5,useLocalStoage

// src/hooks/useLocalStoage.ts

import { ref, watch } from "vue"
// useLocalStoage("token","68656789fsadfsadf78765fasdfadsf")
// useLocalStoage("token")
export default function(key,value){
    let data = ref(value)

    if(value){
        // 存
        window.localStorage.setItem(key,JSON.stringify(value))
    }else{
        // 取
        data.value = JSON.parse(window.localStorage.getItem(key))
    }

    watch(data,(newValue)=>{
        window.localStorage.setItem(key,JSON.stringify(newValue))
    })

    return data;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

组件中使用之,如下:

// Demo.vue

<template>
  <h1>useMousePosition</h1>
  <button @click="changeData">修改数据</button>
</template>

<script setup>
import useLocalStoage from "./hooks/useLocalStoage";

let data = useLocalStoage("token", "123456789");
console.log("data:", data);
let changeData = () => (data.value = "987654321");
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Last Updated: 12/25/2022, 10:02:14 PM