目录
vue2与vue3生命周期对比
OptionsAPI vs CompositionAPI
OptionsAPI
CompositionAPI
为什么Composition API要在setup中
Composition API多逻辑组织方式
nextTick()
nextTick()原理
nextTick使用场景
nextTick使用位置
vue2与vue3生命周期对比
| 阶段 | Vue 2(Options API) | Vue 3(Compositions API) | 说明 |
| 创建前 | beforeCreate | —— | 实例刚创建,数据未绑定 |
| 创建后 | created | setup() | 数据已绑定,但 DOM 未挂载 |
| 挂载前 | beforeMount | onBeforeMount | 模板编译完成,即将挂载 |
| 挂载后 | mounted | onMounted | DOM 已挂载,可访问 $el |
| 更新前 | beforeUpdate | onBeforeUpdate | 数据变化,DOM 未更新 |
| 更新后 | updated | onUpdated | DOM 已更新 |
| 卸载前 | beforeDestroy | onBeforeUnmount | 组件即将销毁 |
| 卸载后 | destroyed | onUnmounted | 组件已销毁 |
| keep-alive | activated/deactivated | onActivated/onDeactivated | 缓存组件激活/停用 |
| 错误捕获 | errorCaptured | onErrorCaptured | 子组件错误捕获 |
| SSR相关 | —— | onServerPrefetch | SSR 数据预取 |
OptionsAPI vs CompositionAPI
| 对比 | Options API | Composition API |
| 适用版本 | Vue 2/Vue 3 | Vue 3专属 |
| 代码组织 | 按选项类型 | 按逻辑功能 |
| 逻辑复用 | mixins | Composable |
| 学习曲线 | 低 | 稍高 |
| 适合场景 | 小型项目 | 中大型项目 |
OptionsAPI
- 在OptionsAPI中使用TypeScript时,需要为每个选项属性分别定义类型。例如,在
data函数中定义的数据需要单独定义类型,methods中的方法也需要定义参数和返回值的类型等。这使得类型定义分散在整个组件定义中,代码结构相对复杂。export default { // 数据 data() { return { count: 0, name: '张三' } }, // 计算属性 computed: { doubleCount() { return this.count * 2 } }, // 方法 methods: { increment() { this.count++ } }, // 生命周期 mounted() { console.log('mounted') } }export default { data() { return { count: 0 } }, computed: { double() { return this.count * 2 , methods: { add() { this.count++ } }, mounted() { console.log('count:', this.count) } }CompositionAPI
- 由于以函数形式组织逻辑,在
setup函数中可以更方便地进行整体的类型定义。可以对setup函数的输入参数和返回值进行类型定义,并且在自定义组合函数中也能很好地定义类型。这种方式与TypeScript的函数类型系统结合得更加紧密,代码的类型定义更加简洁清晰。<script setup> import { ref, computed, onMounted } from 'vue' // 数据 const count = ref(0) const name = ref('张三') // 计算属性 const doubleCount = computed(() => count.value * 2) // 方法 function increment() { count.value++ } // 生命周期 onMounted(() => { console.log('mounted') }) </script>import { ref, computed, onMounted } from 'vue' export default { setup() { const count = ref(0) const double = computed(() => count.val* 2) function add() { count.value++ } onMounted(() => { console.log('count:', count.value) }) return { count, double, add } } }核心优势
逻辑复用示例特性 说明 逻辑复用 通过useXxx()组合函数轻松复用逻辑 代码组织 相关逻辑集中,利于大型项目维护 类型推断 更好的ts支持 更小体积 更灵活的tree-shaking,减小包体积 // useCount.js export function useCount() { const count = ref(0) function add() { count.value++ } return { count, add } } // 组件中使用 const { count, add } = useCount()为什么Composition API要在setup中
Composition API必须在setup阶段调用。Vue 3 的 Composition API 本质上是一组 函数 API ( ref 、 reactive 、 onMounted 等),它们的设计原理是:在组件创建阶段(setup)同步调用,返回响应式数据或注册生命周期回调。
setup是VUE3的入口点
组件实例创建 ↓ setup() 执行 ← Composition API 在这里生效 ↓ template 编译 / DOM 渲染 ↓ onMounted() 执行Vue 2 的 data 、 methods 、 computed 是分散在各个选项中,由 Vue 内部在适当时机调用。
Vue 3 统一改为 setup() 入口,所有逻辑在里面通过函数调用注册。
<script setup> 是 setup() 的 语法糖 ,让代码更简洁:
普通写法
export default { setup() { const count = ref(0) function increment() { count.value++ } onMounted(() => { console.log('mounted') }) return { count, increment } } }<script setup>写法:
<script setup> const count = ref(0) function increment() { count.value++ } onMounted(() => { console.log('mounted') }) // 无需 return,模板直接用 count、increment </script>图解流程
普通 setup(): ┌─────────────────────────────────┐ │ setup() { │ │ const count = ref(0) ← 注册响应式 │ onMounted(...) ← 注册生命周期回调 │ return { count } ← 暴露给模板 │ } │ └────────────────────────────────── <script setup>: ┌───────────────────────────────── │ <script setup> │ │ const count = ref(0) ← 注册响应式(编译器自动处理) │ onMounted(...) ← 注册生命周期(编译器自动处理) │ // 无需 return ← 编译器自动暴露给模板 │ </script> │ └─────────────────────────────────┘| 问题 | 答案 |
| 为什么在 setup 中? | setup 是 Vue 3 的初始化入口,Composition API 函数需要在组件创建时同步调 |
| <script setup> 是什么? | etup 的语法糖,省去 return 语句,编译器自动处理暴露逻辑 |
| 可以不用吗? | Vue 3 仍支持 Options API,但 Composition API 是未来趋势 |
Composition API多逻辑组织方式
核心思想:按功能分组,使用Composables(组合函数)
方式一:使用Composables(推荐)
把相关逻辑抽离到独立的"组合函数"文件中:
src/ composables/ useCount.js # 计数相关 useSearch.js # 搜索相关逻辑 useAuth.js # 认证相关逻辑useCount.js
import { ref, computed } from 'vue' export function useCount() { const count = ref(0) const double = computed(() => count.value * 2) function increment() { count.value++ } function reset() { count.value = 0 } return { count, double, increment, reset } }useSearch.js
import { ref } from 'vue' export function useSearch() { const keyword = ref('') const results = ref([]) async function search() { // 搜索逻辑 } return { keyword, results, search } }组件中使用
<script setup> import { useCount } from '@/composables/useCount' import { useSearch } from '@/composables/useSearch / 按逻辑分组,互不影响 const { count, double, increment } =useCount() const { keyword, results, search } = useSearch() </script>方式二:在setup中按功能分区
<script setup> import { ref, computed, onMounted,onUnmounted } from 'vue' // ========== 1. 用户相关逻辑 ========== const userName= ref('') const userInfo = ref({}) function fetchUser() { /* ... */ } // ========== 2. 分页相关逻辑 ========== const page = ref(1) const pageSize = ref(10) const total = ref(0) function nextPage() { page.value++ } function prevPage() { page.value-- } // ========== 3. 弹窗相关逻辑 ========== const visible = ref(false) function openModal() { visible.value = true } function closeModal() { visible.value = false } // ========== 生命周期 ========== onMounted(() => { fetchUser() }) onUnmounted(() => { /* 清理 */ }) </script>| 场景 | 推荐方式 |
| 逻辑需复用 | Composables( useXxx.js ) |
| 逻辑不复杂,不复用 | setup 内分区注释 |
nextTick()
Vue的DOM更新是异步的,不会立即生效,多个数据变化会合并成一次DOM更新提高性能,nextTick用于在DOM更新完成后执行回调。
流程图
数据变化 → 触发更新 → 加入更新队列 → nextTick → 执行回调 → DOM 已更新 ↑ ↓ | | └── 同步代码继续执行 回调函数中获取到最新 DOMnextTick()原理
Promise+队列机制
Vue3内部维护一个callbacks(回调队列),存放nextTick的回调函数,由microtask执行
| callbacks | microtask |
| Vue内部维护的数组,存放所有nextTick的回调 | js执行机制(任务队列) |
| 存储待执行的函数 | 执行这些函数的时机/方式 |
| 依赖microtask执行 | 提供执行能力 |
Vue用microtask来异步执行callbacks中的回调,比setTimeOut更快,保证在DOM更新后第一时间执行
nextTick使用场景
需要等Vue更新完DOM后再执行操作时,就用nextTick
Vue数据变化->DOM更新是异步的->需要nextTick
- 当需要确保DOM已经更新时
- 当需要访问组件引用或DOM元素时
- 当需要在数据变更后执行依赖DOM操作时
nextTick使用位置
| 位置 | 可用 | 说明 |
| setup() | 是 | 数据响应式已建立,但DOM未渲染 |
| onMounted() | 是 | DOM挂载完成,等待子组件渲染 |
| onUpdated() | 是 | DOM更新后,需等待本次更新完成 |
| methods | 是 | 最常见,数据变化后操作DOM |
| watch | 是 | 响应数据变化,执行DOM操作 |
| computed | 否 | 不推荐,computed是同步的 |
| beforeCreate | 否 | setup()之前,响应式未建立 |
场景对应位置
| 场景 | 推荐位置 |
| 数据变化后获取DOM | methods或watch |
| 组件挂载后初始化 | onMounted |
| 组件更新后操作DOM | onMounted |
| 异步操作后获取DOM | async/await+nextTick |
router
路由是URL路径到组件的映射(如/home->Home组件)
router-view vs router-link
router-view:用于渲染,路由出口,渲染匹配到的组件(根据URL在router-view中显示对应组件)。当一个路由被匹配时,它的组件将会渲染在 < router-view > 中。可以不需要router-link,URL变化时自动渲染对应组件。
router-link:用于跳转,url路径发生变化,导航链接,点击切换路由(默认渲染为<a>),可以将某个链接或者按钮转换成一个具有路由功能的链接。可以不需要router-view,单纯做导航触发。
二者可以组合使用,也可以分开使用。
| 行为 | 含义 |
| 跳转 | URL 路径发生变化(如 /home → /about ) |
| 渲染 | 根据 URL 在 router-view 中显示对应组件 |
只有跳转到内部组件才使用router-view渲染
URL 变化 │ ├── http://xxx ──→ 外部链接 ──→ 浏览器跳转,整个页面卸载 │ └── /about ──→ Vue Router 处理 │ ├── 路由表有匹配 ──→ router-view 渲染对应组件 │ └── 路由表无匹配 ──→ 渲染 404路由跳转的两种方式
| 方式 | 代码 |
| 组件式 | <router-link to="/home">首页</router-link> |
| JS 编程式 | router.push('/home') |