Hooks are used in Vue3, what exactly are hooks? how to understand

1 Introduction

Recently, the word hooks has been brought up all the time. I often hear it but I don’t know how to use it, so I took the time to study it carefully.

2.vue3 hooks

2.1 Why vue3 can use hooks

Because vue2 changed from optional Api to combined Api. The underlying source code has changed a lot.
The advantage of the combined API
is that the statement cycle can be used within the function.

2.2 Benefits and advantages of using hooks

Whether it is vue or react, it can be done through the Hooks writing method, and the "code blocks scattered in various statement cycles" can be aggregated by Hooks.
This sentence made me suddenly see the light.
Because the vue2 option API used to disperse the things of each life cycle, it must be more standardized when writing small projects. But when the project is large, many similar businesses are placed in different modules because of the statement cycle.
I don't have this problem with the functional API.
insert image description here

Unload the same functionality together. And the declaration cycle function can be used in each code block. Code reusability has improved linearly.

3. The origin of hooks

3.1 Distinction between functional components and class components

It was react at the beginning; before react, it was written as a class component. Then functional components cannot be stateful. Periodic functions cannot be called.

//1.创建函数式组件
		function MyComponent(){
    
    
			console.log(this); 
    //此处的this是undefined,因为babel编译后开启了严格模式,严格模式最大的特点就是禁止自定义的函数中this指向window
			return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
		}
		//2.渲染组件到页面,如果第一个参数直接传入函数名会报函数类型不能作为React的节点错误,必须以标签的形式传入而且如果名字是小写的话会报当前这个标准在浏览器中不被允许,因为如果是小写的标签会被转成html标签,但是html中并没有此标签,还要注意标签必须要闭合
		ReactDOM.render(<MyComponent/>,document.getElementById('test'))

functional components

//1.创建类式组件,1、必须要继承React.Component,2、必须要有render函数,3、render函数中必须要返回值 
		class MyComponent extends React.Component {
    
    
			render(){
    
    
				//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。但是有个问题就是我们并没有手动去new MyComponent这个实例,但是下面的ReactDOM.render函数中写了组件标签后React会自动的帮我们创建实例
				
				//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
				console.log('render中的this:',this);
				return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<MyComponent/>,document.getElementById('test'))

class component

3.2 useState and useEffect

useState: In the functional component, the state is called through this hook function (equivalent to the data in vue2, which is the mounted data). Complete state change operations

import React, {
    
     useState } from 'react';
 
function CounterWithHooks(props) {
    
    
	const [count, setCount] = useState(props.initialValue);
 
	return (
		<div>
			<h2>This is a counter using hooks</h2>
			<h1>{
    
    count}</h1>
			<button onClick={
    
    () => setCount(count + 1)}>Click to Increment</button>
		</div>
	);
}
 
export default CounterWithHooks;

The useEffect statement cycle is equivalent to the statement cycle hook, I don’t know much about it; I don’t know much about react.

import React, {
    
     Component } from 'react';
 
class App extends Component {
    
    
	componentDidMount() {
    
    
		console.log('I have just mounted!');
	}
 
	render() {
    
    
		return <div>Insert JSX here</div>;
	}
}

function App() {
    
    
	useEffect(() => {
    
    
		console.log('I have just mounted!');
	});
 
	return <div>Insert JSX here</div>;
}

4. Combined functions in vue 3

Take an example from the vue3 official website. In the concept of Vue applications, "Composables" are functions that leverage Vue's composition API to encapsulate and reuse stateful logic.
mouse tracker example

<script setup>
import {
    
     ref, onMounted, onUnmounted } from 'vue'

const x = ref(0)
const y = ref(0)

function update(event) {
    
    
  x.value = event.pageX
  y.value = event.pageY
}

onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
</script>

<template>Mouse position is at: {
    
    {
    
     x }}, {
    
    {
    
     y }}</template>

But what if we want to reuse this same logic in multiple components? We can extract this logic into an external file as a composite function:

// mouse.js
import {
    
     ref, onMounted, onUnmounted } from 'vue'

// 按照惯例,组合式函数名以“use”开头
export function useMouse() {
    
    
  // 被组合式函数封装和管理的状态
  const x = ref(0)
  const y = ref(0)

  // 组合式函数可以随时更改其状态。
  function update(event) {
    
    
    x.value = event.pageX
    y.value = event.pageY
  }

  // 一个组合式函数也可以挂靠在所属组件的生命周期上
  // 来启动和卸载副作用
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  // 通过返回值暴露所管理的状态
  return {
    
     x, y }
}

Use in components

<script setup>
import {
    
     useMouse } from './mouse.js'

const {
    
     x, y } = useMouse()
</script>

<template>Mouse position is at: {
    
    {
    
     x }}, {
    
    {
    
     y }}</template>

5. The application of hooks in vue3 in open source projects;

Open source project address https://github.com/fect-org/fect

5.1 vue3 use useState

The author's practice of using hooks in vue3

import {
    
     ref, readonly } from 'vue'
import type {
    
     UnwrapRef, Ref, DeepReadonly } from 'vue'

export type SetStateAction<S> = S | ((prevState: S) => S)

export type Dispatch<T> = (val: SetStateAction<T>) => void

const useState = <T>(initial?: T) => {
    
    
  const state = ref<T | undefined>(initial)
  const dispatch = (next: SetStateAction<T>) => {
    
    
    const draft = typeof next === 'function' ? (next as (prevState: T) => T)(state.value as T) : next
    state.value = draft as UnwrapRef<T>
  }
  return [readonly(state), dispatch] as [DeepReadonly<Ref<T>>, Dispatch<T>]
}

export {
    
     useState }

Specific use

import {
    
     useEventListener, useState } from '@fect-ui/vue-hooks'
  //===============
  setup(props, {
    
     slots, emit }) {
    
    
    const affixRef = ref<HTMLElement>()

    const [fixed, setFixed] = useState<boolean>(false)
    const [style, setStyle] = useState<AffixLayout>({
    
     width: 0, height: 0 })

    const offset = computed(() => convertUnitToPx(props.offset))

    const scrollHandler = () => {
    
    
      if (!affixRef.value || isHidden(affixRef)) return
      const {
    
     position } = props
      const affixRect = getDomRect(affixRef)
      const scrollTop = getScrollTop(window)
      setStyle({
    
    
        width: affixRect.width,
        height: affixRect.height
      })
      if (position === 'top') {
    
    
        setFixed(() => offset.value > affixRect.top)
      }
      if (position === 'bottom') {
    
    
        const {
    
     clientHeight } = document.documentElement
        setFixed(() => clientHeight - offset.value < affixRect.bottom)
      }

5.2 Think about how we apply it.
When the project is small, we probably won't be able to use it. Such as a simple login and registration. There is a high probability that it will not be applied.

import {
    
     reactive, toRefs } from 'vue'
import {
    
     login, register } from '@/service/user'
import {
    
     setLocal } from '@/common/js/utils'
import {
    
     Toast } from 'vant'

export default {
    
    
  setup() {
    
    
    
    const state = reactive({
    
    
      username: '',
      password: '',
      email1: '',
      username1: '',
      password1: '',
      type: 'login',
      verify: ''
    })

    // 切换登录和注册两种模式
    const toggle = (v) => {
    
    
      state.type = v
      state.verify = ''
    }
  
    // 提交登录或注册表单
    const onSubmit = async (values) => {
    
    
   
      if (state.type == 'login') {
    
    
        const  data  = await login({
    
    
          "email": values.username,
          "password": values.password
        })
        console.log("token",data)
        setLocal('token', data.content)
        
        // 需要刷新页面,否则 axios.js 文件里的 token 不会被重置
        window.location.href = '/'
        
      } else {
    
    
        await register({
    
    
          "username": values.username1,
          "password": values.password1,
          "email":values.email1
        })
        Toast.success('注册成功')
        state.type = 'login'
        state.verify = ''
      }
    }
    return {
    
    
      ...toRefs(state),
      toggle,
      onSubmit,
  
    }
  }

}
</script>

Guess you like

Origin blog.csdn.net/qq_21561833/article/details/126798853