组合式API-计算属性computed,侦听watch,watchEffect,转换为Ref响应式数据toRef,toRefs, setup()中的第一个参数就是props,setup中的上下文

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);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值