vue3快速入门
文章目录
- 1. Vue3简介
- 1.1. 性能的提升
- 1.2.源码的升级
- 1.3. 拥抱TypeScript
- 1.4. 新的特性
- 2. 创建Vue3工程
- 2.1. 基于 vue-cli 创建
- 2.2. 基于 vite 创建(推荐)
- vite介绍
- 创建步骤
- 项目结构
- 安装插件
- 项目结构
- 总结
- 2.3. 一个简单的效果
- Person.vue
- App.vue
- 3. Vue3核心语法
- 3.1. OptionsAPI 与 CompositionAPI
- Options API 的弊端
- Composition API 的优势
- 3.2. 拉开序幕的 setup
- setup 概述
- setup 的返回值
- setup 与 Options API 的关系
- setup 语法糖
- 3.3. ref 创建:基本类型的响应式数据
- 3.4. reactive 创建:对象类型的响应式数据
- 3.5. 【ref 创建:对象类型的响应式数据】
- 3.6. 【ref 对比 reactive】
- 宏观角度
- 区别
- 使用原则
- 3.7 toRefs 与 toRef
- 现象
- toRefs&toRef的使用
1. Vue3简介
-
2020年9月18日,
Vue.js
发布版3.0
版本,代号:One Piece
(n -
经历了:4800+次提交、40+个RFC、600+次PR、300+贡献者
-
官方发版地址:Release v3.0.0 One Piece · vuejs/core
-
截止2023年10月,最新的公开版本为:
3.3.4
1.1. 性能的提升
-
打包大小减少
41%
。 -
初次渲染快
55%
, 更新渲染快133%
。 -
内存减少
54%
。
1.2.源码的升级
-
使用
Proxy
代替defineProperty
实现响应式。 -
重写虚拟
DOM
的实现和Tree-Shaking
。
1.3. 拥抱TypeScript
Vue3
可以更好的支持TypeScript
。
1.4. 新的特性
-
Composition API
(组合API
):-
setup
-
ref
与reactive
-
computed
与watch
…
-
-
新的内置组件:
-
Fragment
-
Teleport
-
Suspense
…
-
-
其他改变:
-
新的生命周期钩子
-
data
选项应始终被声明为一个函数 -
移除
keyCode
支持作为v-on
的修饰符…
-
2. 创建Vue3工程
2.1. 基于 vue-cli 创建
点击查看 Vue-Cli 官方文档,(基于vue-cli创建,其实就是基于webpack来创建vue项目)
备注:目前
vue-cli
已处于维护模式,官方推荐基于Vite
创建项目。
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 执行创建命令
vue create vue_test
## 随后选择3.x
## Choose a version of Vue.js that you want to start the project with (Use arrow keys)
## > 3.x
## 2.x
## 启动
cd vue_test
npm run serve
2.2. 基于 vite 创建(推荐)
vite介绍
vite
是新一代前端构建工具,官网地址:https://vitejs.cn,vite
的优势如下:
- 轻量快速的热重载(
HMR
),能实现极速的服务启动。 - 对
TypeScript
、JSX
、CSS
等支持开箱即用(不用配置,直接就可以用)。 - 真正的按需编译,不再等待整个应用编译完成。
webpack
构建 与vite
构建对比图如下:
创建步骤
具体操作如下(点击查看官方文档)
## 1.创建命令(基于vite创建vue3项目,前提是需要安装nodejs环境)
npm create vue@latest
## 2.具体配置
## 配置项目名称
√ Project name: vue3_test
## 是否添加TypeScript支持
√ Add TypeScript? Yes
## 是否添加JSX支持
√ Add JSX Support? No
## 是否添加路由环境
√ Add Vue Router for Single Page Application development? No
## 是否添加pinia环境
√ Add Pinia for state management? No
## 是否添加单元测试
√ Add Vitest for Unit Testing? No
## 是否添加端到端测试方案
√ Add an End-to-End Testing Solution? » No
## 是否添加ESLint语法检查
√ Add ESLint for code quality? Yes
## 是否添加Prettiert代码格式化
√ Add Prettier for code formatting? No
构建过程如下:
访问vue3项目如下:
项目结构
安装插件
安装官方推荐的vscode
插件:
项目结构
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
main.ts
import './assets/main.css'
// 引入createApp用于创建应用
import { createApp } from 'vue'
// 引入App根组件
import App from './App.vue'
createApp(App).mount('#app')
App.vue
<!-- 自己动手编写的一个App组件 -->
<template>
<div class="app">
<h1>你好啊!</h1>
</div>
</template>
<script lang="ts"> // 添加lang="ts", 里面写ts或js都可以
export default {
name:'App' //组件名
}
</script>
<style>
.app {
background-color: #ddd;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
</style>
总结
Vite
项目中,index.html
是项目的入口文件,在项目最外层。- 加载
index.html
后,Vite
解析<script type="module" src="xxx">
指向的JavaScript
。 Vue3
在main.ts中是通过createApp
函数创建一个应用实例。
2.3. 一个简单的效果
Vue3
向下兼容Vue2
语法,且Vue3
中的模板中可以没有根标签
Person.vue
<template>
<div class="person">
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">年龄+1</button>
<button @click="showTel">点我查看联系方式</button>
</div>
</template>
<script lang="ts">
export default {
name:'App',
data() {
return {
name:'张三',
age:18,
tel:'13888888888'
}
},
methods:{
changeName(){
this.name = 'zhang-san'
},
changeAge(){
this.age += 1
},
showTel(){
alert(this.tel)
}
},
}
</script>
App.vue
<template>
<div class="app">
<h1>你好啊!</h1>
<Person/>
</div>
</template>
<script lang="ts">
import Person from './components/Person.vue'
export default {
name:'App', //组件名
components:{Person} //注册组件
}
</script>
<style>
.app {
background-color: #ddd;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
</style>
3. Vue3核心语法
3.1. OptionsAPI 与 CompositionAPI
Vue2
的API
设计是Options
(配置)风格的。Vue3
的API
设计是Composition
(组合)风格的。
Options API 的弊端
Options
类型的 API
,数据、方法、计算属性等,是分散在:data
、methods
、computed
中的,若想新增或者修改一个需求,就需要分别修改:data
、methods
、computed
,不便于维护和复用。
Composition API 的优势
可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。
3.2. 拉开序幕的 setup
setup 概述
介绍
setup
是Vue3
中一个新的配置项,值是一个函数。- 它是
Composition API
“表演的舞台”,组件中所用到的:数据、方法、计算属性、监视…等等,均配置在setup
中。
特点如下:
setup
函数返回的对象中的内容,可直接在模板中使用。setup
中访问this
是undefined
。setup
函数会在beforeCreate
之前调用,它是“领先”所有钩子执行的。
<template>
<div class="person">
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">年龄+1</button>
<button @click="showTel">点我查看联系方式</button>
</div>
</template>
<script lang="ts">
export default {
name:'Person',
// 生命周期函数
beforeCreate(){
console.log('beforeCreate')
},
setup(){
// 先打印的setup..., 再打印的beforeCreate, 说明了setup函数与beforeCreate生命周期函数的执行顺序
console.log('setup ...')
// 【setup函数中的this是undefined】
console.log(this); // undefined
// 数据,原来写在data中【注意:此时的name、age、tel数据都不是响应式数据】
// (不是响应式的意思是:当这些数据变化,并不会触发dom更新,
// 模板中应用这些变量的地方没有重新渲染)
let name = '张三'
let age = 18
let tel = '13888888888'
// 方法,原来写在methods中
function changeName(){
name = 'zhang-san' // 注意:此时这么修改name页面是不变化的
console.log(name) // (name确实改了,但name不是响应式的)
}
function changeAge(){
age += 1 // 注意:此时这么修改age页面是不变化的
console.log(age) // (age确实改了,但age不是响应式的)
}
function showTel(){
alert(tel)
}
// 返回一个对象,对象中的内容,模板中可以直接使用(将数据、方法交出去,模板中才可以使用这些交出去的数据、方法)
return {name,age,tel,changeName,changeAge,showTel}
}
}
</script>
setup 的返回值
- 若返回一个对象:则对象中的:属性、方法等,在模板中均可以直接使用**(重点关注)。**
- 若返回一个函数:则可以直接指定 自定义渲染的内容,代码如下:
<template>
<div class="person">
我特么一点都不重要了
</div>
</template>
<script lang="ts">
export default {
name:'Person',
setup(){
// setup的返回值也可以是一个渲染函数
// (模板什么的都不重要了,直接在页面上渲染成:你好啊!这几个字)
// return ()=>'哈哈'
}
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
setup 与 Options API 的关系
Vue2
的配置(data
、methos
…)中可以访问到setup
中的属性、方法。- 但在
setup
中不能访问到Vue2
的配置(data
、methos
…)。 - 如果与
Vue2
冲突,则setup
优先。
<template>
<div class="person">
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="showTel">查看联系方式</button>
<hr>
<h2>测试1:{{a}}</h2>
<h2>测试2:{{c}}</h2>
<h2>测试3:{{d}}</h2>
<button @click="b">测试</button>
</div>
</template>
<script lang="ts">
export default {
name:'Person',
beforeCreate(){
console.log('beforeCreate')
},
data(){
return {
a:100,
// 在data配置项中, 可以使用this.name来使用setup中交出的数据, 因为setup执行时机更早。
// 但是在setup中不能使用在data中定义的数据
c:this.name,
d:900,
age:90
}
},
methods:{
b(){
console.log('b')
}
},
// setup可以与data、methods等配置项同时存在
setup(){
// 数据,原来是写在data中的,此时的name、age、tel都不是响应式的数据
let name = '张三'
let age = 18
let tel = '13888888888'
// 方法
function changeName() {
name = 'zhang-san' // 注意:这样修改name,页面是没有变化的
console.log(name) // name确实改了,但name不是响应式的
}
function changeAge() {
age += 1 // 注意:这样修改age,页面是没有变化的
console.log(age) // age确实改了,但age不是响应式的
}
function showTel() {
alert(tel)
}
// 将数据、方法交出去,模板中才可以使用
return {name,age,tel,changeName,changeAge,showTel}
// setup的返回值也可以是一个渲染函数
// return ()=>'哈哈'
}
}
</script>
<style scoped>
.person {
background-color: skyblue;
box-shadow: 0 0 10px;
border-radius: 10px;
padding: 20px;
}
button {
margin: 0 5px;
}
</style>
setup 语法糖
setup
函数有一个语法糖,这个语法糖,可以让我们把setup
独立出去,代码如下:
<template>
<div class="person">
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<button @click="changName">修改名字</button>
<button @click="changAge">年龄+1</button>
<button @click="showTel">点我查看联系方式</button>
</div>
</template>
<!-- 专门单个弄个script标签, 特地来配置组件的名字 -->
<script lang="ts">
export default {
name:'Person',
}
</script>
<!-- 下面的写法是setup语法糖 -->
<!-- 1. 相当于写了setup函数;
2. 相当于自动把其中定义的变量交出去)-->
<script setup lang="ts">
console.log(this) // undefined
// 数据(注意:此时的name、age、tel都不是响应式数据)
let name = '张三'
let age = 18
let tel = '13888888888'
// 方法
function changName(){
name = '李四'//注意:此时这么修改name页面是不变化的
}
function changAge(){
console.log(age)
age += 1 //注意:此时这么修改age页面是不变化的
}
function showTel(){
alert(tel)
}
</script>
扩展:上述代码,还需要编写一个不写
setup
的script
标签,去指定组件名字,比较麻烦,我们可以借助vite
中的插件简化
- 第一步:
npm i vite-plugin-vue-setup-extend -D
- 第二步:
vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
VueSetupExtend(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
- 第三步:
<script setup lang="ts" name="Person">
3.3. ref 创建:基本类型的响应式数据
- **作用:**定义响应式变量。
- 语法:
let xxx = ref(初始值)
。 - **返回值:**一个
RefImpl
的实例对象,简称ref对象
或ref
,ref
对象的value
属性是响应式的。 - 注意点:
JS
中操作数据需要:xxx.value
,但模板中不需要.value
,直接使用即可。- 对于
let name = ref('张三')
来说,name
不是响应式的,name.value
是响应式的。
<template>
<div class="person">
<!-- 模板中直接使用, 不需要.value -->
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>电话:{{tel}}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">年龄+1</button>
<button @click="showTel">点我查看联系方式</button>
</div>
</template>
<!-- 使用了setup语法糖, 会自动将定义的变量和方法交出去, 以供给模板使用 -->
<script setup lang="ts" name="Person">
// 引入vue中的ref函数
import { ref } from 'vue'
// name和age是一个RefImpl的实例对象,简称ref对象,它们的value属性是响应式的。
//(所谓的响应式指的是, 对数据的改变后, 能够让模板中使用该数据的地方得到重新渲染更新)
// ref是1个函数, 向这个ref函数中传入参数, 返回的是1个RefImpl的实例对象
let name = ref('张三')
let age = ref(18)
// tel就是一个普通的字符串,不是响应式的
let tel = '13888888888'
function changeName(){
// JS中操作ref对象时候需要.value
name.value = '李四' // 页面得到刷新
console.log(name.value)
// 注意:name不是响应式的,name.value是响应式的,所以如下代码并不会引起页面的更新。
// name = ref('zhang-san')
}
function changeAge(){
// JS中操作ref对象时候需要.value
age.value += 1 // 页面得到刷新
console.log(age.value)
}
function showTel(){
// tel是普通数据
tel += '1' // tel的确改了, 但页面并未刷新
alert(tel)
}
</script>
3.4. reactive 创建:对象类型的响应式数据
- 作用:定义一个响应式对象(基本类型不要用它,要用
ref
,否则报错) - 语法:
let 响应式对象= reactive(源对象)
。 - **返回值:**一个
Proxy
的实例对象,简称:响应式对象。 - 注意点:
reactive
定义的响应式数据是“深层次”的。
<template>
<div class="person">
<h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
<h2>游戏列表:</h2>
<ul>
<li v-for="g in games" :key="g.id">{{ g.name }}</li>
</ul>
<h2>测试:{{ obj.a.b.c.d }}</h2>
<button @click="changeCarPrice">修改汽车价格</button>
<button @click="changeFirstGame">修改第一游戏</button>
<button @click="test">测试</button>
</div>
</template>
<script lang="ts" setup name="Person">
import { reactive } from 'vue'
// 定义数据
// reactive是1个函数, 向这个reactive函数中传入参数(传入对象或数组), 返回的是1个Proxy的实例对象
//(Proxy是原生Js就有的函数)
// reactive函数中传入对象
let car = reactive({ brand: '奔驰', price: 100 })
console.log('car', car); // car Proxy {brand: '奔驰', price: 100}
// reactive函数传入数组
let games = reactive([
{ id: 'ahsgdyfa01', name: '英雄联盟' },
{ id: 'ahsgdyfa02', name: '王者荣耀' },
{ id: 'ahsgdyfa03', name: '原神' }
])
// reactive定义的响应式数据是 深层次 的
let obj = reactive({
a: {
b: {
c: {
d: 666
}
}
}
})
// 修改对象中的属性(修改使用reactive包裹对象后返回的对象)
function changeCarPrice() {
car.price += 10
}
// 修改数组中的对象的属性(修改使用reactive包裹数组后返回的对象)
function changeFirstGame() {
games[0].name = '流星蝴蝶剑'
}
function test() {
obj.a.b.c.d = 999
}
</script>
3.5. 【ref 创建:对象类型的响应式数据】
- 其实
ref
接收的数据可以是:基本类型、对象类型。 - 若
ref
接收的是对象类型,内部其实也是调用了reactive
函数。
<template>
<div class="person">
<h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2>
<h2>游戏列表:</h2>
<ul>
<li v-for="g in games" :key="g.id">{{ g.name }}</li>
</ul>
<h2>测试:{{ obj.a.b.c.d }}</h2>
<button @click="changeCarPrice">修改汽车价格</button>
<button @click="changeFirstGame">修改第一游戏</button>
<button @click="test">测试</button>
</div>
</template>
<script lang="ts" setup name="Person">
import { ref,reactive } from 'vue'
// 使用ref定义对象类型响应式数据
let car = ref({ brand: '奔驰', price: 100 })
// 使用reactive定义对象类型响应式数据
let car2 = reactive({brand: '奔驰', price: 100})
// reactive只能用来定义对象类型的响应式数据
// let name = reactive('zhangsan') // 错误, value cannot be made reactive: zhangsan
// 使用ref定义对象(数组)类型响应式数据
let games = ref([
{ id: 'ahsgdyfa01', name: '英雄联盟' },
{ id: 'ahsgdyfa02', name: '王者荣耀' },
{ id: 'ahsgdyfa03', name: '原神' }
])
// 使用ref定义对象类型响应式数据也是深层次的
let obj = ref({
a: {
b: {
c: {
d: 666
}
}
}
})
// 若ref接收的是对象类型,内部其实也是使用的reactive函数
console.log(car) // RefImpl {__v_isShallow: false, dep: undefined,
// __v_isRef: true, _rawValue: {…}, _value: Proxy}
console.log(car.value) // Proxy {brand: '奔驰', price: 100}
console.log(car2) // Proxy {brand: '奔驰', price: 100}
function changeCarPrice() {
// 使用ref函数定义的响应式数据, 在js操作时, 需要带上.value, 才能碰到内部的Proxy对象
car.value.price += 10
console.log(car.value.price);
}
function changeFirstGame() {
// 使用ref函数定义的响应式数据, 在js操作时, 需要带上.value, 才能碰到内部的Proxy对象
games.value[0].name = '流星蝴蝶剑'
console.log(games.value); // Proxy {0: {…}, 1: {…}, 2: {…}}
}
function test() {
// 使用ref函数定义的响应式数据, 在js操作时, 需要带上.value, 才能碰到内部的Proxy对象
obj.value.a.b.c.d = 999
}
</script>
3.6. 【ref 对比 reactive】
宏观角度
-
ref可以定义:基本类型、对象类型的响应式数据
-
reactive只能定义:对象类型的响应式数据
区别
-
ref创建的变量必须使用
.value
(可以使用volar
插件自动添加.value
)。可以在齿轮->设置->扩展->volar中勾选
,它会在使用ref创建的变量时,自动添加上.value -
reactive重新分配一个新对象,会失去响应式(可以使用
Object.assign
去整体替换)。<template> <div class="person"> <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2> <button @click="changeBrand">改品牌</button> <button @click="changePrice">改价格</button> <button @click="changeCar">改car</button> </div> </template> <script lang="ts" setup name="Person"> import { ref,reactive } from 'vue' let car = reactive({brand:'奔驰', price:100}) function changeBrand() { // 正常修改car的brand, 并且是响应式 car.brand = '宝马' } function changePrice() { // 正常修改car的price, 并且是响应式 car.price += 10 } function changeCar() { // 错误做法1 // 不可以直接给reactive重新分配一个新对象,这会让car直接失去响应式 // car = {brand:'奥托', price:10} // 错误做法2 // 这样也不行, 因为模板中用的car是上面定义的响应式对象, // 现在car指向的是1个新的响应式对象, 而模板中压根就没有使用这个新的响应式对象 // car = reactive({brand:'奥托', price:10}) // 正确做法(car仍然是响应式的) // API介绍: Object.assign(obj1, obj2, obj3, ..), // 将obj2中的每一组属性和值设置到obj1中, 然后obj3的每一组属性和值设置到obj1中 Object.assign(car, {brand:'奥托', price:10}) } </script>
<template> <div class="person"> <h2>汽车信息:一台{{ car.brand }}汽车,价值{{ car.price }}万</h2> <button @click="changeBrand">改品牌</button> <button @click="changePrice">改价格</button> <button @click="changeCar">改car</button> </div> </template> <script lang="ts" setup name="Person"> import { ref,reactive } from 'vue' let car = ref({brand:'奔驰', price:100}) function changeBrand() { // 正常修改car的brand, 并且是响应式 car.value.brand = '宝马' } function changePrice() { // 正常修改car的price, 并且是响应式 car.value.price += 10 } function changeCar() { // 错误做法1 // 不能直接给car换了个ref, 因为模板中压根就没有使用这个新的RefImpl对象 // car = ref({brand:'奥托', price:10}) // 正确做法1(car仍然是响应式的) // API介绍: Object.assign(obj1, obj2, obj3, ..), 将obj2中的每一组属性和值设置到obj1中, // 然后obj3的每一组属性和值设置到obj1中 // Object.assign(car.value, {brand:'奥托', price:10}) // 正确做法2 //(这里相比于对car使用reactive定义而言, 使用ref定义则可以直接给car.value整体赋值 // 原因在于car.value获取的是Proxy响应式对象, 凡是对Proxy响应式对象的操作都可以被拦截到) car.value = {brand:'奥托', price:10} } </script>
使用原则
-
若需要一个基本类型的响应式数据,必须使用
ref
。 -
若需要一个响应式对象,层级不深,
ref
、reactive
都可以。 -
若需要一个响应式对象,且层级较深,推荐使用
reactive
。
3.7 toRefs 与 toRef
- 作用:将一个响应式对象中的每一个属性,转换为
ref
对象。 - 备注:
toRefs
与toRef
功能一致,但toRefs
可以批量转换。
现象
对响应式对象直接结构赋值,得到的数据不是响应式的
<template>
<div class="person">
<h2>姓名:{{ person.name }} {{ name }}</h2>
<h2>年龄:{{ person.age }} {{ age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
</div>
</template>
<script lang="ts" setup name="Person2">
import { ref, reactive, toRefs, toRef } from 'vue'
// 数据
let person = reactive({ name: '张三', age: 18 })
console.log(person); // Proxy {name: '张三', age: 18}
// 这里的解构赋值其实就等价于: let name = person.name; let age = person.age;
// 只是记录了此时person.name、person.age的值, 仅此而已
// 因此, 此处使用结构赋值语法获取的name和age都不是响应式的
let {name, age } = person
console.log(name, age); // 张三 18
// 方法
function changeName() {
name += '~'
console.log(name, person.name); // 变化的是name, 而person.name仍然未修改
}
function changeAge() {
age += 1
console.log(age, person.age); // 变化的是age, 而person.age仍然未修改
}
</script>
toRefs&toRef的使用
通过toRefs将person对象中的所有属性都批量取出, 且依然保持响应式的能力
<template>
<div class="person">
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>性别:{{ person.gender }} {{ gender }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changeGender">修改性别</button>
<button @click="changeGender2">修改性别2</button>
</div>
</template>
<script lang="ts" setup name="Person">
import { ref, reactive, toRefs, toRef } from 'vue'
// 数据
let person = reactive({ name: '张三', age: 18, gender: '男' })
// 通过toRefs将person对象中的所有属性都批量取出, 且依然保持响应式的能力
//(使用toRefs从person这个响应式对象中,解构出name、age, 且name和age依然是响应式的,
// name和gender的值是ref类型, 其value值指向的是person.name和person.age,
// 对name.value和对age.value的修改将会修改person.name和person.age, 并且会页面渲染刷新)
let { name, age } = toRefs(person)
console.log(name.value, name); // '张三' ObjectRefImpl {_object: Proxy, _key: 'name',
// _defaultValue: undefined, __v_isRef: true}
console.log(age.value, age.value); // 18 ObjectRefImpl {_object: Proxy, _key: 'age',
// _defaultValue: undefined, __v_isRef: true}
console.log(toRefs(person)); // {name: ObjectRefImpl, age: ObjectRefImpl,
// gender: ObjectRefImpl}
// 通过toRef将person对象中的gender属性取出,且依然保持响应式的能力
let gender = toRef(person, 'gender')
console.log(gender, gender.value); // ObjectRefImpl {_object: Proxy, _key: 'gender',
// _defaultValue: undefined, __v_isRef: true} '男'
// 方法
function changeName() {
// 此处修改name.value, 将会修改person.name, 并且页面会刷新person.name的值
name.value += '~'
console.log(name.value, person.name);
}
function changeAge() {
// 此处修改age.value, 将会修改person.age, 并且页面会刷新person.age的值
age.value += 1
console.log(age.value, person.age);
}
function changeGender() {
// 此处修改gender.value, 将会修改person.age, 并且页面会刷新person.gender的值
gender.value = '女'
console.log(gender.value, person.gender);
}
function changeGender2() {
// 此处对person.gender的修改, 将会修改上面的let gender = toRef(person, 'gender')
// 并且页面会刷新person.gender和gender的值
person.gender = '男'
console.log(gender.value, person.gender);
}
</script>