### setup
setup是所有组合API "表演的舞台"
组件中所用到的:数据 方法等等,均要配置在setup中
setup函数的返回值:返回一个对象 则对象中的属性,方法,在模板中可以直接使用
注意:
1.vue3尽量不要与vue2配置混用
2.setup不能是一个async函数,因为返回值不再是reture的对象,而是promise,模板看不到return对象中的属性
### ref函数
作用:定义一个响应式的数据
接收的数据可以是:基本类型 对象类型
1.创建一个包含响应式数据的引用对象:
2.js中操作数据:
3.模板中读取数据:
### reactive函数
作用:定义一个对象类型的响应式的数据 (基本类型不要用它 要用ref函数)
语法:
const 代理对象 = reactive (源对象) 接收一个对象(或数组) 返回一个代理对象(Proxy的实例对象)
### 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:收到的插槽内容 相当于this.$slots
emit:分发自定义事件的函数 相当于 this.$emit
### computed函数
与vue2中的配置功能一致
写法:
<template>
<h1>一个人的信息</h1>
姓:<input type="text" v-model="person.firstName">
<br>
<br>
名: <input type="text" v-model="person.lastName">
<br>
<br>
<span>全名:{{person.fullName}}</span>
<br>
<br>
<input type="text" v-model="person.fullName">
</template>
<script>
import { reactive,computed } from "vue";
export default {
name: "computed",
setup() {
// 数据
let person = reactive({
firstName:'蒋',
lastName:'丞'
})
// 计算属性 --简写 (没有考虑计算属性被修改的情况)
// person.fullName = computed(()=>{
// return person.firstName + '-' + person.lastName
// })
// 完整写法
person.fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
// 返回一个对象
return {
person
};
},
};
</script>
### watch函数
与vue2中的配置功能一致
两个小坑:
监视reactive定义的响应式数据时:oldValue无法正确获取,强制开启了深度监视(deep配置无效)
监视reactive定义的响应式数据中某个属性时:deep配置有效
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">点我+1</button>
<hr>
<h2>当前的信息为:{{msg}}</h2>
<button @click="msg+='!'">修改信息</button>
<hr>
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<h2>薪资:{{person.job.j1.salary}}K</h2>
<button @click="person.name+='~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">涨薪</button>
</template>
<script>
import {ref,reactive,watch} from 'vue'
export default {
name: 'Watch',
setup(){
//数据
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'蒋丞',
age:18,
job:{
j1:{
salary:20
}
}
})
//情况一:监视ref所定义的一个响应式数据
/* watch(sum,(newValue,oldValue)=>{
console.log('sum变了',newValue,oldValue)
},{immediate:true}) */
//情况二:监视ref所定义的多个响应式数据
/* watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变了',newValue,oldValue)
},{immediate:true}) */
/*
情况三:监视reactive所定义的一个响应式数据的全部属性
1.注意:此处无法正确的获取oldValue
2.注意:强制开启了深度监视(deep配置无效)
*/
/* watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{deep:false}) //此处的deep配置无效 */
//情况四:监视reactive所定义的一个响应式数据中的某个属性
/* watch(()=>person.name,(newValue,oldValue)=>{
console.log('person的name变化了',newValue,oldValue)
}) */
//情况五:监视reactive所定义的一个响应式数据中的某些属性
/* watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
console.log('person的name或age变化了',newValue,oldValue)
}) */
//特殊情况
/* watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive所定义的对象中的某个属性,所以deep配置有效 */
//返回一个对象(常用)
return {
sum,
msg,
person
}
}
}
</script>
### watchEffect函数
watch函数的套路是:既要指明监听的属性,也要指明监听的回调
watchEffect函数的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
watchEffect函数有点像computed:
但computed注重计算出来的值(回调函数的返回值),所以必须要写返回值
而watchEffect更注重的是过程(回调函数的返回体),所以不用写返回值
<!-- watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调-->
<template>
<h2>当前求和为:{{ sum }}</h2>
<button @click="sum++">点我+1</button>
<hr />
<h2>当前的信息为:{{ msg }}</h2>
<button @click="msg += '!'">修改信息</button>
<hr />
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>薪资:{{ person.job.j1.salary }}K</h2>
<button @click="person.name += '~'">修改姓名</button>
<button @click="person.age++">增长年龄</button>
<button @click="person.job.j1.salary++">涨薪</button>
</template>
<script>
import { ref, reactive, watch, watchEffect } from "vue";
export default {
name: "watchEffect",
setup() {
//数据
let sum = ref(0);
let msg = ref("你好啊");
let person = reactive({
name: "张三",
age: 18,
job: {
j1: {
salary: 20,
},
},
});
//监视
/* watch(sum,(newValue,oldValue)=>{
console.log('sum的值变化了',newValue,oldValue)
},{immediate:true}) */
watchEffect(() => {
const x1 = sum.value;
const x2 = person.job.j1.salary;
console.log("watchEffect所指定的回调执行了");
});
//返回一个对象(常用)
return {
sum,
msg,
person,
};
},
};
</script>
### 自定义hook函数
本质是一个函数 把setup函数中使用的Composition API进行了封装
自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂
### toRef和toRefs
作用:创建一个ref对象,其value值指向另一个对象中的某个属性
语法:const name = toRef(person,'name')
应用:要将响应式对象中的某个属性单独提供给外部使用时
扩展:toRef和toRefs功能一致,但是toRefs可以批量创建多个ref对象 语法:toRefs(person)
<template>
<h4>{{person}}</h4>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import {ref,reactive,toRef,toRefs} from 'vue'
export default {
name: 'Demo',
setup(){
//数据
let person = reactive({
name:'蒋丞',
age:18,
job:{
j1:{
salary:20
}
}
})
// const name2 = toRef(person,'name')
// console.log('####',name2)
const x = toRefs(person)
console.log('******',x)
//返回一个对象(常用)
return {
person,
// name:toRef(person,'name'),
// age:toRef(person,'age'),
// salary:toRef(person.job.j1,'salary'),
...toRefs(person)
}
}
}
</script>
### shallowReadonly 与 readonly
readonly:让一个响应式数据变为只读的(深只读)
shallowReadonly:让一个响应式数据变为只读的(浅只读)
应用场景:不希望数据被修改时
<template>
<h4>当前求和为:{{sum}}</h4>
<button @click="sum++">点我++</button>
<hr>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import {ref,reactive,toRefs,readonly,shallowReadonly} from 'vue'
export default {
name: 'Demo',
setup(){
//数据
let sum = ref(0)
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
// person = readonly(person)
person = shallowReadonly(person) //深层次不受影响
// sum = readonly(sum)
// sum = shallowReadonly(sum)
//返回一个对象(常用)
return {
sum,
...toRefs(person)
}
}
}
</script>
### toRaw 与 markRaw
toRaw:
作用:将一个由reactive生成的响应式对象转化为普通对象
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面的更新
markRaw:
作用:标记一个对象 使其永远不会再成为响应式对象
应用场景:
1.有些值不应该被设置为响应式的,例如复杂的第三方类库等
2.当渲染具有不可变数据源的大列表时 条过响应式转换可以提高性能
<template>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<h3 v-show="person.car">座驾信息:{{person.car}}</h3>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
<button @click="showRawPerson">输出原始的percon</button>
<button @click="addCar">给人添加一台车</button>
<button @click="person.car.name+='!'">换台车</button>
<button @click="person.car.price++">换价格</button>
</template>
<script>
import {ref,reactive,toRefs,toRaw, markRaw} from 'vue'
export default {
name: 'Demo',
setup(){
//数据
let person = reactive({
name:'蒋丞',
age:18,
job:{
j1:{
salary:20
}
}
})
function showRawPerson(){
const p = toRaw(person)
p.age++
console.log(p);
}
function addCar(){
let car = {name:'凯迪拉克',price:40}
person.car = markRaw(car)
}
//返回一个对象(常用)
return {
person,
...toRefs(person),
showRawPerson,
addCar
}
}
}
</script>
### customRef
<template>
<input type="text" v-model="keyword" />
<h3>{{ keyword }}</h3>
</template>
<script>
import { ref, customRef } from "vue";
export default {
name: "APP",
setup() {
// 自定义一个ref---名为:nyRef
function myRef(value, delay) {
let timer;
return customRef((track, trigger) => {
return {
get() {
console.log(`有人从myRef这个容器中读取数据来,我把${value}给他了`);
track(); //通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
return value;
},
set(newValue) {
console.log(`有人把myRef这个容器中数据改为了:${newValue}`);
//防抖
clearTimeout(timer);
// 延时展示
timer = setTimeout(() => {
value = newValue;
trigger(); //通知Vue去重新解析模板
}, delay);
},
};
});
}
let keyword = myRef("hello",500); //使用自定义的ref
return { keyword };
},
};
</script>
<style>
</style>
### provide 与 inject
作用:实现祖与后代组件间通信
套路:父组件有一个 provide选项来提供数据 后代组件有一个inject选项来开始使用这些数据
具体写法:
祖组件中:
setup() {
let car = reactive({name:'凯迪拉克',price:'40W'})
provide('car',car) //给自己的后台组件传递数据
return {...toRefs(car)}
},
后代组件中:
setup() {
let car = inject('car') //给后代组件注入数据
return {car}
},
###
响应式数据的判断
console.log(isRef(sum)); //检查一个值是否为一个ref对象
console.log(isReactive(car)); //检查一个对象是否是由reactive创建的应式处理
console.log(isReadonly(car2)); //检查一个对象是否是由readonly创建的读代理
console.log(isProxy(car)); //检查一个对象是否由reactive或者readonly法创建的代理
## Composition API 的优势
可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。
### 新的组件
## 1.Fragment
在Vue2中: 组件必须有一个根标签
在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
好处: 减少标签层级, 减小内存占用
## 2.Teleport
- 什么是Teleport?—— `Teleport` 是一种能够将我们的组件html结构移动到指定位置的技术。
<teleport to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>