学习参考视频:尚硅谷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、了解插槽 |
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 |
实例销毁后 | 清理内存/移除事件监听 |
计数器: {{ count }}
组件初始化时:
1. setup阶段 - 数据初始化完成
3. mounted - 组件已挂载到DOM
点击"增加"按钮时:
2. 数据变化触发更新流程
4. updated - DOM已更新
点击"卸载组件"时:
子组件 unmounted
setup()
执行onBeforeMount
setup()
执行onBeforeMount
onMounted
(子组件先完成挂载)onMounted
(父组件最后完成挂载)onMounted(async () => {
// 加载初始数据
const response = await fetch('/api/data')
data.value = await response.json()
// 初始化图表
chart = new Chart(document.getElementById('chart'), {
// 配置项
})
})
onUpdated(() => {
// 典型用例:自动滚动到底部
listContainer.value.scrollTop = listContainer.value.scrollHeight
})
onUnmounted(() => {
// 清除定时器
clearInterval(timer)
// 取消事件监听
window.removeEventListener('resize', handleResize)
// 销毁第三方库实例
chart?.destroy()
})
setup
替代了 beforeCreate
和 created
自定义 Hook 是 Vue 3 组合式 API 的核心实践方式,本质是一个封装了响应式逻辑的函数。它解决了两个核心问题:
就比如,在写到一个组件里面存着在不同逻辑的数据和处理数据的方法时,全部都混用了
当前求和为:{{sum}}
图示代码又处理数字数据又处理狗狗数据,数据和处理逻辑没有分开,导致代码阅读时有一定的障碍,这个时候我们就可以使用hooks自定义,将实现同一个功能所需要的数据和方法单独封装在一起
首先注意,hook命名规范是useXxxx.js/ts
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
}
}
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
}
}
Count: {{ counter.count }}
{{ counter.count }}
Loading...
{{ posts.data }}
use
开头 (useXxx
)src/hooks
目录下// useUser.ts
import { ref } from 'vue'
interface User {
id: number
name: string
email: string
}
export default function() {
const user = ref(null)
return {
user
}
}
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
}
}
特性 | 自定义 Hook | Mixins |
---|---|---|
代码组织 | 按功能组织 | 按选项合并 |
命名冲突 | 无(可重命名解构) | 容易冲突 |
类型支持 | 完整 TypeScript 支持 | 支持有限 |
逻辑复用 | 显式导入 | 全局混入 |
调试 | 清晰的调用栈 | 难以追踪逻辑来源 |
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
}
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 }
}
// useSharedState.ts
import { reactive } from 'vue'
const state = reactive({
count: 0
})
export function useSharedCounter() {
const increment = () => state.count++
return {
count: state.count,
increment
}
}
// ❌ 错误 - 失去响应性
const { count, increment } = useCounter()
// ✅ 正确 - 保持响应性
const counter = useCounter()
// 在模板中使用 counter.count
(未完)