vue3快速入门

1. Vue3简介

1.1. 性能的提升

  • 打包大小减少41%

  • 初次渲染快55%, 更新渲染快133%

  • 内存减少54%

1.2.源码的升级

  • 使用Proxy代替defineProperty实现响应式。

  • 重写虚拟DOM的实现和Tree-Shaking

1.3. 拥抱TypeScript

  • Vue3可以更好的支持TypeScript

1.4. 新的特性

  1. Composition API(组合API):

    • setup

    • refreactive

    • computedwatch

  2. 新的内置组件:

    • Fragment

    • Teleport

    • Suspense

  3. 其他改变:

    • 新的生命周期钩子

    • 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.cnvite的优势如下:

  • 轻量快速的热重载(HMR),能实现极速的服务启动。
  • TypeScriptJSXCSS 等支持开箱即用(不用配置,直接就可以用)。
  • 真正的按需编译,不再等待整个应用编译完成。
  • 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

  • Vue2API设计是Options(配置)风格的。
  • Vue3API设计是Composition(组合)风格的。

Options API 的弊端

Options类型的 API,数据、方法、计算属性等,是分散在:datamethodscomputed中的,若想新增或者修改一个需求,就需要分别修改:datamethodscomputed,不便于维护和复用。

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

Composition API 的优势

可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。

在这里插入图片描述

在这里插入图片描述

3.2. 拉开序幕的 setup

setup 概述

介绍

  • setupVue3中一个新的配置项,值是一个函数。
  • 它是 Composition API “表演的舞台,组件中所用到的:数据、方法、计算属性、监视…等等,均配置在setup中。

特点如下:

  • setup函数返回的对象中的内容,可直接在模板中使用。
  • setup中访问thisundefined
  • 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 的配置(datamethos…)中可以访问到 setup中的属性、方法。
  • 但在setup不能访问到Vue2的配置(datamethos…)。
  • 如果与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>

扩展:上述代码,还需要编写一个不写setupscript标签,去指定组件名字,比较麻烦,我们可以借助vite中的插件简化

  1. 第一步:npm i vite-plugin-vue-setup-extend -D
  2. 第二步: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))
    }
  }
})

  1. 第三步:<script setup lang="ts" name="Person">

3.3. ref 创建:基本类型的响应式数据

  • **作用:**定义响应式变量。
  • 语法:let xxx = ref(初始值)
  • **返回值:**一个RefImpl的实例对象,简称ref对象refref对象的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

  • 若需要一个响应式对象,层级不深,refreactive都可以。

  • 若需要一个响应式对象,且层级较深,推荐使用reactive

3.7 toRefs 与 toRef

  • 作用:将一个响应式对象中的每一个属性,转换为ref对象。
  • 备注:toRefstoRef功能一致,但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>

相关推荐

  1. Vue3快速入门

    2024-02-04 19:10:02       30 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-04 19:10:02       14 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-04 19:10:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-04 19:10:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-04 19:10:02       18 阅读

热门阅读

  1. node 版本管理器 --- Volta

    2024-02-04 19:10:02       34 阅读
  2. 介绍 HTTPS 中间人攻击

    2024-02-04 19:10:02       33 阅读
  3. PLM传输生产工艺至SAP-RFC代码

    2024-02-04 19:10:02       29 阅读
  4. linux基础工具-make/makefile

    2024-02-04 19:10:02       33 阅读
  5. android 4.4 audio 框架

    2024-02-04 19:10:02       28 阅读
  6. 20240203周报—Tomcat暂时收尾,SpringBoot开始

    2024-02-04 19:10:02       34 阅读
  7. c# 语音播报

    2024-02-04 19:10:02       33 阅读
  8. BindingResult的作用

    2024-02-04 19:10:02       23 阅读