普通视图

发现新文章,点击刷新页面。
昨天 — 2026年3月12日首页

最新版vue3+TypeScript开发入门到实战教程之ref与reactive的实战区别用法

作者 angerdream
2026年3月12日 15:54

概述

上节详细 说明ref、reactive如何定义使用响应式数据。总结如下:

  • ref定义基础类型数据
  • reactive定义对象数据
  • 但ref也可以定义对象类型的数据。但底层基于reactive实现 ref定义对象类型数据,底层用reactive实现, 但两者区别在何处,本文将详细说明

ref使用对象类型的数据定义响应式

  • 新建组件Fish
  • 创建响应式对象fish,数组fishs
  • 显示、修改响应式对象fish数据、fishs第一条数据
<template>
  <h2>鱼类:{{ fish.name }}</h2>
  <h2>价格:{{fish.price  }}</h2>
  <button @click="changeName">改变鱼</button>
  <button @click="addPrice">涨价</button>
  <h3>鱼的列表</h3>
  <ul>
    <li v-for="item in fishs" :key="item.id">
      {{ item.name }}:{{ item.price }}
    </li>
  </ul>
  <button @click="changeFirstPrice">改变第三条鱼的价格</button>

</template>
<script setup>
import { ref } from 'vue'
let fish = ref({ name: '鲫鱼', price: 10 });
let fishs = ref([
  {id:'txdi01',name:'鲫鱼',price:10},
  {id:'txdi02',name:'鲤鱼',price:20},
  {id:'txdi03',name:'草鱼',price:30},
])
function changeName() {
  fish.value.name = '草鱼';
  console.log(fish);
  console.log(fish.value);

}
function addPrice() {
  fish.value.price += 10;

}
function changeFirstPrice() {
  fishs.value[0].price += 10;
}
</script>

用ref定义的响应式对象,需要用.value去访问赋值。访问页面,发现他与reactive定义的响应式对象是一样的,除了reactive不需要用.value访问。如图 在这里插入图片描述 在点击按钮改变鱼,打印输入fish和fish.value,发现fish使用RefImpl定义,但 fish.value使用Proxy定义的对象,这与使用reactive定义是一样的。使用ref定义的响应式对象,底层使用reactive实现的。

ref对比reactive区别

从使用整体看

  • ref定义可以是基础数据、对象类型数据
  • reactive定义只能是对象类型数据

从细微之处看两者区别

  • ref创建的响应式对象,必须使用.value
  • reactive是深层次响应对象
  • reactive重新分配一个新对象,会丢失响应式

ref与reactive实际项目使用原则

  • 基本类型数据,必须使用ref
  • 简单的对象,不需要层级太深,ref、reactive都可以
  • 层级较深的对象,推荐使用reactive 以上使用原则,简单归纳,基本数据使用ref、对象使用reactive。但实际要清楚两者在深层次对象使用区别。

如何正确理解reactive重新分配一个新对象会流失响应式

reactive重新分配一个新对象会流失响应式是官方的说明。猛一看有些懵。我们从事例代码中去理解这句话的含义。事例中,定义let fish = reactive({ name: '鲫鱼', price: 10 }),创建5个按钮

  • 改变鱼的种类,fish.name = '草鱼';
  • 改变鱼的价格,fish.price += 10;
  • 方式一改变整个鱼,fish={ name: '鲤鱼', price: 30 }
  • 方式二改变整个鱼,fish = reactive({ name: '鲤鱼鱼', price: 40 });
  • 方式三改变整个鱼,Object.assign(fish,{ name: '带鱼', price: 50 }) 运行查看效果,发现,点击按钮方式一改变整个鱼、方式二改变整个鱼,页面无任何变。方式一、方式二都是重新给fis分配一个新的对象,导致fish丢失响应式。方式三是将新对象数据重新分配给fish,所以可以。 当fish丢失响应式数据后,再给fish重新赋值,页面也无法改变,也不在具有响应式。 细看图中的操作: 在这里插入图片描述

详细代码

<template>
  <h2>鱼类:{{ fish.name }}</h2>
  <h2>价格:{{fish.price  }}</h2>
  <button @click="changeName">改变鱼的种类</button>
  <button @click="addPrice">改变鱼的价格</button>
  <div>
    <button @click="changeallfish1">方式一改变整个鱼</button>
    <button @click="changeallfish2">方式二改变整个鱼</button>
    <button @click="changeallfish3">方式三改变整个鱼</button>
  </div>

</template>
<script setup>
import { reactive } from 'vue'
let fish = reactive({ name: '鲫鱼', price: 10 });
function changeName() {
  fish.name = '草鱼';

}
function addPrice() {
  fish.price += 10;

}
function changeallfish1() {
  fish={ name: '鲤鱼', price: 30 }

}
function changeallfish2() {
 fish = reactive({ name: '鲤鱼鱼', price: 40 });


}
function changeallfish3() {
  console.log('sadsd')
  Object.assign(fish, { name: '带鱼', price: 50 })
  console.log(fish);

}
</script>

ref定义的响应式对象重新分配一个新对象会怎样

  • 使用.value赋值,是响应式
  • 直接赋值,流失响应式 看事例
<template>
  <h2>鱼类:{{ fish.name }}</h2>
  <h2>价格:{{fish.price  }}</h2>
  <button @click="changeallfish1">方式一改变整个鱼</button>
  <button @click="changeallfish2">方式二改变整个鱼</button>
</template>
<script setup>
import { ref } from 'vue'
let fish = ref({ name: '鲫鱼', price: 10 });
function changeallfish1() {
  fish.value={ name: '鲤鱼', price: 30 }

}
function changeallfish2() {
 fish = ref({ name: '鲤鱼鱼', price: 40 });
}
</script>

在这里插入图片描述

最新版vue3+TypeScript开发入门到实战教程之学会vue3真正的响应式数据

作者 angerdream
2026年3月12日 15:52

响应式数据概述

在vue2那个年代,响应式数据是在data里面定义,只要把数据放在data里,然后在模版内引用,数据一变,模版就跟着显示,如下图代码:

<template>
  <h2>我是一条{{ fish }}</h2>
  <button @click="changeFish">改变鱼</button>
</template>
<script lang="ts">
export default {
  name:'Fish',
  data() {
    return {
      fish:'鲫鱼'
    }
  },
  methods: {
    changeFish() {
      this.fish+='!'
    }
  }
}
</script>

在这里插入图片描述

点击按钮改变鱼。fish一变化,模版就跟着变化,这就是响应式数据。

vue3是如何定义响应式数据的

在vue3中,使用的是组合式 API (Composition API)语法,它没有data,数据和方式都定义在script标签里。在定义数据时,有两种方式给数据标记成响应式数据,分别是ref、reactive。

  • ref给基本数据标记成响应式数据,如整数、浮点数、字符串
  • reactive给对象标记成响应式数据,如对象、数组
  • ref也可给对象标记响应式数据,但底层用reactive实现

用ref给基本数据标记成响应式对象

  • 从vue引入ref,才可以使用ref函数
  • 定义响应式变量,用ref赋值
  • 获取、改变响应式数据变量的值,不能直接访问,需要加.value
  • 模版中可直接引用变量
<template>
  <h1>鱼类:{{ fish }}</h1>
  <h2>价格:{{price  }}</h2>
  <button @click="changeName">改变鱼</button>
  <button @click="addPrice">涨价</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
let fish = ref('鲫鱼');
let price =ref(10);
function changeName() {
  fish.value = '草鱼';
  console.log(fish);
  console.log(fish.value);

}
function addPrice() {
  price.value += 10;
  console.log(price)
  console.log(price.value)

}
</script>

在浏览中访问http://localhost:5173/,查看效果 在这里插入图片描述

当点击按钮,鱼与价格都跟着改变。fish与price变成响应式数据,控制台打印 console.log(price)、 console.log(price.value),发现定义fish与price,并不是字符串与数字,而是用RefImpl 类型的定义的数据,fish基本结构如下: 在这里插入图片描述 能够访问的属性,只有vulue。在changeFIsh与addPrice函数中,通过fish.value和price.value赋值

借助vue DevTools 工具查看Fish组件的数据与方法

在这里插入图片描述 想要谁变成响应式数据,在数据外面包一层ref。不需要的,直接定义变量。

reactive定义对象类型的响应式数据

  • 从vue引入reactive
  • 定义响应式对象,用reactive包裹对象
  • 访问、改变响应式对象,直接操作成员变量
  • 模版直接使用响应式对象 分别定义两个响应式数据,一是用对象定义的响应式,一是用数组定义的响应式,来说明响应式对象如何使用。
let fish = reactive({ name: '鲫鱼', price: 10 });
let fishs = reactive([
  {id:'txdi01',name:'鲫鱼',price:10},
  {id:'txdi02',name:'鲤鱼',price:20},
  {id:'txdi03',name:'草鱼',price:30},
])

改变鱼的名称与价格

function changeName() {
  fish.name = '草鱼';
}
function addPrice() {
  fish.price += 10;

}

改变fishs第二个鱼的价格

function changeThreePrice() {
  fishs[1].price += 10;
}

具体代码如下:

template>
  <h2>鱼类:{{ fish.name }}</h2>
  <h2>价格:{{fish.price  }}</h2>
  <button @click="changeName">改变鱼</button>
  <button @click="addPrice">涨价</button>
  <h2>鱼的列表</h2>
  <ul>
    <li v-for="item in fishs" :key="item.id">
      {{ item.name }}:{{ item.price }}
    </li>
  </ul>
  <button @click="changeThreePrice">改变第三条鱼的价格</button>

</template>
<script setup>
import { reactive } from 'vue'
let fish = reactive({ name: '鲫鱼', price: 10 });
let fishs = reactive([
  {id:'txdi01',name:'鲫鱼',price:10},
  {id:'txdi02',name:'鲤鱼',price:20},
  {id:'txdi03',name:'草鱼',price:30},
])
function changeName() {
  fish.name = '草鱼';
  console.log(fish);
  console.log(fish.name);

}
function addPrice() {
  fish.price += 10;

}
function changeThreePrice() {
  fishs[1].price += 10;
}
</script>

在浏览器访问http://localhost:5173/,数据渲染和修改都正确,如下图 在这里插入图片描述 通过reactive定义的数据,都是Proxy,这是JavaScript内置对象,它的数据存放在Target中。其结构如下图: 在这里插入图片描述

reactive定义的响应式对象是深层次

对象的深层次,是其成员变量中含有成员变量,其成员变量又含有成员变量,当修改最底层成员变量值时,数据也是响应式的。如下:

<template>
  <h2>鱼类:{{ a.b.c.color }}</h2>
  <button @click="changeC">深度改变</button>
</template>
<script setup>
import { reactive } from 'vue'
let a = reactive({
  b: {
    c: {
      color: 'red'
    }
  }
})
a.b.c.color='black'
function changeC() {
  a.b.c.color = 'black';
}
</script>

访问浏览器,点击深度改变按钮,发现color值是响应式。用reactive定义的响应式对象都是深层次的。

最新版vue3+TypeScript开发入门到实战教程之学会vue3第一步必是setup语法糖

作者 angerdream
2026年3月12日 15:49

setup 概述

在vue3中,若没有学好setup函数,后面学习vue3将会越学越乱。 setup是一个函数,它在vue3是一个新的配置项,是组合式语法 (Composition API)表演的舞台,组件中所用的属性、计算属性、方法、监视等等,均需要在setup中配置。 setup特性:

  • setup函数中没有this,它是undefined
  • setup函数返回的对象内容,可直接在模版中调用
  • setup函数在beforeCreate之前调用,领先所有生命周期钩子。

最简单setup事例语法

上节 vue3与vue2语法优劣对比中,vue2语法是选项式(OptionsAPI),如下图: 在这里插入图片描述 既然vue3是组合式语法 (Composition API),它就不应该有data,methods,而是所有内容都合并在一起,删除data与methos,建立setup函数,定义数据与方法,流程如下

  • 删除data内容
  • 删除methos内容
  • 创建setup函数
  • 在setup函数中定义变量与方法
  • setup函数要返回数据与方法供模版使用
<template>
  <div class="car">
    <h2>品牌:{{ name }}</h2>
    <h2>价格:{{ price }}万</h2>
    <button @click="changeName">修改品牌</button>
    <button @click="changePrice">修改价格</button>
    <button @click="showLowPrice">查看低价</button>

  </div>
</template>
<script lang="ts">
export default{
  name: 'Car',
  setup() {
    console.log(this)
    let name = '奔驰';
    let price = 100;
    let lowPrice = 80;
    function changeName() {
      name = '宝马'
      console.log(name)

    }
    function changePrice() {
      price += 10;
      console.log(price)

    }
    function showLowPrice() {
      alert(lowPrice);
    }
    return { name, price, changeName, changePrice, showLowPrice }
  }
}
</script>

浏览器输入http://localhost:5173,效果如下: 在这里插入图片描述

  • 首次打开页面,控制台首先输出this为undefined,setup没有this
  • 页面能够渲染品牌与价格
  • 点击修改品牌,name值能够修改,但页面没有变化
  • 点击修改价格,price值能够修改,但页面没有变化
  • 点击查看低价,控制台正确输出 在vue2中data定义的数据是响应式数据,但在vue3这种方式定义的数据不是响应式。在vue3中有五中类型的响应式数据。在下期细讲明,避免与setup知识点理解不清,暂不提及。name、price、lowPrice都不是响应式数据。

setup与data、methods常常被问到面试题

在vue组件中,常常有人将vue2语法与vue3语法混着写,既在data定义数据,又在setup定义数据。当使用函数访问数据中,问题出现。setup数据能否访问data数据,反之亦能否?页面属性与方法非常混乱,所以在vue3中,不要去写vue2语法,实在搞不定再去写。

  • setup与data、methods可以共存
  • data、methods能访问setup数据与方法
  • setup不能访问data中的数据与方法

setup与data、methods可以共存

继上面的事例,给car新增一个color颜色属性,用vue2语法编写

<template>
  <div class="car">
    <h2>品牌:{{ name }}</h2>
    <h2>价格:{{ price }}万</h2>
    <h2>颜色:{{ color }}</h2>
    <button @click="changeName">修改品牌</button>
    <button @click="changePrice">修改价格</button>
    <button @click="showLowPrice">查看低价</button>
    <button @click="changeColor">修改颜色</button>

  </div>
</template>
<script lang="ts">
export default{
  name: 'Car',
  data() {
    return {
      color:'红色'
    }
  },
  methods: {
    changeColor() {
      this.color='蓝色'
    }
  },
  setup() {
    let name = '奔驰';
    let price = 100;
    let lowPrice = 80;
    function changeName() {
      name = '宝马'
      console.log(name)

    }
    function changePrice() {
      price += 10;
      console.log(price)

    }
    function showLowPrice() {
      console.log(lowPrice)

    }
    return { name, price, changeName, changePrice, showLowPrice }
  }
}
</script>

在浏览器访问,发现可以共存,且color是响应式数据。如图:在这里插入图片描述

data、methods能访问setup数据与方法

在data中修改color默认赋值为name+'color',修改methods函数changeColor,让它访问name属性,调用修改价格函数

 data() {
    return {
      color:name+'color'
    }
  },
  methods: {
    changeColor() {
      this.color = '蓝色'
      console.log(this.name);
      this.changePrice();
    }
  },

在浏览器访问,效果如下图 在这里插入图片描述

  • 属性颜色,显示奔驰红色
  • 修改颜色函数,控制台输出name,并调用修改价格函数 以下是具体代码
<template>
  <div class="car">
    <h2>品牌:{{ name }}</h2>
    <h2>价格:{{ price }}万</h2>
    <h2>颜色:{{ color }}</h2>
    <button @click="changeName">修改品牌</button>
    <button @click="changePrice">修改价格</button>
    <button @click="showLowPrice">查看低价</button>
    <button @click="changeColor">修改颜色</button>

  </div>
</template>
<script lang="ts">
export default{
  name: 'Car',
  data() {
    return {
      color: this.name + '红色'
    }
  },
  methods: {
    changeColor() {
      this.color = '蓝色'
      console.log(this.name);
      this.changePrice();
    }
  },
  setup() {
    let name = '奔驰';
    let price = 100;
    let lowPrice = 80;
    function changeName() {
      name = '宝马'
      console.log(name)

    }
    function changePrice() {
      price += 10;
      console.log(price)

    }
    function showLowPrice() {
      console.log(lowPrice)

    }
    return { name, price, changeName, changePrice, showLowPrice }
  }
}
</script>

setup数据与方法不能访问data数据与methods方法

  • 在setup函数中打印color属性,提示异常:Uncaught ReferenceError: color is not defined,页面无法渲染
  • 在setup方法中访问color属性,提示异常:Uncaught ReferenceError: color is not defined 在这里插入图片描述

让setup函数更优雅

set函数需要返回值,否则模版中无法访问setup定义的属性

setup() {
    let name = '奔驰';
    return { name }
  }

如若新增一个属性weight重量,,就需要返回weight属性

setup() {
    let name = '奔驰';
    return { name,weight }
  }

当有许多个属性时,代码很繁琐,且return { name,weight }是无必要的。只需要在script标签中添加setup可优雅解决

创建一个最简setup函数

<template>
  <div class="car">
    <h2>品牌:{{ name }}</h2>
    <button @click="changeName">修改品牌</button>
  </div>
</template>
<script setup lang="ts">
let name = '奔驰';
function changeName() {
  name = '宝马'
}
</script>

注意script有setup标识,在setup里声明的属性和方法,模版都可以访问。

❌
❌