一、Vue3的变化
main.js的内容
import { createApp } from 'vue' import App from './App.vue' import './index.css' const app = createApp(App) app.mount('#app')
template的变化:可以不使用div包裹内容
<template> <h1>{{ msg }}</h1> <h2>HelloWorld</h2> </template>
二、常用composition API
2.1、setup
vue3可以使用vue2的 data
和 method
的方法,但更推荐使用setup
export default {
name:'App',
setup(){
// 定义数据
let name = 'zhang'
let age = 18
// 定义函数
function showAge(){
console.log(age) // 直接使用数据,无需使用this指向
}
// 返回一个对象
return {
name,
age,
showAge
}
}
}
- 尽量不要与vue2配置混用
- vue2的配置可以访问到setup中的属性、方法,反过来则不行,setup的优先级更高
2.2、setup注意点
- setup执行的时机:在
beforeCreate
之前执行一次,此时this
是undefined setup
的参数:- props:值为对象,包含外部传递过来的组件以及组件内部声明接收了的属性
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
- slots: 插槽, 相当于
this.$slots
- emit: 分发自定义事件的函数, 相当于
this.$emit
2.3、实现响应式
ref函数(了解)
vue3数据的响应式需要自己进行添加
import {ref} from 'vue' export default { name:'App', setup(){ let name = ref('zhang') let age = ref(18) function changeName(){ name.value = 'Lishi' } // 返回一个对象 return { name, age, showAge } } }
- ref将数据包装成对象,在对其修改值时需要通过
xxx.value
来修改,模板中使用无需.value
- 对于基本类型数据,响应式依然是靠
Object.defineProperty()
来完成的
- ref将数据包装成对象,在对其修改值时需要通过
reactive函数(常用)
reactive
不能处理基本类型的数据,定义的响应式数据是“深层次的”基于 ES6 的
Proxy
实现,通过代理对象操作源对象内部数据进行操作对于数组类型,可以使用索引进行修改和读取值
setup(){ let num = reactive(['1','2','3']) function changeNum(){ num[0] = '0' } return { num, changeNum } }
ref对比reactive
从定义数据角度对比
ref
用来定义:基本类型数据reactive
用来定义:对象(或数组)类型数据- 备注:
ref
也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive
转为代理对象
从原理角度对比
ref
通过类中的的getter
与setter
来实现响应式(数据劫持)reactive
通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据
从使用角度对比
ref
定义的数据:操作数据需要.value
,读取数据时模板中直接读取不需要.value
- reactive定义的数据:操作数据与读取数据:均不需要
.value
2.4、计算属性
计算属性的使用要 按需引入
import { computed } from 'vue'
computed的使用
let fullname = computed(function() { ...... return }) //完整写法 let fullname = computed(function() { get(){ return }, set(value){ ...... } })
2.5、监视属性
监视ref的属性
immediate:true
刷新时调用一下handler语句deep:true
开启深度监视
// 监视单个属性或对象 watch( sum , (newValue,oldValue) => { console.log('sum改变了',newValue,oldValue) },{immediate:true} // 监视多个属性或对象 watch( [sum,msg] , (newValue,oldValue)=>{ console.log('sum或msg变化了',newValue,oldValue) })
监视reactive的属性
- 监视reactive定义的响应式数据时,无法正确获得
oldValue
且强制为深度监视 - 若监视的是reactive定义的对象中的某个属性,此时deep配置有效,否则deep配置无效
// 监视单个属性或对象整体 watch( person , (newValue,oldValue) => { console.log('person变化了',newValue,oldValue) },{immediate:true,deep:false}) //deep配置无效 // 监视对象内的某个属性 watch( ()=>person.name ,(newValue,oldValue)=>{ console.log('name变化了',newValue,oldValue) } // 监视对象内的一堆属性 watch( [()=>person.name , ()=>person.age],(newValue,oldValue)=>{ console.log('name或age变化了',newValue,oldValue) }
- 监视reactive定义的响应式数据时,无法正确获得
watchEffect
函数watch
:既要监视属性,也要监视属性的回调watchEffect
:无需指明监视哪个属性,监视的回调中用到哪些属性就监视谁watchEffect( ()=>{ const a = person.name const b = sum.value console.log('回调执行了') }) // 此时监视person.name 和 sum.value
2.6、生命周期
vue3与vue2对比,有两个被更名:
beforeDestroy
改名为beforeUnmount
destroyed
改名为unmounted
- 组合式API里的钩子会比配置项的钩子先执行
2.7、自定义hook
- 创建一个hooks文件夹,里面创建文件
自定义函数名.js
- 本质上是封装,类似vue2的mixin
使用:
//---------useXXX.js--------------// import { ...... } from "vue"; export default function() { //数据 ...... //方法 ...... //生命周期钩子 ...... return xxx; } //------组件内使用--------// import useXXX from '../hooks/useXXX'
2.8、toRef与toRefs
- 创建一个
ref
对象,其value值指向另一个对象中的某个属性(类似与指针指向地址的值,并为ref对象的形式) 用法:
setup(){ let num = { a:1, b:2, sum:{ c:10 } } return { a:toRef(num , 'a'), // 单独暴露出a,此时在模板里面可以直接用a而不是num.a c:toRef(num.sum.c , 'c'),// 单独暴露出c,此时在模板里面可以直接用c而不是num.sum.c ...toRefs(num), //此时深度暴露num内所有数据,在模板里面可以直接用a、b,但是对于c,需要使用sum.c } }
三、其他API
3.1、shallowReactive
与 shallowRef
- 用于处理浅层次的响应式
shallowReactive
只处理对象最外层属性的响应式,shallowRef
只处理基本数据类型的响应式
3.2、readonly
与 shallowReadonly
readonly
: 让一个响应式的数据深层次的变为只读shallowReadonly
:让一个响应式数据浅层次的变为只读setup(){ let num = { a:1, b:2, sum:{ c:10 } num = readonly(num) // 此时num内部所有属性均为只读 num = shallowReadonly(num) // 此时num内部除了c,其他均为只读 return{ ...toRefs(num) } }
3.3、toRaw
与 markRaw
toRaw
- 作用:将一个由
reactive
生成的响应式对象转为普通对象(去响应式) - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,比如还原json响应式的原数据
- 作用:将一个由
markRaw
- 作用:标记一个对象,使其永远不会再成为响应式对象
应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
3.4、customRef
- 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
- 用例:
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
// 获取数据怎么做
get() {
track(); // 通知Vue追踪value的变化
return value
},
// 如何交出数据---设置延时再显示数据
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger() // 通知Vue去重新解析模板
}, delay)
}
}
})
}
// 使用
let num = useDebouncedRef(18)
3.5、provide
与 inject
- 组件间通信,实现祖组件与后代组件间通信
用例:
祖组件
import { reactive , provide } from 'vue' setup(){ ...... let num2 = reactive({a:1,b:2}) provide('num2',num2) // 给后代组件传递数据 }
后代组件
import { reactive , inject } from 'vue' setup(props,context){ const num2 = inject('num2') // 获取祖组件的数据 return {num2} }
3.6、响应式数据的判断
isRef
: 检查一个值是否为一个ref
对象isReactive
: 检查一个对象是否是由reactive
创建的响应式代理isReadonly
: 检查一个对象是否是由readonly
创建的只读代理isProxy
: 检查一个对象是否是由reactive
或者readonly
方法创建的代理
四、新组件
4.1、Teleport
- 作用:能够将组件html结构移动到指定位置
用例:
<teleport to="移动位置"> //to后直接使用html元素或元素选择器 <div id="box1"> <h3>测试文字--box1</h3> </div> </teleport>
4.2、Suspense
- 等待异步组件时渲染一些额外内容,先加载完成的先出来
用例:
// 引入 import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
<template> <div class="app"> <h3>App</h3> <Suspense> // default:就是组件要显示的内容 <template v-slot:default> <Demo /> </template> // fallback:组件没加载出来时显示的内容 <template v-slot:fallback> <h3>正在加载.....</h3> </template> </Suspense> </div> </template>