1. setup 配置项
beforeCreate 之前执行 即:setup 中 this 不存在
1. setup 是所有Composition API "表演的舞台"
2. 组件中所用到的所有 data,methods…都要配置到 setup 中
3. setup 函数的返回值:
- 返回一个对象,则对象的属性方法在模板中可以直接使用
- 返回一个渲染函数
1.1 setup 配置项中的三个注意点
- props
- emit
- slots
App.vue
<template>
<Demo name="tom" :age="18" school="atguigu" @showMsg="showMsg">
<!-- 只能用 v-slot log 中才能打印出来 -->
<template v-slot:test1>
<span>这里是父标签给子组件的标签体</span>
</template>
</Demo>
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'App',
components:{Demo},
setup(){
function showMsg(value){
alert(`我是App我收到的数据是:${value}`)
}
return{
showMsg,
}
}
}
</script>
<style>
</style>
Demo.vue
<template>
<h3>这里是Demo组件</h3>
<h3>{{name}}</h3>
<h3>{{school}}</h3>
<h3>{{age}}</h3>
<slot name="test1"></slot><br>
<button @click="showAgeType">显示年龄的类型</button><br>
<button @click="showMsg">点击触发App传来的方法</button>
</template>
<script>
export default {
name:'Demo',
//接收父组件传的数据
props:['name','school','age'],
//接受父组件的函数
emits:['showMsg'],
//setup 能接受的两个参数 setup 没有 this
setup(props,context){
console.log('Demo 中的 setup 配置项执行了...')
console.log(props,context)
console.log('emit是:',context.emit);
console.log('slots是:',context.slots);
function showAgeType(){
console.log(typeof(props.age))
}
function showMsg(){
context.emit('showMsg',666)
}
return{
showAgeType,
showMsg,
}
},
mounted() {
console.log(this)
console.log('Demo 组件挂载了...')
},
}
</script>
<style>
</style>
子组件需要申明接受 父组件传来的全部参数和自定义方法 props 和 emits,不接受收 log 会 warning
computed 计算属性
不同于vue2 计算属性变成了 一个函数 简写形式只触发 get 方法 完整形式可以使用 set 方法
<template>
姓:<input type="text" v-model="person.firstName"><br>
名:<input type="text" v-model="person.lastName"><br>
<span>全名:{{person.fullName}}</span><br>
<input type="text" v-model="person.fullName">
</template>
<script>
import { computed, reactive } from '@vue/reactivity'
export default {
name:'Demo',
setup(){
//使用 reactive 实现对象响应式
let person = reactive({
firstName:'张',
lastName:'三'
})
//reactive 属性追加的属性也是响应式的
//完整写法
// person.fullName = computed({
// get(){
// return person.firstName + '-' + person.lastName
// },
// set(value){
// const nameArr = value.split('-')
// person.firstName = nameArr[0]
// person.lastName = nameArr[1]
// }
// })
//简写形式
person.fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
return{
person,
}
},
}
</script>
<style>
</style>
1.2 watch监视数据变化【注意deep的使用】
监视的是 refImpl 包裹的对象
但是使用 ref 定义对象时,_value 是 Proxy 对象
即:refImpl 对象中的对象 无法进行监视
对象.value 是 Proxy 对象, 可进行监视
也可以对 ref 定义的对象使用 deep 监视
watch(监视对象,回调函数,开启immediate或者开启深度监视等)
reactive 定义的数据 在监视时无法获得 oldValue 无需开启深度监视且没法关闭
如果要监视对象的某一个属性需要使用函数
- 情况1
<template>
<h3>{{sum}}</h3><button @click="sum++">点击按钮改变其值</button>
<h3>{{msg}}</h3><button @click="msg += '!'">点击按钮改变其值</button>
</template>
<script>
import { reactive,ref,watch } from 'vue'
export default {
name:'Demo',
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age : 20,
job:{
salary:20
}
})
// 情况一:watch监视 ref 定义的一个响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum值变了',newValue,oldValue);
},{immediate:true})
return{
sum,
msg
}
},
}
</script>
<style>
</style>
- 情况2
<template>
<h3>{{sum}}</h3><button @click="sum++">点击按钮改变其值</button>
<h3>{{msg}}</h3><button @click="msg += '!'">点击按钮改变其值</button>
</template>
<script>
import { reactive,ref,watch } from 'vue'
export default {
name:'Demo',
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age : 20,
job:{
salary:20
}
})
// 情况二:watch监视 ref 定义的多个响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum值或者msg变了',newValue,oldValue);
},{immediate:true})
return{
sum,
msg
}
},
}
</script>
<style>
</style>
- 情况三
默认开启深度监视,且无法关闭
无法获取 oldValue 值 与 newValue 的值相同
<template>
<h3>{{sum}}</h3><button @click="sum++">点击按钮改变其值</button>
<h3>{{msg}}</h3><button @click="msg += '!'">点击按钮改变其值</button>
<hr>
<h3>{{person.name}}</h3><button @click="person.name += '~'">点击按钮改变其值</button>
<h3>工资:{{person.job.salary}}</h3><button @click="person.job.salary++">点击按钮改变其值</button>
</template>
<script>
import { reactive,ref,watch } from 'vue'
export default {
name:'Demo',
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age : 20,
job:{
salary:20
}
})
// 情况三:watch监视 reactive 定义的一个响应式对象的全部属性
watch(person,(newValue,oldValue)=>{
console.log('person值变了',newValue,oldValue);
},{immediate:true})
return{
sum,
msg,
person
}
},
}
</script>
<style>
</style>
- 情况四
需要使用函数的返回值作为监视的对象
用不到 deep 监视
<template>
<h3>{{sum}}</h3><button @click="sum++">点击按钮改变其值</button>
<h3>{{msg}}</h3><button @click="msg += '!'">点击按钮改变其值</button>
<hr>
<h3>{{person.name}}</h3><button @click="person.name += '~'">点击按钮改变其值</button>
<h3>{{person.age}}</h3><button @click="person.age++">点击按钮改变其值</button>
<h3>工资:{{person.job.salary}}</h3><button @click="person.job.salary++">点击按钮改变其值</button>
</template>
<script>
import { reactive,ref,watch } from 'vue'
export default {
name:'Demo',
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age : 20,
job:{
salary:20
}
})
// 情况四:watch监视 reactive 定义的一个响应式对象的 某个属性
// 需要使用函数 返回谁就是监视谁
watch(()=>person.age,(newValue,oldValue)=>{
console.log('person的age值变了',newValue,oldValue);
},{immediat:true,deep:false})
return{
sum,
msg,
person
}
},
}
</script>
<style>
</style>
- 情况五
<template>
<h3>{{sum}}</h3><button @click="sum++">点击按钮改变其值</button>
<h3>{{msg}}</h3><button @click="msg += '!'">点击按钮改变其值</button>
<hr>
<h3>{{person.name}}</h3><button @click="person.name += '~'">点击按钮改变其值</button>
<h3>{{person.age}}</h3><button @click="person.age++">点击按钮改变其值</button>
<h3>工资:{{person.job.salary}}</h3><button @click="person.job.salary++">点击按钮改变其值</button>
</template>
<script>
import { reactive,ref,watch } from 'vue'
export default {
name:'Demo',
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age : 20,
job:{
salary:20
}
})
// 情况五:watch监视 reactive 定义的一个响应式对象的 某些属性
// 需要使用函数 返回谁就是监视谁
watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{
console.log('person的age值或者name值变了',newValue,oldValue);
},{immediat:true,deep:false})
return{
sum,
msg,
person
}
},
}
</script>
<style>
</style>
- 情况6
需要使用深度监测
开启后也无法能获取 oldValue 值
<template>
<h3>{{sum}}</h3><button @click="sum++">点击按钮改变其值</button>
<h3>{{msg}}</h3><button @click="msg += '!'">点击按钮改变其值</button>
<hr>
<h3>{{person.name}}</h3><button @click="person.name += '~'">点击按钮改变其值</button>
<h3>{{person.age}}</h3><button @click="person.age++">点击按钮改变其值</button>
<h3>工资:{{person.job.salary}}</h3><button @click="person.job.salary++">点击按钮改变其值</button>
</template>
<script>
import { reactive,ref,watch } from 'vue'
export default {
name:'Demo',
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age : 20,
job:{
salary:20
}
})
// 特殊情况:watch监视 reactive 定义的一个响应式对象的 某个深度属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job值变了',newValue,oldValue);
},{immediat:true,deep:true})
return{
sum,
msg,
person
}
},
}
</script>
<style>
</style>
- 情况7
对第六种监视使用 person.job 的形式 能强制开启对下一层整个对象的深度监视
也无法获取 oldValue 值
1.3 watchEffect
回调函数中用谁就会监视谁
<template>
<h3>{{sum}}</h3><button @click="sum++">点击按钮改变其值</button>
<h3>{{msg}}</h3><button @click="msg += '!'">点击按钮改变其值</button>
<hr>
<h3>{{person.name}}</h3><button @click="person.name += '~'">点击按钮改变其值</button>
<h3>{{person.age}}</h3><button @click="person.age++">点击按钮改变其值</button>
<h3>工资:{{person.job.salary}}</h3><button @click="person.job.salary++">点击按钮改变其值</button>
</template>
<script>
import { reactive,ref,watchEffect} from 'vue'
export default {
name:'Demo',
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name:'张三',
age : 20,
job:{
salary:20
}
})
//会点函数中用到了谁 就监视谁 和 vue2中的computed属性类似,只是不注重返回
watchEffect(()=>{
const s1 = sum.value
const s2 = person.job.salary
console.log('watchEffect 中的属性:sum 或 salary 发生了改变')
})
return{
sum,
msg,
person
}
},
}
</script>
<style>
</style>
1.4 toRef 和 toRefs
为了简化模板中的代码【去除对象】并保持响应式
toRef(对象,对象属性)
toRefs(对象) 解析第一层的全部属性
<template>
<h3>用于对比是否修改了到了person:{{person}}</h3>
<h3>{{name}}</h3><button @click="name += '~'">点击修改姓名</button>
<h3>{{age}}</h3><button @click="age++">点击修改年龄</button>
<h3>person.job.salary.yearsSalary:{{person.job.salary.yearsSalary}}</h3><button @click="person.job.salary.yearsSalary++">点击修改年薪</button>
<h3>yearsSalary:{{yearsSalary}}</h3><button @click="yearsSalary++">点击修改年薪</button>
</template>
<script>
import { reactive, toRef, toRefs } from '@vue/reactivity'
export default {
name:'Demo',
setup(){
let person = reactive({
name:'张三',
age:22,
job:{
salary:{
yearsSalary:20
}
},
})
return{
//return对象本身
person,
//toRef 指定对象的某个个属性
yearsSalary:toRef(person.job.salary,'yearsSalary'),
//对象的全部属性,这里不包含对象本省,所以想要在外面使用person对象还需要 return对象本身
...toRefs(person)
}
},
}
</script>
<style>
</style>
2. ref 函数
创建一个包含响应式的引用对象
3. customRef
自定义 ref
track() 追终变化
trigger() 触发从新解析模板
4. provide 和 inject
祖组件给后代组件传递消息的方式
祖组件 provide
后代组件 inject
5. 不常用方法
shallowReactive
第一层数据响应式
shallowRef
第一层响应式 【不求人 reactive】
readonly
数据不允许修改 且 log 警告
【注意】是否与reactive响应式自相矛盾呢?
答案是否定的,不用响应式 不用 readonly 数据可以被修改,只是不是响应式,即:页面不改变,但是如果使用readonly 数据是不允许修改的
shallowReadonly
第一层数据不允许修改
toRaw
将一个 reactive 生成的对象转为普通对象 【ref 生成的不行】
与响应式相反 即:让你响应式的数据变成不是响应式
markRow
将对象标记为普通对象不是响应式
vue3生命周期
自定义 hook 函数
import { reactive,onMounted,onBeforeUnmount } from "vue"
//自定义打点 hook
export default function(){
//打点相关的数据
let point = reactive({
x:0,
y:0
})
//打点相关的方法
function savePoint(event){
point.x = event.pageX
point.y = event.pageY
}
//打点相关的生命周期钩子
onMounted(() => {
window.addEventListener('click',savePoint)
}),
onBeforeUnmount(() => {
window.removeEventListener('click',savePoint)
})
return point
}
3. reactive 函数
定义一个对象类型的响应式数据 基本类型处理不了
2. Vue3 响应式原理
使用 Proxy 代理和 Reflect 反射