一、响应式数据(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>
注意:
- 计算属性会缓存,方法不会。
- 计算属性默认是只读的。如果让其可写,需要设置其 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)
})
总结:响应式数据侦听
- 直接监视基本数据类型和计算属性直接写 变量名。
- 直接监视对象 直接写 对象名。
- 监视对象的属性 写成 函数式。
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>