一、初识 vue3
vue3 简介:
2020 年 9 月 18 日发布 3.0 版本,代号大海贼时代来临,One Piece。
特点包括无需构建步骤、渐进式增强静态的 HTML、可作为 Web Components 嵌入、支持单页应用 (SPA)、全栈 / 服务端渲染 (SSR)、Jamstack / 静态站点生成 (SSG)、可开发多种界面等。
Vue3 带来了什么:
打包大小减少 40%。
初次渲染快 55%,更新渲染快 133%。
内存减少 54%。
分析目录结构:
main.js中的引入:引入的是名为
createApp的工厂函数,而不是Vue构造函数。在模板中 vue3 可以没有根标签,应用实例可以创建多个,且每个都有自己的作用域。
安装 vue3 的开发者工具的方式有在 chrome 应用商店搜索或离线模式下将包丢到扩展程序。
二、常用 Composition API(组合式 API)
setup 函数:
理解:Vue3.0 中一个新的配置项,值为一个函数。
是所有 Composition API 的“表演舞台”,组件中用到的数据、方法等均在此配置。
两种返回值:若返回一个对象,对象中的属性、方法在模板中可直接使用;若返回一个渲染函数,则可自定义渲染内容。
注意点:尽量不与 Vue2.x 配置混用,Vue2.x 配置中不能访问 setup 中的属性,但 setup 中不能访问 Vue2.x 配置,若有重名,setup 优先,setup 不能是 async 函数。
关于单文件组件
<script setup></script>:每个 *.vue 文件最多包含一个,其代码会被预处理为组件的 setup() 函数,且顶层绑定会自动暴露给模板。
基本语法包括顶层的绑定会被暴露给模板,import 导入的内容也能直接在模板中使用。
响应式状态需要明确使用响应式 API 来创建,ref 在模板中使用时会自动解包。
使用组件时,
<script setup>范围里的值能直接作为自定义组件的标签名使用,包括动态组件、递归组件、命名空间组件。使用自定义指令时,全局注册的自定义指令正常工作,本地的自定义指令在
<script setup>中不需要显式注册,但必须遵循vNameOfDirective命名规范。defineProps()和defineEmits()用于在声明 props 和 emits 选项时获得完整的类型推导支持,它们会自动在<script setup>中可用,且传入的选项会从 setup 中提升到模块的作用域。defineExpose可显式指定在<script setup>组件中要暴露出去的属性。useSlots()和useAttrs()在<script setup>中使用 slots 和 attrs 的罕见场景中使用,分别用useSlots和useAttrs两个辅助函数。<script setup>可以和普通的<script>一起使用,普通的<script>在有声明无法在<script setup>中完成的需求时使用。<script setup>中可以使用顶层 await,结果代码会被编译成async setup()。
ref 函数:
作用:定义一个响应式的数据。
语法:
const xxx = ref(initValue),创建一个包含响应式数据引用对象(reference 对象)。JS 中操作数据:
xxx.value,模板中读取数据:不需要.value,直接:{{xxx}}。备注:接收的数据可以是基本类型或对象类型,基本类型的数据响应式靠
Object.defineProperty()的get和set完成,对象类型的数据内部“求助”了reactive函数。
reactive 函数:
作用:定义一个对象类型的响应式数据(基本类型别用它,用
ref函数)。语法:
const 代理对象 = reactive(被代理对象),接收一个对象(或数组),返回一个代理对象(proxy 对象)。reactive定义的响应式数据是“深层次的”,内部基于 ES6 的Proxy实现,通过代理对象操作源对象内部数据进行操作。
Vue3.0 中响应式原理:
vue2 的响应式原理:对象类型通过
Object.defineProperty()对属性进行拦截,数组类型通过重写更新数组的方法实现拦截。vue3 的响应式原理:通过
Proxy拦截对象中任意属性的变化,包括属性值的读写、添加、删除等,通过Reflect对被代理对象的属性进行操作。
reactive 对比 ref:
从定义数据角度对比:
ref用来定义基本数据类型,reactive用来定义对象(或数组)类型数据,ref也可以定义对象(或数组)类型数据,内部会自动通过reactive转为代理对象。从原理角度对比:
ref通过Object.defineProperty()的get和set实现响应式,reactive通过Proxy实现响应式,并通过Reflect操作源对象内部的数据。从使用角度对比:
ref定义数据操作数据需要.value,读取数据时模板中直接读取不需要.value;reactive定义的数据操作数据和读取数据均不需要.value。
setup 的两个注意点:
setup 执行的时机:在
beforeCreate之前执行一次,this是undefined。setup 的参数:props 值为对象,包含组件外部传递过来且组件内部声明接收的属性;context 是上下文对象,包含
attrs(组件外部传递过来但没有在 props 配置中声明的属性,相当于this.$attrs)、slots(收到插槽的内容,相当于$slots)、emit(分发自定义事件的函数,相当于this.$emit)。
计算属性与监视:
computed函数:与 vue2.x 中的写法一致,需要引入computed。watch函数:和computed一样需要引入 api,有两个小坑:监视reactive定义的响应式数据时,oldValue无法获取到正确的值,强制开启了深度监视(deep配置无效);监视reactive定义的响应式数据中某个属性时,deep配置有效。watchEffect函数:套路是不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性,注重逻辑过程,不用写返回值。
生命周期函数:
通过组合式 API 的形式使用生命周期钩子,
beforeCreate和created这两个生命周期钩子相当于 setup,不需要,其他生命周期钩子对应关系为beforeMount ===> onBeforeMount、mounted ===> onMounted、beforeUpdate ===> onBeforeUpdate、updated ===> onUpdated、beforeUnmount ===> onBeforeUnmount、unmounted ===> onUnmounted。
自定义 hook 函数:
本质是一个函数,把 setup 函数中使用的 Composition API 进行了封装,类似于 vue2.x 中的 mixin,优势是复用代码,让 setup 中的逻辑更清楚易懂。
使用 hook 实现鼠标打点:创建
usePoint.js文件,包含实现鼠标打点的数据、方法和生命周期钩子,在组件中使用。
toRef:
作用:创建一个
ref对象,其value值指向另一个对象中的某个属性值。语法:
const name = toRef(person, ‘name’)。应用:要将响应式对象中的某个属性单独提供给外部使用。
扩展:
toRefs与toRef功能一致,但是可以批量创建多个ref对象,语法:toRefs(person)。
三、TypeScript 与组合式 API
为组件的 props 标注类型:
使用
<script setup>时,可直接在defineProps中标注类型,也可将 props 的类型移入一个单独的接口中。不使用
<script setup>时,在defineComponent中设置props属性并标注类型。注意点:传给
defineProps()的泛型参数必须是特定的类型之一。Props解构默认值可通过目前实验性的响应性语法糖解决。
为组件的 emits 标注类型:
使用
<script setup>时,可直接在defineEmits中标注类型,也可基于类型进行标注。不使用
<script setup>时,在defineComponent中设置emits属性并标注类型。
为 ref() 标注类型:
ref会根据初始化时的值推导其类型,也可通过使用Ref这个类型或在调用ref()时传入泛型参数来指定类型,若指定泛型参数但没有给出初始值,得到的将是一个包含undefined的联合类型。
为 reactive() 标注类型:
reactive()会隐式地从它的参数中推导类型,也可显式地标注一个 reactive 变量的类型,使用接口。
为 computed() 标注类型:
computed()会自动从其计算函数的返回值上推导出类型,也可通过泛型参数显式指定类型。
为事件处理函数标注类型:
在处理原生 DOM 事件时,应显式地为事件处理函数的参数标注类型,需要显式地强制转换 event 上的属性。
为 provide / inject 标注类型:
使用
InjectionKey接口来同步注入值的类型,建议将注入 key 的类型放在一个单独的文件中。当使用字符串注入 key 时,注入值的类型是
unknown,需要通过泛型参数显式声明,若提供了默认值,undefined类型可被移除,若确定该值将始终被提供,还可以强制转换该值。
为模板引用标注类型:
模板引用需要通过一个显式指定的泛型参数和一个初始值
null来创建,为了严格的类型安全,在访问el.value时使用可选链或类型守卫。
为组件模板引用标注类型:
为子组件添加模板引用时,首先需要通过
typeof得到其类型,再使用TypeScript内置的InstanceType工具类型来获取其实例类型,注意若在 TypeScript 文件中使用此技巧,需要开启 Volar 的 Takeover 模式。
四、Vuex 与组合式 API
组合式 API 可以通过调用
useStore函数,在setup钩子函数中访问store,与在组件中使用选项式 API 访问this.$store等效。访问 state 和 getter:
通过创建
computed引用以保留响应性来访问state和getter,与在选项式 API 中创建计算属性等效。
访问 Mutation 和 Action:
在
setup钩子函数中调用commit和dispatch函数来使用mutation和action。
五、其他的 Composition API
shallowReactive 与 shallowRef:
shallowRef创建的数据.value里面是一个object数据类型,不会响应式数据。使用场景:如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ===>
shallowReactive;如果有一个对象数据,后续功能不会修改对象中的属性,而是生新的对象来替换 ===>shallowRef。
readonly 与 shallowReadonly:
readonly:让一个响应式的数据变成只读的(深只读)。shallowReadonly:让一个响应式数据变成只读的(浅只读)。应用场景:不希望数据被修改的时候。
toRaw 与 markRaw:
toRaw:将一个由reactive生成的响应式对象转换为普通对象,用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。markRaw:标记一个对象,使其永远不会再成为响应式对象,应用场景包括有些值不应被设置成响应式的,例如复杂的第三方类库等,以及当渲染具有不可变数据的大列表时候,跳过响应式转换可以提高性能。
customRef:
创建一个自定义的
ref,并对其依赖项跟踪和更新触发进行显示控制,可实现防抖效果。
provide 与 inject:
作用:实现祖孙组件间的通信。
套路:父组件有一个
provide选项提供数据,子组件有一个inject选项来开始使用这些数据。
响应式数据的判断:
isRef:检查一个值是否为ref对象。isReactivce:检查一个对象是否是由reactive创建的响应式代理。isReadonly:检查一个对象是否由readonly创建的只读代理。isProxy:检查一个对象是否由reactive或者readonly方法创建的代理。