当前位置: 首页 > news >正文

前端笔记-Vue3(上)

学习参考视频:尚硅谷Vue3入门到实战,最新版vue3+TypeScript前端开发教程_哔哩哔哩_bilibili

vue3学习目标:

VUE 31、Vue3架构与设计理念
2、组合式API(Composition API)
3、常用API:ref、reactive、watchcomputed
4Vue3的生命周期
5、组件间通信(propsemitdefineModel
6、了解插槽

Vue3简介

1. 核心特性

  • 组合式 API (Composition API)​​:替代Vue 2的选项式API,提供更灵活的代码组织方式
  • 性能提升​:比Vue 2快2倍,包体积小41%
  • 更好的TypeScript支持​:完整的类型定义
  • 新的响应式系统​:基于Proxy实现,性能更好
  • Fragment/Teleport/Suspense​:新增内置组件

2. 主要改进

特性Vue 2Vue 3
响应式系统Object.definePropertyProxy
代码组织选项式API组合式API
虚拟DOM全量对比静态标记+动态对比
打包体积完整版23kb最小10kb

创建Vue 3项目

具体可以查看一下官方文档:快速上手 | Vue.js

1. 通过Vite创建(推荐)

基于vite构建(类似于webpack,比webpack还快),对TS,JSX,CSS支持开箱即用。

npm create vite@latest my-vue-app --template vue
cd my-vue-app
npm install
npm run dev

2. 通过Vue CLI创建

npm install -g @vue/cli
vue create my-vue-app
# 选择Vue 3预设
cd my-vue-app
npm run serve

Vue 3生命周期对比

Vue 2选项Vue 3组合式API说明
beforeCreatesetup()组件初始化前
createdsetup()组件初始化后
beforeMountonBeforeMountDOM挂载前
mountedonMountedDOM挂载后
beforeUpdateonBeforeUpdate数据更新前
updatedonUpdated数据更新后
beforeUnmountonBeforeUnmount组件卸载前
unmountedonUnmounted组件卸载后

组合式API

特性选项式 API (Options API)组合式 API (Composition API)
代码组织方式按选项类型分组按逻辑功能组织
复用机制Mixins/作用域插槽组合式函数
响应式系统自动响应式显式声明响应式
this 使用需要不需要
TypeScript 支持有限优秀
学习曲线平缓较陡
适用场景简单组件/新手友好复杂组件/大型项目

1. 代码组织方式

选项式 API​:

export default {data() {return {count: 0,searchQuery: ''}},computed: {doubleCount() {return this.count * 2}},methods: {increment() {this.count++}},mounted() {console.log('组件已挂载')}
}

组合式 API​: 

需要注意的是setup中的this是undefined

import { ref, computed, onMounted } from 'vue'export default {setup() {const count = ref(0)const searchQuery = ref('')const doubleCount = computed(() => count.value * 2)function increment() {count.value++}onMounted(() => {console.log('组件已挂载')})return {count,searchQuery,doubleCount,increment}}
}

2. 逻辑复用实现

3. 响应式系统差异

选项式 API​:

  • 自动将 data() 返回的对象转为响应式
  • 计算属性和方法自动绑定 this 上下文

组合式 API​:

  • 需要显式使用 ref() 或 reactive() 创建响应式数据
  • 需要手动暴露给模板使用的变量和方法

同样的例子使用vue2语法写过之后无法在vue3里面实现响应式变化(还有后续) 

<template><div class="student"><h2>学生姓名:{{studentName}}</h2><h2>学生年龄:{{studentAge}}</h2><h2>数学成绩:{{mathScore}}</h2><button @click="updateName">修改姓名</button><button @click="increaseAge">增加年龄</button><button @click="improveScore">提高成绩</button></div>
</template><script>
export default {name: 'Student',setup() {// 普通变量(非响应式)let studentName = '李四'let studentAge = 16let mathScore = 85// 方法function updateName() {studentName = 'Li Si' // 修改不会反映到视图中console.log('姓名已改为:', studentName)}function increaseAge() {studentAge++ // 修改不会反映到视图中console.log('年龄已增加至:', studentAge)}function improveScore() {mathScore += 5 // 修改不会反映到视图中console.log('数学成绩已提高至:', mathScore)}// 返回这些变量和方法return {studentName,studentAge,mathScore,updateName,increaseAge,improveScore}}
}
</script><style scoped>
.student {background-color: #f5f5f5;padding: 20px;margin: 20px;border-radius: 8px;
}
button {margin: 0 10px;padding: 5px 10px;
}
</style>

4. 生命周期对应关系

选项式 API组合式 API
beforeCreatesetup()
createdsetup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateon

5.setup 的返回值

  • 若返回一个对象:则对象中的:属性、方法等,在模板中均可以直接使用
  • 若返回一个函数:则可以自定义渲染内容,代码如下:(因为没有this)
setup(){return ()=> 'Hello World'
}

6. setup与Options API 的关系

  • Vue2 的配置(datamethos......)中可以访问到 setup中的属性、方法。
  • 但在setup不能访问到Vue2的配置(datamethos......)。
  • 如果与Vue2冲突,则setup优先。

7.setup 语法糖

使用setup语法糖,可以使我们使用vue3编写脚本时更加方便:

<template><div class="student"><h2>学生姓名:{{studentName}}</h2><h2>学生年龄:{{studentAge}}</h2><h2>数学成绩:{{mathScore}}</h2><button @click="updateName">修改姓名</button><button @click="increaseAge">增加年龄</button><button @click="improveScore">提高成绩</button></div>
</template><script setup>
// 普通变量(非响应式)
let studentName = '李四'
let studentAge = 16
let mathScore = 85// 方法
function updateName() {studentName = 'Li Si' // 修改不会反映到视图中console.log('姓名已改为:', studentName)
}function increaseAge() {studentAge++ // 修改不会反映到视图中console.log('年龄已增加至:', studentAge)
}function improveScore() {mathScore += 5 // 修改不会反映到视图中console.log('数学成绩已提高至:', mathScore)
}
</script><style scoped>
.student {background-color: #f5f5f5;padding: 20px;margin: 20px;border-radius: 8px;
}
button {margin: 0 10px;padding: 5px 10px;
}
</style>

我们现在只需要写数据和方法即可,

这时候<script setup>和export default{}不能再共用,但是可以同时存在两个script(一个是用来配置名字,一个是关键配置文件)

当然还可以再简单一点,

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

  1. 第一步:npm i vite-plugin-vue-setup-extend -D
  2. 第二步:vite.config.ts
import { defineConfig } from 'vite'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'export default defineConfig({plugins: [ VueSetupExtend() ]
})
  1. 第三步:<script setup lang="ts" name="Person">

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

要使数据呈现响应式变化,可以引入ref创建响应式数据

关键区别对比

特性非响应式版本响应式版本 (setup语法糖)
语法传统 setup 函数<script setup> 语法糖
响应式❌ 数据变化不更新视图✔️ 数据变化自动更新视图
变量声明普通变量 (let)ref() 响应式引用
数据访问直接访问 (studentName)通过 .value 访问 (studentName.value)
代码简洁度需要显式 return自动暴露顶层绑定,无需 return
TypeScript支持有限更好的类型推断
组件选项可以混合使用其他选项纯组合式API风格

还是之前的例子,我们加入ref实现响应式基本类型的响应式变化

<template><div class="student"><h2>学生姓名:{{studentName}}</h2><h2>学生年龄:{{studentAge}}</h2><h2>数学成绩:{{mathScore}}</h2><button @click="updateName">修改姓名</button><button @click="increaseAge">增加年龄</button><button @click="improveScore">提高成绩</button></div>
</template><script setup>
import { ref } from 'vue'// 响应式变量
const studentName = ref('李四')
const studentAge = ref(16)
const mathScore = ref(85)// 方法
function updateName() {studentName.value = 'Li Si' // 修改会实时反映到视图console.log('姓名已改为:', studentName.value)
}function increaseAge() {studentAge.value++ // 修改会实时反映到视图console.log('年龄已增加至:', studentAge.value)
}function improveScore() {mathScore.value += 5 // 修改会实时反映到视图console.log('数学成绩已提高至:', mathScore.value)
}
</script><style scoped>
.student {background-color: #f5f5f5;padding: 20px;margin: 20px;border-radius: 8px;
}
button {margin: 0 10px;padding: 5px 10px;
}
</style>

 点击按钮,数据变化成功。

在这个背后,ref 通过 .value 属性实现响应式追踪:

  • 创建时:ref(16) → { value: 16 }
  • 修改时:检测 .value 变化触发更新
  • 模板中:自动解包 .value

另外, 基本类型优先使用ref,对于对象类型我们还可以选择reactive。


 reactive 创建:对象类型的响应式数据

当你把一个普通 JavaScript 对象传入 reactive(),它会返回该对象的响应式代理。

  • 专为对象/数组设计
  • 深度响应式​(嵌套对象也是响应式的)
  • 修改属性时自动触发视图更新
  • 比 ref 更适合处理复杂对象结构

使用reactive的组合式API版本

<template><div class="user-card"><h2>{{ user.name }}</h2><p>年龄: {{ user.age }}</p><p>城市: {{ user.address.city }}</p><button @click="growOlder">长大一岁</button><button @click="moveCity">搬到上海</button></div>
</template><script setup>
import { reactive } from 'vue'// 响应式对象
const user = reactive({name: '张三',age: 25,address: {city: '北京'}
})function growOlder() {user.age++ // 自动更新视图
}function moveCity() {user.address.city = '上海' // 自动更新视图(深度响应式)
}
</script>

reactive深度响应式特性

顾名思义,不管数据藏得有多深,只要用reative包裹起来,就会实现响应式的改变。

<template><div class="cart"><h2>购物车 ({{ cart.items.length }}件)</h2><ul><li v-for="(item, index) in cart.items" :key="index">{{ item.name }} - ¥{{ item.price }}<button @click="removeItem(index)">删除</button></li></ul><p>总价: ¥{{ cart.total }}</p><button @click="addRandomItem">添加随机商品</button></div>
</template><script setup>
import { reactive, computed } from 'vue'// 购物车状态
const cart = reactive({items: [{ name: '商品1', price: 100 },{ name: '商品2', price: 200 }],// 计算属性get total() {return this.items.reduce((sum, item) => sum + item.price, 0)}
})// 添加商品
function addRandomItem() {const randomId = Math.floor(Math.random() * 1000)cart.items.push({name: `商品${randomId}`,price: Math.floor(Math.random() * 500)})
}// 删除商品
function removeItem(index) {cart.items.splice(index, 1)
}
</script>

同样,ref也能处理对象数据类型的响应式变化

<template><div class="cart"><h2>购物车 ({{ cart.items.length }}件)</h2><ul><li v-for="(item, index) in cart.items" :key="index">{{ item.name }} - ¥{{ item.price }}<button @click="removeItem(index)">删除</button></li></ul><p>总价: ¥{{ total }}</p><button @click="addRandomItem">添加随机商品</button></div>
</template><script setup>
import { ref, computed } from 'vue'// 使用 ref 创建购物车状态
const cart = ref({items: [{ name: '商品1', price: 100 },{ name: '商品2', price: 200 }]
})// 计算总价(使用独立的计算属性)
const total = computed(() => {return cart.value.items.reduce((sum, item) => sum + item.price, 0)
})// 添加商品
function addRandomItem() {const randomId = Math.floor(Math.random() * 1000)cart.value.items.push({name: `商品${randomId}`,price: Math.floor(Math.random() * 500)})
}// 删除商品
function removeItem(index) {cart.value.items.splice(index, 1)
}
</script>

ref 与 reactive 关键对比

特性reactive 实现ref 实现
创建方式const cart = reactive({...})const cart = ref({...})
数据访问直接访问属性:cart.items需要通过 .value 访问:cart.value.items
计算属性可以定义在对象内部作为 getter需要单独定义计算属性
模板使用直接使用对象属性:{{ cart.total }}直接使用 ref 对象(自动解包):{{ cart.items }}
修改数据直接修改属性:cart.items.push()需要通过 .value 修改:cart.value.items.push()
类型支持对复杂对象类型推断更友好对基本类型更友好,对象类型需要额外处理
适用场景适合管理复杂的、嵌套的对象状态适合管理单个值或需要替换整个对象的情况

关键差异详解

1. 数据访问方式不同
// 创建
const cart = reactive({ items: [...] })// 访问
cart.items.push(item) // 直接访问

ref:​

// 创建
const cart = ref({ items: [...] })// 访问
cart.value.items.push(item) // 需要通过 .value
2. 计算属性的处理

reactive 可以在对象内部定义:​

const cart = reactive({items: [...],get total() {return this.items.reduce(...)}
})

ref 需要单独定义:​

const cart = ref({ items: [...] })
const total = computed(() => cart.value.items.reduce(...))
3. 模板中的使用差异

虽然模板中都可以直接使用,但原理不同:

  • reactive 对象的属性直接暴露
  • ref 在模板中会自动解包,不需要写 .value

4. 整体替换的区别

reactive 的限制:​

const state = reactive({ count: 0 })
state = { count: 1 } // ❌ 会失去响应式

ref 的优势:​

const state = ref({ count: 0 })
state.value = { count: 1 } // ✅ 可以整体替换

这里扩展一下,在settings里面找到Extension(扩展)里面可以设置自动带上.value

 

更新reactive不可以整体改变,可以借助assign,但是ref可以直接在function里面更新(当然要带上value)。


补充:ToRef和ToRefs

直接解构 vs toRefs 解构对比

1. 直接解构(失去响应式)​

let { name, gender } = person

特点​:

  • 失去响应式​:解构出来的 name 和 gender 是普通变量,​不再是响应式数据
  • 修改无效​:即使修改 name 或 gender,​不会触发 Vue 的更新机制,界面不会自动刷新。
  • 不影响原对象​:修改解构后的变量不会影响​ person 的原始数据。

示例​:

let { name, gender } = person
name = "李四"  // 修改无效,不会更新界面,也不会影响 person.name
console.log(person.name) // 仍然是 "张三"

2. toRefs 解构(保持响应式)​
let { name, gender } = toRefs(person)

特点​:

  • 保持响应式​:解构出来的 name 和 gender 是 ref 对象,​仍然是响应式数据
  • 修改有效​:修改 name.value 或 gender.value,​会触发 Vue 的更新机制,界面会自动刷新。
  • 双向绑定​:修改解构后的变量会影响​ person 的原始数据,反之亦然。

示例​:

let { name, gender } = toRefs(person)
name.value = "李四"  // 修改有效,界面会更新,person.name 也会变成 "李四"
console.log(person.name) // "李四"

对比总结

特性​直接解构 let {name} = persontoRefs 解构 let {name} = toRefs(person)
响应式❌ 失去响应式✅ 保持响应式
修改方式name = "新值"(普通赋值)name.value = "新值"ref 语法)
是否影响原对象❌ 不影响✅ 双向绑定,影响原对象
适用场景仅需读取数据,不修改需要修改数据并保持响应式

toRefs 的使用

  1. 批量解构响应式属性

    let {name, gender} = toRefs(person)
    • 从 reactive 对象 person 中批量解构出 name 和 gender 属性
    • 解构后的变量仍然保持响应式
    • 每个解构出来的属性都是一个 ref 对象,需要通过 .value 访问/修改
  2. 使用场景

    • 当需要从 reactive 对象中解构多个属性时
    • 保持解构后的变量仍然是响应式的

toRef 的使用

  1. 单个属性解构

    let age = toRef(person, 'age')
    • 从 reactive 对象 person 中单独解构出 age 属性
    • 解构后的变量仍然保持响应式
    • 需要通过 .value 访问/修改
  2. 使用场景

    • 当只需要从 reactive 对象中解构单个属性时
    • 比 toRefs 更精确地控制要解构的属性

重要注意事项

  1. value 访问

    name.value += '~'  // 正确
    name += '~'        // 错误,会失去响应式
  2. 与直接解构的区别

    // 错误方式 - 会失去响应式
    let {name} = person// 正确方式 - 保持响应式
    let {name} = toRefs(person)
  3. 模板中使用

    • 在模板中可以直接使用,不需要 .value
    <h2>姓名:{{name}}</h2>  <!-- 正确,自动解包 -->
  4. 修改原始对象

    • 通过 toRef/toRefs 解构的属性和原对象属性是双向绑定的
    • 修改解构后的变量会影响原对象,反之亦然

总结对比

函数作用适用场景
toRef为 reactive 对象的单个属性创建 ref只需要解构单个属性时
toRefs为 reactive 对象的所有属性创建 ref需要解构多个或所有属性时


http://www.mrgr.cn/news/99300.html

相关文章:

  • Linux学习笔记|入门指令
  • Linux:权限相关问题
  • Linux 入门十一:Linux 网络编程
  • 辛格迪客户案例 | 上海科济药业细胞治疗生产及追溯项目(CGT)
  • 3200温控板电路解析
  • 推荐系统/业务,相关知识/概念1
  • 【Maven】项目管理工具
  • 诱骗协议芯片支持PD2.0/3.0/3.1/PPS协议,支持使用一个Type-C与电脑传输数据和快充取电功能
  • 无需花钱购买域名服务器!使用 VuePress + Github 30分钟搭建属于自己的博客网站(保姆级教程)
  • 在Ubuntu 18.04下编译OpenJDK 11
  • Emacs入门篇2:安装evil插件以便vi老用户可以使用VI快捷键来快速使用Emacs
  • 【go】什么是Go语言中的GC,作用是什么?调优,sync.Pool优化,逃逸分析演示
  • Java虚拟机之GC收集器对比解读
  • Linux学习之守护进程1
  • 【springsecurity oauth2授权中心】简单案例跑通流程 P1
  • 音视频小白系统入门课-2
  • NestJS-Knife4j
  • 9.策略模式:思考与解读
  • HTTP/1.1 队头堵塞
  • [架构之美]一键服务管理大师:Ubuntu智能服务停止与清理脚本深度解析