一、应用创建
2.x
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app')
3.x
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
基于脚手架创建的项目,mian.js中已经创建好应用了,此处了解即可。
二、组合式 API
除了setup,其余api若用到,需要手动引入
import { ref, reactive, watch, watchEffect, computed, provide, inject,
setup
Vue3.0中一个新的配置项,值为一个函数,是所有组合api放置的容器,组件中所用到的数据、方法等,均要配置在setup中。
参数:
props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs,使用attrs.xxx
- slots: 收到的插槽内容, 相当于this.$slots,使用slots.xxx
- emit: 分发自定义事件的函数, 相当于this.$emit
- expose: 暴露公共属性(函数),当父组件通过模板引用访问该组件的实例时,将仅能访问 expose 函数暴露出的内容
setup(props, { attrs, slots, emit, expose }) { ... }
返回值:
1.对象。若返回值是一个对象,对象中所有属性、方法,在<template>模板中可以直接使用。
setup(){
const msg = '你好'
return { msg }
}
2.渲染函数。若返回一个渲染函数,则组件渲染内容会是返回值,不再渲染<template>中编写内容
setup(){
return () => return h('div', '你好啊') //h函数需要引入 import { h } from 'vue'
}
注意:
1.尽量不要与Vue2.x配置混用
- Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。
- 但在setup中不能访问到Vue2.x配置(data、methos、computed...)。
- 如果有重名, setup优先。
2.setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
3.setup执行的时机:在beforeCreate之前执行一次,this是undefined
生命周期钩子
组合api中生命周期钩子与vue3中声明周期对比
ref
作用: 定义一个响应式的数据(RefImpl: reference implement 引用实现的实例对象,简称ref对象)
语法: const xxx = ref(initValue)
- JS中操作数据: xxx.value
- 模板中读取数据: 不需要.value,直接:<div>{ {xxx}}</div>
备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的。
- 对象类型的数据:内部 求助了Vue3.0中的一个新函数——reactive函数。
reactive
作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
语法:const 代理对象= reactive(源对象)
- 接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
- reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象Reflect操作源对象内部数据进行操作。
computed
与2.x中类似,每一个计算属性,都是computed函数的返回值,用法如下:
import { computed } from 'vue'
setup(){
//简写方式
let fullName = computed(() => { return p.firstName+'-'+p.lastName }) //函数
//完整方式
let fullName = computed({ //对象
get(){ return p.firstName+'-'+p.lastName },
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
}
watch
情况一:监视ref定义的响应式数据
watch(sum, (newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
},{immediate:true})
情况二:监视多个ref定义的响应式数据
watch([sum, msg], (newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
})
情况三:监视reactive定义的响应式数据
- 无法正确获得oldValue
- 强制开启了深度监视
watch(person, (newValue, oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
情况四:监视reactive定义的响应式数据中的某个属性
- 此时 deep配置有效
- 此时 能正确获取oldValue的值
watch(()=>person.job, (newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job, ()=>person.name], (newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
注意:
1.若监视的是reactive对象中的某个属性,而这个属性恰好是个对象,此时需要配置{ deep: true }
2.监视ref定义的基本类型时,不需要.value, 直接watch(变量名, ()=>{})即可
监视ref定义的对象类型时,需要.value,watch(变量名.value, ()=>{}) 或者 配置 { deep: true }
watchEffect
watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调,默认开启了immediate
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
- watch的套路是:既要指明监视的属性,也要指明监视的回调。
- watchEffect的套路是:不指明监视哪个属性,监视的回调中用到哪个属性,就监视哪个属性。
- watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
toRef 与 toRefs
创建一个ref对象,其value值指向另一个对象中的某个属性。
const person = reactive({
name: `tom',
job: {
salary: 2000,
}
})
在模板中用到salary时,要 person.job.salary , 可以通过toRef设置,在模板中直接使用salary
toRef 一次只能处理一个对象中的一个属性
const salary = toRef(person.job, `salary`) // 此时 salary 与 person.job.salary会同步
salary.value ++
toRefs 可以批量处理一个对象中的所有属性,将对象中所有属性都变成ref对象,此时支持结构赋值的写法
const p = toRefs(person)
const { name, job } = p //此时 name,job都是ref对象,与 state中属性值同步,不丢失响应性
name.value = `jerry`
job.value.salary ++
shallowRef 与 shallowReactive
shallowRef: 只处理基本类型的响应式,不进行对象的响应式处理,用于不会修改对象属性,而是生成新的对象来替换的情况
shallowReactive: 只处理对象最外层属性的响应式(浅响应式),用于对象结构比较深,但变化时只是外层属性变化的情况
readonly 与 shallowReadonly
readonly: 让一个响应式数据变为只读的(深层次)
shallowReadonly: 让一个响应式数据变为只读的(浅层次)
不希望数据被修改时使用
toRaw 与 markRaw
toRaw:
- 将一个由 reactive 定义的响应式数据,转换为普通对象
- 用于读取响应式对象的普通对象,对这个对象的所有操作,不会引起页面更新
markRaw:
- 标记一个对象,使其永远不会再变成响应式对象
- 用于不应被设置为响应式的值,eg:第三方类库
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
setup(){
const person = reactive{
name: `tom`
} // 此时person是响应式的,再添加不存在的属性也会变成响应式
function addCar(){
... //获取car信息,用于展示,car中数据不会响应式变化
let car = { name: `ben`, price: `40w` }
person.car = markRaw(carInfo)
}
}
customRef
用于创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制
实现 防抖效果
<script setup lang="ts">
import { customRef } from 'vue'
//自定义一个ref-名为myRef
function myRef (value, delay = 2000) {
let timeout
return customRef((track, trigger)=>{
return {
get () {
track()
return value
},
set (newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
let inputValue = myRef(`hello`)
</script>
provide 与 inject
用于祖孙组件间通讯。父组件用provide选项提供数据,子组件用inject选项使用数据
父
setup(){
provide(propName, propValue)
}
子
setup(){
let variable = inject(propName) // variable是响应式的
}
响应式数据的判断
isRef: 检查一个值是否是ref对象
isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
isProxy: 检查一个对象是否是由 reactive 或 readonly 或 shallowReactive 或 shallowReadonly 创建的代理
更多详细介绍,请查看官方文档: