1.computed方法
在组合式API,computed()实现计算属性,()的写法和选项式API一样
**注意:**ref数据的侦听,.value的添加
<body>
<div id="app">
<p>数据:{{count}}</p>
<button @click="add">加1</button>
<p>计算属性count双倍:{{doubleCount}}</p>
<hr>
<p>计算属性count减1:{{plusOne}}</p>
<button @click="update">改变计算属性的值</button>
</div>
<script src="./lib/vue.global.js"></script>
<script>
// 解构出createApp方法 ref方法 computed方法
const {createApp,ref,computed} = Vue;
const app =createApp({
setup(){
//基本计算属性--count是响应式
const count = ref(0);
//定义方法
const add =()=>{
//让count自增--ref的数据,通过.value访问到
count.value++;
}
//计算属性实现---count2倍的值--计算属性名doubleCount
//computed内是一个函数
const doubleCount = computed(()=>count.value*2);
//computed中添加getter和setter
const plusOne = computed({
//get方法--获取值
get(){return count.value-1},
//set设置值
set(val){
console.log("set运行");
count.value=val;
}
})
//定义方法update
const update =()=>{
//将plusOne的值设置为100
plusOne.value = 100;
}
return{
count,add,doubleCount,plusOne,update
}
},
// 选项式写法
// computed:{
// doubleCount(){
// return this.count*2
// }
// }
})
app.mount("#app");
</script>
</body>
2.组合式API中的侦听器watch
watch(‘监听谁’,监听的回调,监听的配置)
- 第一个参数:'监听谁’就是监听数据的变量
- 第二个参数:监听的回调,和选项式API写法一样
- 第三个参数:监听的配置,deep:true开启深度监听,immediate:true表示立刻执行(数据变化前就执行)
- 注意点1:监听reactive数据时,默认开启深度监听,deep:false不起作用
- 注意点2:监听reactive数据时,Vue没有保留副本,newValue和oldValue是一样的,若想获取到oldValue,需要将reactive数据封装为一个计算属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>watch()</title>
</head>
<body>
<div id="app">
<h1>sum值为:{{sum}}</h1>
<button @click="sum++">改变sum的值加1</button>
<hr>
<h1>msg值为:{{msg}}</h1>
<button @click="msg+='2217学员'">改变msg的值</button>
<hr>
<h2>person姓名:{{person.name}}</h2>
<h2>person年龄:{{person.age}}</h2>
<h2>person薪资:{{person.job.j1.salary}}</h2>
<button @click="person.name+='好'">改变person.name的值</button>
<button @click="person.age++">改变age的值</button>
<button @click="person.job.j1.salary+=1000">改变薪资</button>
</div>
<script src="./lib/vue.global.js"></script>
<script>
// 解构watch
const {createApp,ref,reactive,watch} = Vue;
const app =createApp({
setup(){
//定义数据--Number
let sum = ref(0);
//定义数据--String
let msg = ref('hello');
//定义对象 --reactive
let person =reactive({
name:'张三',
age:'22',
// 多层对象--有嵌套
job:{
j1:{
salary:15000
}
}
});
//参数:监听谁,监听的回调函数,监听的配置(,{deep:true,immediate: true})
// watch(sum,(newVal,oldVal)=>{
// console.log("sum改变了",newVal,oldVal);
// })
//监听多个ref数据,写在数组内--共用回调函数,只要1个数据变,就会执行侦听回调
// watch([sum,msg],(newVal,oldVal)=>{
// console.log("sum或msg改变了",newVal,oldVal);
// })
//监听reactive对象--默认深度监听--deep:false不起作用
// watch(person,(newVal,oldVal)=>{
// console.log("person改变了",newVal,oldVal);
// },{deep:false})
//监听reactive对象的某个属性 ()=>person.name表示一个回调函数,返回person.name
// watch(()=>person.name,(newVal,oldVal)=>{
// console.log("person.name改变了",newVal,oldVal);
// })
//监听reactive对象的某个属性--是一个对象--手动开启深度监听
// watch(()=>person.job,(newVal,oldVal)=>{
// console.log("person.job改变了",newVal,oldVal);
// },{deep:true})
//监听reactive对象的多个属性 --数组的写法,共用回调
watch([()=>person.name,()=>person.age],(newVal,oldVal)=>{
console.log("person.name或age改变了",newVal,oldVal);
})
return{
sum,msg,person
}
}
})
app.mount("#app");
</script>
</body>
</html>
3.watchEffect()
Effect是副作用的意思,只要回调函数中有某个数据,就会监听这个数据,立刻执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>watchEffect</title>
</head>
<body>
<div id="app">
<p>count数据:{{count}}</p>
<button @click="increment">count变化</button>
<button @click="stopWatch">停止监听</button>
<hr>
<button @click="id=2">第2条数据</button>
<button @click="id=3">第3条数据</button>
<button @click="id=4">第4条数据</button>
</div>
<script src="./lib/vue.global.js"></script>
<script>
//解构出来watchEffect
const {createApp,ref,watchEffect} = Vue;
const app =createApp({
setup(){
//定义基本数据类型的数据
const count = ref(0);
const id = ref(1);
//定义方法
const increment = ()=>{
count.value++
}
//使用watchEffect--()内是回调函数,不需要指定监听对象
//只要回调函数中有这个数据,就会被监听
//将监听对象赋值给stop,stop用来停止监听
const stop = watchEffect(()=>{
console.log("监听到了count数据的变化"+count.value);
})
//定义函数,停止监听
const stopWatch = ()=>{stop()}
//监听回调是异步函数
//onInvalidate是形参,接收一个函数---清除其他异步开销
watchEffect((onInvalidate)=>{
console.log(id.value);
//一次性定时器
const timer = setTimeout(()=>{
console.log(`请求第${id.value}条数据`);
},1000)
//调用onInvalidate函数--清除定时器
onInvalidate(()=>{
//清除定时器
clearTimeout(timer)
})
})
return{
count,increment,stopWatch,id
}
}
})
app.mount("#app");
</script>
</body>
</html>
4.toRef()
将一个对象的属性,转换为Ref响应式数据,让属性的值和转换后的值保存一致
//toRef(对象,'属性名');
const count = toRef(state,'foo');
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>toRef</title>
</head>
<body>
<div id="app">
<!-- 使用组件在这里写 -->
</div>
<script src="./lib/vue.global.js"></script>
<script>
//解构出toRef
const {createApp,toRef,reactive} = Vue;
const app =createApp({
setup(){
//基于响应式对象上的一个属性,创建一个对应的ref
const state =reactive({
foo:1,
val:2
});
//toRef--state对象中的foo属性,转换为ref
//目的:count的值与state的foo的属性值保持一致
const count = toRef(state,'foo');
console.log(count.value);//1
//自增
//count.value++;//2
//console.log(state.foo);//2
state.foo++;//2
console.log(count.value);//2
}
})
app.mount("#app");
</script>
</body>
</html>
5.toRefs()
批量将一个对象中的所有属性,转换为ref数据,使用时"对象名.属性名.value"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>toRefs</title>
</head>
<body>
<div id="app">
<!-- 使用组件在这里写 -->
</div>
<script src="./lib/vue.global.js"></script>
<script>
//解构出toRefs
const {createApp,toRefs,reactive} = Vue;
const app =createApp({
setup(){
//将一个响应式对象转换为一个普通对象
const state =reactive({
foo:1,
val:2
});
//toRefs(对象名)将响应式对象转换ref对象
//批量将每个属性转换为了ref数据
const newState = toRefs(state);
//注意加.value
newState.foo.value++;
console.log(newState.foo.value);//2
//输出reactive数据--state对象的foo属性值--也变为2
console.log(state.foo);
}
})
app.mount("#app");
</script>
</body>
</html>
6.访问props
setup()中的第一个参数就是props,prop传值与接收和选项式APi一样
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>访问props</title>
</head>
<body>
<div id="app">
<button @click="count++">改变count的值</button>
<p>{{count}}</p>
<!-- prop传值 -->
<my-com :count="count"></my-com>
</div>
<!-- 组件的视图层的定义 -->
<template id="com">
<div>
<h1>子组件</h1>
<p>
父组件给的数据:{{count}}--计算属性{{doubleCount }}
<button @click="count++">子组件count</button>
</p>
</div>
</template>
<script src="./lib/vue.global.js"></script>
<script>
const {createApp,ref,computed,toRefs,toRef} = Vue;
// 组件的数据层的定义
const Com = {
template:'#com',
//接收属性
props:{
//规定count属性必须为Number
count:Number
},
setup(props){
//props被获取到
// console.log(props);//--{count:0}
//计算属性
//const doubleCount = computed(()=>props.count*2)
//count一旦从props解构出来,丢失响应式
//Attempting to mutate prop "count". Props are readonly.--只读
//const {count} = props;
//count一旦从props解构出来,保持响应式
//转换一个属性为ref数据,不需要解构
const count = toRef(props,'count');
//转换所有属性为ref数据,需要解构
//const {count} = toRefs(props);
const doubleCount = computed(()=>count.value*2)
return{
doubleCount
}
}
};
const app =createApp({
//组件的注册
components:{
MyCom:Com
},
setup(){
//count为0
const count=ref(0);
return{count}
}
})
app.mount("#app");
</script>
</body>
</html>
7.setup的上下文context
context是setup的第二个参数,表示上下文
- context.attrs类似选项APi中的this.$attrs
- context.slots类似this.$slots
- context.emit(‘my-event’,666)相当于 this.$emit(‘事件’,参数)—实现子给父传值
- context.expose可以将自己的内容暴露给父组件,父组件通过子组件的ref属性获取到暴露的内容
//相当于this.$refs.child.name,child是ref数据
console.log('子组件暴露的值',child.value.name);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>setup中的上下文context</title>
</head>
<body>
<div id="app">
<!-- 定义的属性做测试用--看下子组件的attrs是否获取到 -->
<!-- @my-event="getData" 表示给子组件一个自定义事件my-event-->
<my-com ref="child" class="active" style="color:red" id="box" @click="test"
@my-event="getData">
<template #default>第一个插槽内容</template>
<template #footer>第二个插槽内容</template>
</my-com>
</div>
<!-- 组件的视图层的定义 -->
<template id="com">
<!-- 这个div在vue2中必须要加,vue3中可以不加 -->
<div>
<h1>子组件</h1>
<button @click="sentData">传递给父组件的值</button>
<!-- 匿名插槽 -->
<slot></slot>
<!-- 具名插槽 -->
<slot name="footer"></slot>
</div>
</template>
<script src="./lib/vue.global.js"></script>
<script>
// onMounted是生命周期中的一个钩子
const {createApp,ref,onMounted} = Vue;
// 组件的数据层的定义
const Com = {
template:'#com',
//context是上下文的意思
setup(props,context){
//将context作为选项式APi中的this使用即可
//组件内置属性--类似$attr $slots
console.log('获取到了属性对象:'+context.attrs);
console.log('获取到了插槽对象:'+context.slots);
//派发事件--实现子给父传值
const sentData=()=>{
// this.$emit('自定义事件名',参数),派发事件
context.emit('my-event',666)
}
//定义数据,暴露给父组件
const msg = ref('Vue3');
const name = ref('zhangsan');
const changName =()=>{
name.value="李四";
}
//父组件可以通过ref获取到暴露的方法和数据
context.expose({
msg,name,changName
})
return{
sentData
}
}
};
const app =createApp({
components:{
MyCom:Com
},
setup(){
const test =()=>{}
//定义函数:getData
const getData = (val)=>{
console.log("子组件传来的值"+val);
}
//定义ref数据--给子组件作为ref属性的值
const child = ref()
//使用生命周期钩子--只需在名字前加"on",onMounted表示挂载后
onMounted(()=>{
console.log(child);
//this.$refs.child.name
console.log('获取到了子组件暴露的值',child.value.name);
})
return{
child,test,getData
}
}
})
app.mount("#app");
</script>
</body>
</html>
1.Promise是什么(what)?作用(why)?如何使用?(how)?场景(when)
Promise 是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
promise本质 不是控制 异步代码的执行顺序(无法控制) , 而是控制异步代码结果处理的顺序
如何使用:
Promise的实例有三个状态:
- Pending(进行中)
- Resolved(已完成)
- Rejected(已拒绝)
Promise的实例有两个过程: - pending -> fulfilled : Resolved(已完成)
- pending -> rejected:Rejected(已拒绝)
注意:一旦从进行状态变成为其他状态就永远不能更改状态了。
使用场景: - 解决回调地狱,将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。流程更加清晰,代码更加优雅。
- Promise对象提供统一的接口,使得控制异步操作更加容易。
缺点: - 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
2.复习事件循环。Promise在事件循环中什么时候运行的?
处理异步任务时运行
3.ref与reactive的区别
ref和reactive都是用来定义响应式数据.
reactive
- reactive适合定义复杂的数据类型,参数必须是对象或数组,
- 如果对象的某个元素要实现响应式,需要"toRefs"
ref - 函数参数可以是基本数据类型,也可以是对象,
- 如果参数是对象类型时,其实底层的本质还是reactive,系统会自动根据我们给ref传入的值转换成:```ref(1)->reactive({value:1})``
- ref函数只能操作浅层次的数据,把基本数据类型当作自己的属性值;深层次依赖于reactive
- 在template中访问,系统会自动添加.value;在js中需要手动.value
- ref响应式原理是依赖于Object.defineProperty()的get()和set()的。
4.watch与watchEffect的区别
watch:
- watch显式指定依赖数据,依赖数据更新时执行回调函数
具有一定的惰性lazy - 第一次页面展示的时候不会执行,只有数据变化的时候才会执行(设置- immediate: true时可以变为非惰性,页面首次加载就会执行)
- 监视ref定义的响应式数据时可以获取到原值
既要指明监视的属性,也要指明监视的回调
watchEffect:
-
watchEffect自动收集依赖数据,依赖数据更新时重新执行自身
-
立即执行,没有惰性,页面的首次加载就会执行
-
无法获取到原值,只能得到变化后的值
-
不用指明监视哪个属性,监视的回调中用到哪个属性就监视哪个属性
5.复习父子组件通信
父传子:
- 1通过props选项接收父组件传来的属性名
- 2在子组件更改props的值会报错,要想对父组件的值修改只能在子组件再创建一个局部数据属性
- 3子组件可以设置一些规则限制父组件从传来的值
子传父:
- 1 通过emits选项接收父组件传来的事件名
- 2 通过 this.$emit(“父组件定义的事件名”,参数)
参数可以有多个 在子组件中设置方法返回给父组件实现给父组件传值
6.toRef()与toRefs()的作用及区别
toRef():
将一个对象的属性,转换为Ref响应式数据,让属性的值和转换后的值保存一致
toRefs():
批量将一个对象中的所有属性,转换为ref数据,使用时"对象名.属性名.value"
7.如何在组合式Api中使用this呢?
用 “context.属性” 来平替this
- context.attrs 类似于选项式api中的 this.attrs
- context.slots 类似于选项式api中的 this.$slots
- context.emit(‘my-event’,666)相当于 this.$emit(‘事件’,参数)—实现子给父传值
8.组合式api上下文context.expose()的作用
- context.expose可以将自己的内容暴露给父组件,父组件通过子组件的ref属性获取到暴露的内容
//相当于this.$refs.child.name,child是ref数据
console.log('子组件暴露的值',child.value.name);