Vue3-02 响应式API
Vue3学习笔记-02 响应式API
升级到Vue3
升级VueCLI
VueCLI需要在4.3.1以上才可以支持Vue3
1 | npm update -g @vue/cli |
创建项目
1 | vue create vue3-learning |
在创建项目时选择手动添加配置,选择vue-router和Vuex,这样创建完的项目各个插件也都会升级为支持Vue3的版本
1 | { |
创建Vue实例
1 | import {createApp} from 'vue'; |
创建Router
1 | import {createRouter, createWebHistory} from 'vue-router'; |
创建路由的方式与以前有所变化,路由模式除了Hash模式(createWebHashHistory
)和History模式(createWebHistory
),还多了带缓存的History路由(createMemoryHistory
)
使用路由跳转的API也有所变化:
1 | import {useRouter} from 'vue-router'; |
关于router的具体变化,后面再单独学习
创建Store
1 | import {createStore} from 'vuex'; |
使用:
1 | import {useStore} from 'vuex'; |
可以看出来,Vuex和vue-router,API都有了一些变化,与React Hooks的API很类似,但是基本原理没有太大变化
setup
setup
函数是新的Composition API的入口点
调用时机
setup
在创建组件之前调用(在beforeCreate
之前),Props初始化后就会调用setup
函数,在beforeCreate
钩子前被调用
返回值
setup
返回的对象的属性将被合并到组件模板的渲染上下文,也可以返回一个渲染函数
参数
接受两个参数:
props
context
props
接受props
作为第一个参数,使用的时候,需要首先声明props
:
1 | export default { |
==props
是响应式的,前提是不对props
进行解构,解构后会失去响应性==
如果需要结构props
,可以通过使用roRefs
来完成解构操作,并且保持响应式:
1 | import { toRefs } from 'vue' |
如果title
是一个可选的Prop,此时toRefs
不会为title
创建一个ref
,需要使用toRef
来创建响应式变量:
1 | import { toRef } from 'vue' |
context
setup
第二个参数是上下文对象context
,从2.x中的this
选择性地暴露出三个组件的属性attrs
、slots
、emit
。context
就是一个普通的JavaScript对象,它不是响应式的,所以可以对context
进行解构,不需要担心失去响应性
1 | export default { |
this
的用法
this
在setup
中不可用,它并不会指向该组件实例
访问组件的Property
执行setup
时,==组件实例尚未被创建==,因此只能访问slots
/props
/attrs
/emit
这些组件属性,无法访问computed
、data
、methods
组件选项
结合模板使用
如果setup
返回一个对象,那么可以在组件模板中直接使用,就如同使用Props一样
1 | <template> |
setup
返回的refs
在模板中访问是被自动解开的,不需要在模板中使用.value
生命周期
生命周期钩子函数只能在setup
期间同步使用,在组件卸载时,生命周期内部创建的侦听器和计算状态也会被自动删除
与Vue2.x相比,beforeCreated
和created
被删除了,对应的逻辑在setup
内部完成,其他的生命周期钩子都改为了onXxx
的形式(beforeDestoryed
改为了onBeforeUnmount
,destroyed
改为了onUnmounted
)
两个新增的调试钩子函数onRenderTracked
和onRenderTriggered
:
1 | export default { |
函数接受一个回调函数,当钩子被组件调用时将会被执行
1 | export default { |
依赖注入
基本使用
使用provide
和inject
实现依赖注入,与2.x版本中基本一致,只能在setup
中使用
1 | import { provide, inject } from 'vue' |
使用ref
传值可以保证provided
和injected
之间值的响应性
响应式修改
在使用响应式provide
/inject
时,建议尽可能,==在提供者内保持响应式Property的任何更改==
如果需要在注入数据的组件内部更新inject
的数据,在这种情况下,建议provide
一个方法来负责改变响应式Property的方法,让注入方调用
如果要确保通过provide
传递的数据不会被inject
的组件更改,可以对提供者的Property使用readonly
1 | import { provide, reactive, readonly, ref } from 'vue' |
getCurrentInstance
如果在setup
中要访问组件实例,需要使用这个方法来访问,例如在app.config.globalProperties
定义了全局变量,如果希望在setup
中访问,就要用到这个方法:
1 | // main.js |
注意,它只能在setup
和生命周期钩子中调用,如果在其外调用(例如Event Listener中)需要在setup
中调用getCurrentInstance()
获取实例后传入对应的函数中使用
1 | const MyComponent = { |
模板Refs
常规使用
Vue2.x中的ref
原本是用于获取DOM的, Vue3中ref
不仅可以响应化数据,也可以实现获取DOM的功能
1 | <template> |
在setup
中声明一个ref
并返回,在模板中声明ref
并且值与返回的ref
相同,这时在渲染初始化后(onMounted
)就可以获取分配的DOM或组件实例
在v-for
中使用
在v-for
中使用时,需要使用3.0新增的函数形的ref
,为ref
赋值:
1 | <template> |
侦听模板引用
在watch
或者watchEffect
中引用Dom Ref,需要使用flush: 'post'
选项,因为默认情况下watch
和watchEffect
的副作用函数是在DOM被挂载或者更新之前运行的,此时模板引用还没有被更新
使用了flush: 'post'
,保证监听器在DOM更新后执行副作用,确保模板引用于DOM保持同步,引用正确元素
组合式API实现更灵活的复用
Mixin的缺点
- 容易发生冲突
- 可复用性优先,不能向Mixin传递参数来改变逻辑,降低了在抽象逻辑方面的灵活行
Vue3的组合式API就是为了解决这些问题而存在的
更多的灵活性来自更多的自我克制
组合式API的初衷就是为了实现更有组织的代码,实现更灵活的逻辑提取与复用,在代码中会出现更多的、零碎的函数模块,在不同的位置、不同的组件间进行重复调用
它可以避免Vue2.x时代逻辑复用的几种主要形式(Mixin/HOS/SLOT)的弊端,带来了比较明显的好处:
但是它在提到了代码质量的上限的同时,降低了下线,setup
中会出现大量面条式的代码,避免这种糟糕情况的关键就是,将逻辑更合理的划分为单独的函数,将setup
作为一个入口,在其中进行不同组合函数的调用。
与React Hooks比较
Vue3的基于函数的组合式API受到了React Hooks的启发,在很多思维模型方面与React Hooks很类似,提供了同等级别的逻辑组合能力,但是也有着比较明显的不同,组合式API的setup
函数只会被调用一次,也就意味着使用组合式API时:
- 不需要考虑调用顺序,可以用在条件语句中(React Hooks不可以)
- 不会再每次渲染时重复执行,降低垃圾回收的压力(React Hooks每次渲染都会重复执行)
- 不存在内联处理函数导致子组件永远更新的问题,也不需要
useCallback
(React Hooks需要用useCallback
进行性能优化) - 不存在忘记记录依赖的问题,也不需要
useEffecr
和useMemo
并传入依赖数组以捕获过时的变量,Vue的自动以来可以确保侦听器和计算值总是准确无误的(React Hooks需要手动记录依赖)