Day02——Vue3学习笔记:响应式数据(ref和reactive)、计算属性(computed)和侦听器(watch)

一、响应式数据(ref和reactive)

数据修改后可以在页面中反映出来为响应式数据。
此时的数据不是响应式数据。
在这里插入图片描述
点击按钮后,数据确实修改了,但是在页面上看不出来。

<template>
    <div class="outside">
        <div class="container">
        <div class="left"><img src="" alt="这是一张图片"></div>
        <div class="right">
            <h2>{{ title }}</h2>
            <p>{{ content }}</p>
        </div>
    </div>
    <div class="operation">
        <button @click="changeTitle">修改标题</button> 
        <button @click="changeContent">修改内容</button>
    </div>
    </div>
    
    
    
</template>
<script lang="ts">
    export default {
        name: 'test',
    }
</script>
<script lang="ts" setup>
    let title = "hello, vue3"
    let content = "这是一个不错的开始。"
    function changeTitle(){
        title = "hello, typescript"
        console.log(title)
    }
    function changeContent(){
        content = "学无止境"
        console.log(content)
    }
</script>

1 使用ref()

import { ref } from 'vue'

可以应用到基本数据类型对象类型

1. 基本数据类型

<script lang="ts" setup>
    import { ref } from 'vue'
    let title = ref("hello, vue3")
    let content = "这是一个不错的开始。"
    function changeTitle(){
        title.value = "hello, typescript"
        console.log(title)
    }
    function changeContent(){
        content = "学无止境"
        console.log(content)
    }
</script>

这里只将 title 属性使用ref 可见页面中只有title值改变了,内容值并没有改变。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注:
使用ref,在script中修改数据时,
基本数据.value = 新值

2. 对象类型

<template>
    <div class="outside">
        <div class="container">
        <div class="left"><img src="" alt="这是一张图片"></div>
        <div class="right">
            <h2>{{ item.title }}</h2>
            <p>{{ item.content }}</p>
        </div>
    </div>
    <div class="operation">
        <button @click="changeTitle">修改标题</button> 
        <button @click="changeContent">修改内容</button>
        <button @click="changeAll">整体全部修改</button>
    </div>
    </div>
    
    
    
</template>
<script lang="ts">
    export default {
        name: 'test',
    }
</script>
<script lang="ts" setup>
    import { ref } from 'vue'
    // let title = ref("hello, vue3")
    // let content = "这是一个不错的开始。"
    let item = ref({
        title: "hello, vue3",
        content: "这是一个不错的开始。"
    })
    function changeTitle(){
        item.value.title= "hello, typescript"
        console.log(item.value.title)
    }
    function changeContent(){
        item.value.content = "学无止境"
        console.log(item.value.content)
    }
    function changeAll(){
        item.value = {
            title: 'hello, python',
            content: "人生苦短, 我学python"
        }
    }
</script>

在这里插入图片描述
注意:
使用ref,在script中修改数据时,
若修改对象的某个属性,用 对象名.value.属性名 = 新值 ,
若修改整个对象,用对象名.value = 新对象此时 对象名.value的地址发生了变化。

2 使用 reactive()

 import { reactive } from 'vue'

只能用于 对象类型

<template>
    <div class="outside">
        <div class="container">
        <div class="left"><img src="" alt="这是一张图片"></div>
        <div class="right">
            <h2>{{ item.title }}</h2>
            <p>{{ item.content }}</p>
        </div>
    </div>
    <div class="operation">
        <button @click="changeTitle">修改标题</button> 
        <button @click="changeContent">修改内容</button>
        <button @click="changeAll">整体全部修改</button>
    </div>
    </div>
    
    
    
</template>
<script lang="ts">
    export default {
        name: 'test',
    }
</script>
<script lang="ts" setup>
    import { reactive } from 'vue'
    // let title = ref("hello, vue3")
    // let content = "这是一个不错的开始。"
    let item = reactive({
        title: "hello, vue3",
        content: "这是一个不错的开始。"
    })
    function changeTitle(){
        item.title= "hello, typescript"
        console.log(item.title)
    }
    function changeContent(){
        item.content = "学无止境"
        console.log(item.content)
    }
    function changeAll(){
        // Object.assign(item, {
        //     title: "python",
        //     content: "人生苦短, 我学python"
        // })
        // item={
        //     title: "python",
        //     content: "人生苦短, 我学python"
        // }
        item.title = "python"
        item.content = "人生苦短,我学python"
    }
</script>

注意:
在修改对象整体时,
如果对象的的属性值过多,一条一条的写就会特别麻烦,
如果给对象赋予一个新对象,则会失去响应式,
可以使用Object.assign(要求改对象的名字,新对象)解决。

        Object.assign(item, {
            title: "python",
            content: "人生苦短, 我学python"
        })

这里如果使用ES6中的解构赋值则会这样写

<template>
    <div class="outside">
        <div class="container">
        <div class="left"><img src="" alt="这是一张图片"></div>
        <div class="right">
            <h2>{{ item.title }}</h2>
            <p>{{ item.content }}</p>
        </div>
    </div>
    <div class="operation">
        <button @click="changeTitle">修改标题</button> 
        <button @click="changeContent">修改内容</button>
        <button @click="changeAll">整体全部修改</button>
    </div>
    </div>
    
    
    
</template>
<script lang="ts">
    export default {
        name: 'test',
    }
</script>
<script lang="ts" setup>
    import { reactive } from 'vue'
    // let title = ref("hello, vue3")
    // let content = "这是一个不错的开始。"
    let item = reactive({
        title: "hello, vue3",
        content: "这是一个不错的开始。"
    })
    let {title, content} = item
    function changeTitle(){
        title= "hello, typescript"
        console.log(title)
    }
    function changeContent(){
        content = "学无止境"
        console.log(content)
    }
    function changeAll(){
        Object.assign(item, {
            title: "python",
            content: "人生苦短, 我学python"
        })
        // item={
        //     title: "python",
        //     content: "人生苦短, 我学python"
        // }
        // item.title = "python"
        // item.content = "人生苦短,我学python"
    }
</script>

在这里插入图片描述
弊端:
这样会使对象的单个属性失去响应式。

toRefs 和 toRef

 import { toRef, toRefs } from 'vue'

此时可以使用 toRefs

<template>
    <div class="outside">
        <div class="container">
        <div class="left"><img src="" alt="这是一张图片"></div>
        <div class="right">
            <h2>{{ item.title }}</h2>
            <p>{{ item.content }}</p>
        </div>
    </div>
    <div class="operation">
        <button @click="changeTitle">修改标题</button> 
        <button @click="changeContent">修改内容</button>
        <button @click="changeAll">整体全部修改</button>
    </div>
    </div>
    
    
    
</template>
<script lang="ts">
    export default {
        name: 'test',
    }
</script>
<script lang="ts" setup>
    import { reactive, toRefs } from 'vue'
    // let title = ref("hello, vue3")
    // let content = "这是一个不错的开始。"
    let item = reactive({
        title: "hello, vue3",
        content: "这是一个不错的开始。"
    })
    let {title, content} = toRefs(item) 
    function changeTitle(){
        title.value= "hello, typescript"
        console.log(title)
    }
    function changeContent(){
        content.value = "学无止境"
        console.log(content)
    }
    function changeAll(){
        Object.assign(item, {
            title: "python",
            content: "人生苦短, 我学python"
        })
        // item={
        //     title: "python",
        //     content: "人生苦短, 我学python"
        // }
        // item.title = "python"
        // item.content = "人生苦短,我学python"
    }
</script>

如果是给对象的某个属性增加响应式可以用 toRef

import { toRef } from 'vue'

let title= toRef(item, 'title')
====================================
title.value = 新值

二、 计算属性(computed)

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。推荐使用计算属性来描述依赖响应式状态的复杂逻辑。
比方说 标题超过12个字符时,超过的部分用省略号...表示。

<template>
    <div class="outside">
        <div class="container">
        <div class="left"><img src="" alt="这是一张图片"></div>
        <div class="right">
            <h2>{{ titlePre }}</h2>
            <p>{{ item.content }}</p>
        </div>
    </div>
    <div class="operation">
        <button @click="changeTitle">修改标题</button> 
        <button @click="changeContent">修改内容</button>
        <button @click="changeAll">整体全部修改</button>
    </div>
    </div>
    
    
    
</template>
<script lang="ts">
    export default {
        name: 'test',
    }
</script>
<script lang="ts" setup>
    import { reactive, toRefs,computed } from 'vue'
    // let title = ref("hello, vue3")
    // let content = "这是一个不错的开始。"
    let item = reactive({
        title: "hello, vue3",
        content: "这是一个不错的开始。"
    })
    let {title, content} = toRefs(item) 
    function changeTitle(){
        title.value= "hello, typescript"
        console.log(title)
    }
    function changeContent(){
        content.value = "学无止境"
        console.log(content)
    }
    function changeAll(){
        Object.assign(item, {
            title: "python",
            content: "人生苦短, 我学python"
        })
        // item={
        //     title: "python",
        //     content: "人生苦短, 我学python"
        // }
        // item.title = "python"
        // item.content = "人生苦短,我学python"
    }
    const titlePre = computed(()=>{
       
        return title.value.length > 12?title.value.slice(0,12)+'...':title.value
    })
</script>

在这里插入图片描述
注意:

  1. 计算属性会缓存,方法不会。
  2. 计算属性默认是只读的。如果让其可写,需要设置其 get()方法和set()方法。
<template>
    <div class="outside">
        <div class="container">
        <div class="left"><img src="" alt="这是一张图片"></div>
        <div class="right">
            <h2>{{ titlePre }}</h2>
            <p>{{ item.content }}</p>
        </div>
    </div>
    <div class="operation">
        <button @click="changeTitle">修改标题</button> 
        <button @click="changeContent">修改内容</button>
        <button @click="changeAll">整体全部修改</button>
        <button @click="changeTitlePre">修改标题计算属性</button>
    </div>
    </div>
    
    
    
</template>
<script lang="ts">
    export default {
        name: 'test',
    }
</script>
<script lang="ts" setup>
    import { reactive, toRefs,computed } from 'vue'
    // let title = ref("hello, vue3")
    // let content = "这是一个不错的开始。"
    let item = reactive({
        title: "hello, vue3",
        content: "这是一个不错的开始。"
    })
    let {title, content} = toRefs(item) 
    function changeTitle(){
        title.value= "hello, typescript"
        console.log(title)
    }
    function changeContent(){
        content.value = "学无止境"
        console.log(content)
    }
    function changeAll(){
        Object.assign(item, {
            title: "python",
            content: "人生苦短, 我学python"
        })
        // item={
        //     title: "python",
        //     content: "人生苦短, 我学python"
        // }
        // item.title = "python"
        // item.content = "人生苦短,我学python"
    }
    const titlePre = computed(
        {
            get(){
                return title.value.length > 12?title.value.slice(0,12)+'...':title.value
            },
            set(newValue){
                title.value = newValue
            }

        }   
    )
    function changeTitlePre(){
        titlePre.value = "我就是超过了12个字符我看看会发生什么啊"
    }
</script>

在这里插入图片描述
在这里插入图片描述

三、侦听器(watch)

watch的第一个参数是:被监视的数据
watch的第二个参数是:监视的回调函数
watch的第三个参数是:配置对象(deep、immediate等等.....)

情况1:监视的是ref定义的基本数据类型,或者是一个计算属性

直接写变量名即可,

监视的是其 value的改变。

<template>
    <div class="outside">
        <div class="container">
            <div class="left"><img src="" alt="这是一张图片"></div>
            <div class="right">
                <h2>{{ titlePre }}</h2>
                <p>{{ item.content }}</p>
            </div>
            <div class="label">{{ label }}</div>
        </div>
        <div class="operation">
            <button @click="changeTitle">修改标题</button> 
            <button @click="changeContent">修改内容</button>
            <button @click="changeAll">整体全部修改</button>
            <button @click="changeTitlePre">修改标题计算属性</button>
            <button @click="changeLabel">修改分类</button>
        </div>
        
    </div>
    
    
    
</template>
<script lang="ts">
    export default {
        name: 'test',
    }
</script>
<script lang="ts" setup>
    import {ref, reactive, toRefs,computed,watch } from 'vue'
    // let title = ref("hello, vue3")
    // let content = "这是一个不错的开始。"
    let labels = ['全栈开发','前端开发','后端开发','人工智能','其他']
    let item = reactive({
        title: "hello, vue3",
        content: "这是一个不错的开始。"
    })
    let label = ref('全栈开发')
    let {title, content} = toRefs(item) 
    function changeTitle(){
        title.value= "hello, typescript"
        console.log(title)
    }
    function changeContent(){
        content.value = "学无止境"
        console.log(content)
    }
    function changeAll(){
        Object.assign(item, {
            title: "python",
            content: "人生苦短, 我学python"
        })
        // item={
        //     title: "python",
        //     content: "人生苦短, 我学python"
        // }
        // item.title = "python"
        // item.content = "人生苦短,我学python"
    }
    const titlePre = computed(
        {
            get(){
                return title.value.length > 12?title.value.slice(0,12)+'...':title.value
            },
            set(newValue){
                title.value = newValue
            }

        }   
    )
    function changeTitlePre(){
        titlePre.value = "我就是超过了12个字符我看看会发生什么啊"
    }
    function changeLabel(){
        
        label.value = labels[Math.floor(Math.random()*labels.length)]
    }
    // 情况1:监听基本数据类型ref
    const stopWatch = watch(label,(newValue, oldValue)=>{
        console.log('label变化了,',newValue, oldValue)
        if( label.value ==='其他'){
            stopWatch()
        }
    })
</script>
<style scoped>
.outside button {
    margin: 10px;
    margin-left: 0;
}
    .container {
        position: relative;
        display: flex;
        width: 500px;
        height: 200px;
        
    }
    .container .left {
        flex:1;
        background-color: pink;
    }
    .container .right {
        flex: 3;
        background-color: skyblue;
        margin-left: 10px;
        padding: 10px;
    }
    .right h2 {
        text-align: center;
    }
    .label {
        color: green;
        position: absolute;
        top: 5px;
        right: 5px;
        font-size: 12px;
    }
</style>
 // 监听计算属性
    watch(titlePre, (newValue,oldValue )=>{
        console.log('titlePre变化了,',newValue, oldValue)
    })

在这里插入图片描述

情况2: 监视的是ref定义的对象类型的数据

直接写对象名,

监视的是对象的地址值,若想监视对象的属性,则需要手动开启深度监视。
写法同上,此处不再赘述。

 watch(item,(newValue, oldValue)=>{
        console.log('item变化了,',newValue, oldValue)
 },{deep: true})

注意:

  • 若修改的是ref定义的对象中的属性,newValue 和 oldValue 都是新值,因为它们是同一个对象。
  • 若修改整个ref定义的对象,newValue 是新值, oldValue 是旧值,因为不是同一个对象了。

情况3: 监视的是reactive定义的对象类型的数据

直接写对象名,

默认开启了深度监视。 若修改整个reactive定义的对象,newValue 和 oldValue 是同一个值,因为它们是同一个对象了。

 watch(item,(newValue, oldValue)=>{
        console.log('item变化了,',newValue, oldValue)
 })

情况4: 监视的是对象的某个属性

建议都直接写成函数式,不管这个属性是基本类型还是对象类型。
如果属性是对象类型,可以直接写对象名。不过建议都写成函数式

如果该对象的属性也是对象类型,如果需要监视其内部属性,则需要手动开启深度监视。

// 监听对象的某个属性
    watch(()=>item.title,(newValue,oldValue )=>{
        console.log('item.title变化了,',newValue, oldValue)
    },{deep:true})

可以这样写,但不建议。如下。

watch(item.author,(newValue, oldValue)=>{
    console.log('item.author变化了,',newValue, oldValue)
})

情况五: 监视的是上述多个数据

将上述多种数据写成一个数组的形式。

    // 情况5:多种数据
    watch([()=>item.author,()=>item.content,label],(newValue, oldValue)=>{
        console.log('多种数据变化了,',newValue, oldValue)
    })

总结:响应式数据侦听

  1. 直接监视基本数据类型和计算属性直接写 变量名
  2. 直接监视对象 直接写 对象名
  3. 监视对象的属性 写成 函数式
    test.vue文件如下。
<template>
    <div class="outside">
        <div class="container">
            <div class="left"><img src="" alt="这是一张图片"></div>
            <div class="right">
                <h2>{{ titlePre }}</h2>
                <p>{{ item.content }}</p>
                <p class="author">作者:{{ item.author.name }}</p>
            </div>
            <div class="label">{{ label }}</div>
        </div>
        <div class="operation">
            <button @click="changeTitle">修改标题</button> 
            <button @click="changeContent">修改内容</button>
            <button @click="changeAll">整体全部修改</button>
            <button @click="changeTitlePre">修改标题计算属性</button>
            <button @click="changeLabel">修改分类</button>
            <button @click="changeAuthor">修改作者</button>
        </div>
        
    </div>
    
    
    
</template>
<script lang="ts">
    export default {
        name: 'test',
    }
</script>
<script lang="ts" setup>
    import {ref, reactive, toRefs,computed,watch } from 'vue'
    // let title = ref("hello, vue3")
    // let content = "这是一个不错的开始。"
    let labels = ['全栈开发','前端开发','后端开发','人工智能','其他']
    let authors = ['wang', '李白','苏轼','cwh']
    let item = reactive({
        title: "hello, vue3",
        content: "这是一个不错的开始。",
        author: {
            name: 'cwh',
            age: 18
        }
    })
    let label = ref('全栈开发')
    let {title, content,author} = toRefs(item) 
    function changeTitle(){
        title.value= "hello, typescript"
        console.log(title)
    }
    function changeContent(){
        content.value = "学无止境"
        console.log(content)
    }
    function changeAll(){
        Object.assign(item, {
            title: "python",
            content: "人生苦短, 我学python",
            author: {
                name: 'wang',
                age: 25
            }
        })
        // item={
        //     title: "python",
        //     content: "人生苦短, 我学python"
        // }
        // item.title = "python"
        // item.content = "人生苦短,我学python"
    }
    const titlePre = computed(
        {
            get(){
                return title.value.length > 12?title.value.slice(0,12)+'...':title.value
            },
            set(newValue){
                title.value = newValue
            }

        }   
    )
    function changeTitlePre(){
        titlePre.value = "我就是超过了12个字符我看看会发生什么啊"
    }
    function changeLabel(){
        
        label.value = labels[Math.floor(Math.random()*labels.length)]
    }
    function changeAuthor(){
        author.value.name = authors[Math.floor(Math.random()*labels.length)]
    }
    // 情况1:监听基本数据类型ref
    const stopWatch = watch(label,(newValue, oldValue)=>{
        console.log('label变化了,',newValue, oldValue)
        if( label.value ==='其他'){
            stopWatch()
        }
    })
    // 情况4: 监听reactive定义的对象数据类型
    watch(item,(newValue, oldValue)=>{
        console.log('item变化了,',newValue, oldValue)
    })
    // 情况4-2: 监听reactive定义的对象数据类型
    watch(item.author,(newValue, oldValue)=>{
        console.log('item.author变化了,',newValue, oldValue)
    })
    // 情况5:多种数据
    watch([()=>item.author,()=>item.content,label],(newValue, oldValue)=>{
        console.log('多种数据变化了,',newValue, oldValue)
    })

    // 监听计算属性
    watch(titlePre, (newValue,oldValue )=>{
        console.log('titlePre变化了,',newValue, oldValue)
    })
    // 监听对象的某个属性
    watch(()=>item.title,(newValue,oldValue )=>{
        console.log('item.title变化了,',newValue, oldValue)
    },{deep:true})
</script>
<style scoped>
.outside button {
    margin: 10px;
    margin-left: 0;
}
    .container {
        position: relative;
        display: flex;
        width: 500px;
        height: 200px;
        
    }
    .container .left {
        flex:1;
        background-color: pink;
    }
    .container .right {
        flex: 3;
        background-color: skyblue;
        margin-left: 10px;
        padding: 10px;
    }
    .right h2 {
        text-align: center;
    }
    .label {
        color: green;
        position: absolute;
        top: 5px;
        right: 5px;
        font-size: 12px;
    }
    .author {
        position: absolute;
        right: 20px;
        bottom: 0px;
    }
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值