前端笔记-Vue3(下)

学习参考视频:尚硅谷Vue3入门到实战,最新版vue3+TypeScript前端开发教程_哔哩哔哩_bilibili

vue3学习目标:

VUE 3 1、Vue3架构与设计理念
2、组合式API(Composition API)
3、常用API:ref、reactive、watch、computed
4、Vue3的生命周期
5、组件间通信(props、emit、defineModel)
6、了解插槽

Vue 生命周期钩子详解(Vue 2 & Vue 3 对比)

生命周期概念图解

https://cn.vuejs.org/assets/lifecycle_zh-CN.W0MNXI0C.png

生命周期阶段对比表

阶段 Vue 2 钩子 Vue 3 钩子 触发时机 典型用途
创建 beforeCreate setup() 组件实例刚被创建 初始化响应式数据
created setup() 数据观测/事件配置完成 异步请求数据
挂载 beforeMount onBeforeMount 模板编译/挂载前 访问/操作DOM前的最后机会
mounted onMounted 组件挂载到DOM后 操作DOM、初始化第三方库
更新 beforeUpdate onBeforeUpdate 数据变化,DOM更新前 获取更新前的DOM状态
updated onUpdated 虚拟DOM重新渲染后 执行依赖DOM的操作
销毁 beforeDestroy onBeforeUnmount 实例销毁前 清除定时器/取消订阅
destroyed onUnmounted 实例销毁后 清理内存/移除事件监听

Vue 3 生命周期示例



生命周期执行顺序演示

  1. 组件初始化时:

    1. setup阶段 - 数据初始化完成
    3. mounted - 组件已挂载到DOM
  2. 点击"增加"按钮时:

    2. 数据变化触发更新流程
    4. updated - DOM已更新
  3. 点击"卸载组件"时:

    子组件 unmounted

前端笔记-Vue3(下)_第1张图片

 挂载阶段完整顺序(父 → 子)

  1. 父组件​ setup() 执行
  2. 父组件​ onBeforeMount
  3. 子组件​ setup() 执行
  4. 子组件​ onBeforeMount
  5. 子组件​ onMounted(子组件先完成挂载)
  6. 父组件​ onMounted(父组件最后完成挂载)

常用钩子使用场景

1. onMounted - 最常用

onMounted(async () => {
  // 加载初始数据
  const response = await fetch('/api/data')
  data.value = await response.json()
  
  // 初始化图表
  chart = new Chart(document.getElementById('chart'), {
    // 配置项
  })
})

2. onUpdated - 谨慎使用

onUpdated(() => {
  // 典型用例:自动滚动到底部
  listContainer.value.scrollTop = listContainer.value.scrollHeight
})

3. onUnmounted - 资源清理

onUnmounted(() => {
  // 清除定时器
  clearInterval(timer)
  
  // 取消事件监听
  window.removeEventListener('resize', handleResize)
  
  // 销毁第三方库实例
  chart?.destroy()
})

Vue 2 与 Vue 3 生命周期对比

关键区别:

  1. ​Vue 3 使用 setup 替代了 beforeCreatecreated
  2. 所有钩子都需要显式导入
  3. 命名更语义化(unmount 替代 destroy)​
  4. 组合式API让相关代码更集中

补充 自定义Hooks

什么是自定义 Hook?

自定义 Hook 是 Vue 3 组合式 API 的核心实践方式,本质是一个封装了响应式逻辑的函数。它解决了两个核心问题:

  1. 逻辑复用​ - 避免重复代码
  2. 关注点分离​ - 让组件更专注于模板渲染

就比如,在写到一个组件里面存着在不同逻辑的数据和处理数据的方法时,全部都混用了





  

 图示代码又处理数字数据又处理狗狗数据,数据和处理逻辑没有分开,导致代码阅读时有一定的障碍,这个时候我们就可以使用hooks自定义,将实现同一个功能所需要的数据和方法单独封装在一起

Hook 基础结构

首先注意,hook命名规范是useXxxx.js/ts

1. 计数器 Hook 示例 (useCounter.ts)

import { ref, onMounted } from 'vue'

export default function(initialValue = 0) {
  // 响应式数据
  const count = ref(initialValue)
  
  // 操作方法
  const increment = () => count.value++
  const decrement = () => count.value--
  const reset = () => count.value = initialValue

  // 生命周期
  onMounted(() => {
    console.log('计数器Hook已挂载')
  })

  // 暴露给组件的API
  return {
    count,
    increment,
    decrement,
    reset
  }
}

2. 异步数据 Hook 示例 (useFetch.ts)

import { reactive, onMounted } from 'vue'
import axios from 'axios'

export default function(url: string) {
  const state = reactive({
    data: null as any,
    loading: false,
    error: null as Error | null
  })

  const fetchData = async () => {
    state.loading = true
    try {
      const response = await axios.get(url)
      state.data = response.data
    } catch (err) {
      state.error = err as Error
    } finally {
      state.loading = false
    }
  }

  onMounted(fetchData)

  return {
    ...state,
    refetch: fetchData
  }
}

组件中使用 Hook

1. 基础用法



2. 组合多个 Hook



最佳实践

1. 命名规范

  • 始终以 use 开头 (useXxx)
  • 一个文件只包含一个 Hook
  • 放在 src/hooks 目录下

2. 类型安全

// useUser.ts
import { ref } from 'vue'

interface User {
  id: number
  name: string
  email: string
}

export default function() {
  const user = ref(null)
  
  return {
    user
  }
}

3. 复杂 Hook 示例 (usePagination.ts)

import { reactive, computed } from 'vue'

export default function(totalItems: number, perPage = 10) {
  const state = reactive({
    currentPage: 1,
    perPage
  })

  const totalPages = computed(() => 
    Math.ceil(totalItems / state.perPage)
  )

  const nextPage = () => {
    if (state.currentPage < totalPages.value) {
      state.currentPage++
    }
  }

  return {
    ...state,
    totalPages,
    nextPage
  }
}

与 Vue 2 Mixins 对比

特性 自定义 Hook Mixins
代码组织 按功能组织 按选项合并
命名冲突 无(可重命名解构) 容易冲突
类型支持 完整 TypeScript 支持 支持有限
逻辑复用 显式导入 全局混入
调试 清晰的调用栈 难以追踪逻辑来源

高级技巧

1. 本地存储 Hook (useLocalStorage.ts)

import { ref, watch } from 'vue'

export default function(key: string, defaultValue: any) {
  const data = ref(JSON.parse(localStorage.getItem(key) || 'null') || defaultValue)

  watch(data, (newVal) => {
    localStorage.setItem(key, JSON.stringify(newVal))
  }, { deep: true })

  return data
}

2. 鼠标位置 Hook (useMouse.ts)

import { ref, onMounted, onUnmounted } from 'vue'

export default function() {
  const x = ref(0)
  const y = ref(0)

  const update = (e: MouseEvent) => {
    x.value = e.pageX
    y.value = e.pageY
  }

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

  return { x, y }
}

常见问题解决

1. Hook 之间共享状态

// useSharedState.ts
import { reactive } from 'vue'

const state = reactive({
  count: 0
})

export function useSharedCounter() {
  const increment = () => state.count++
  
  return {
    count: state.count,
    increment
  }
}

2. 响应式解构问题

// ❌ 错误 - 失去响应性
const { count, increment } = useCounter()

// ✅ 正确 - 保持响应性
const counter = useCounter()
// 在模板中使用 counter.count

(未完) 

你可能感兴趣的:(前端,笔记)