Vue3核心语法基础

一、为什么要学 Composition API?

在以前我们写代码用Vue2写:

export default {data() {return { count: 0, msg: 'hello' }},methods: {add() { this.count++ }},computed: {double() { return this.count * 2 }}
}

很明显 

一个功能被拆成三块:datamethodscomputed,找起来麻烦,改起来更麻烦。

Vue3 提出了 Composition API,把同一功能的代码用函数包在一起,就像把袜子卷成球——整整齐齐不丢单只!

二、舞台:setup 函数

1. setup 是什么?

  • 舞台:所有 Composition API 都要在 setup() 里表演。

  • 时机:组件创建前执行,比任何生命周期都早。

  • 注意:里面访问 this 会得到 undefined(因为组件实例还没出生呢)。

<template><p>{{ name }}</p><button @click="changeName">改名</button>
</template><script>
export default {setup() {let name = '张三'function changeName() {name = '李四' // 页面不会变!因为 name 不是响应式console.log(name)}return { name, changeName } // 暴露给模板用}
}
</script>

我说白了:setup 是 Vue 3 中一个新概念,它就像是组件的"控制中心"或"大脑"。让我用大白话给你解释: 

想象一下你要组装一个玩具:

  • setup 就像是打开工具箱的步骤

  • 在这个工具箱里,你准备好所有零件(数据、方法、计算属性等)

  • 然后你告诉 Vue:"这些就是我要在组件里使用的所有东西"

三、让数据活起来:ref 与 reactive

让数据活起来是啥意思呢?

让我们来试试:

let A = 1
import { ref } from 'vue'let B = ref(1)

这两有什么区别呢?

第一个是一个"死"数据,是非响应式的数据

第二个是可以发生更改的响应式数据

1. ref:基本类型的响应式法宝

  • 语法let xxx = ref(初始值)

  • 规矩:在 JS 里改要加 .value,模板里直接用。

<script setup>
import { ref } from 'vue'let age = ref(18)function addAge() {age.value++ // JS 里要加 .value
}
</script><template><p>年龄:{{ age }}</p> <!-- 模板里不用 .value --><button @click="addAge">+1</button>
</template>

2. reactive:对象类型的响应式法宝

  • 语法let obj = reactive({...})

  • 深度响应:对象里随便嵌套多少层,都能追踪变化。

    <script setup>
    import { reactive } from 'vue'let car = reactive({ brand: '奔驰', price: 100 })function addPrice() {car.price += 10
    }
    </script><template><p>我的{{ car.brand }}价值{{ car.price }}万</p><button @click="addPrice">再涨10万</button>
    </template>

    3. ref vs reactive 速记表

    场景推荐原因
    基本类型refreactive 会报警告
    简单对象均可看个人喜好
    深层嵌套对象reactive写起来更简洁,无需层层.value

    四、语法糖 <script setup>:懒人必备

    每次写 setup() { return {...} } 很烦?Vue3 提供语法糖:

<script setup>
// 这里面的代码直接就是 setup 的内容,无需 return
import { ref } from 'vue'
const count = ref(0)
</script>

(还能更懒)再装个插件 vite-plugin-vue-setup-extend,还能直接写组件名:

<script setup name="UserCard">
// 组件名就叫 UserCard,省去写 name 属性
</script>

五、解构不丢响应式:toRefs & toRef

当你想从 reactive 对象中“摘”属性出来用时,直接解构会丢失响应式,就像把耳机线从卷线盒里抽出来——全乱了!

toRefs / toRef 解决:

哈这个时候就会有同学说,toRef与toRefs的区别是什么呢?,其实最大的区别就是有没有s

toRef 的作用

  • 功能:将响应式对象(reactive 创建的对象)中的单个属性转换为一个响应式的 ref 对象。

toRefs 的作用

  • 功能:将响应式对象(reactive 创建的对象)的所有属性批量转换为 ref 对象,并包装成一个普通对象(每个属性都是对应的 ref)。
import { reactive, toRefs } from 'vue'const user = reactive({name: 'Alice',age: 20
})// 将 user 的所有属性转换为 ref 并包装成普通对象
const refs = toRefs(user)
// refs 结构:{ name: Ref, age: Ref }// 解构后仍保持响应性
const { name, age } = refs// 修改 ref 会同步影响原对象
name.value = 'Bob'
console.log(user.name) // 输出:Bob// 修改原对象也会同步影响 ref
user.age = 21
console.log(age.value) // 输出:21

         好啦,到这里已经将最基础的几个用法,和框架已经搭好了,所以也来用两个练习来巩固一下下

六、实战小案例:待办事项

需求:添加、删除、标记完成,并统计完成数量。

<script setup>
import { ref, reactive, computed } from 'vue'// 1. 数据
const todos = reactive([])
const input = ref('')// 2. 计算属性
const doneCount = computed(() => todos.filter(t => t.done).length)// 3. 方法
function addTodo() {if (!input.value.trim()) returntodos.push({ id: Date.now(), text: input.value, done: false })input.value = ''
}function toggle(todo) {todo.done = !todo.done
}function delTodo(id) {const index = todos.findIndex(t => t.id === id)todos.splice(index, 1)
}
</script><template><h2>待办清单</h2><input v-model="input" @keyup.enter="addTodo" placeholder="输入后敲回车" /><ul><li v-for="t in todos" :key="t.id"><input type="checkbox" v-model="t.done" /><span :class="{ done: t.done }">{{ t.text }}</span><button @click="delTodo(t.id)">删除</button></li></ul><p>已完成:{{ doneCount }} / {{ todos.length }}</p>
</template><style>
.done { text-decoration: line-through; color: gray; }
</style>

这里面有一个用法没讲,就是computed,它的作用也很简单,就是对数据的自动更新(当数据变化的时候,它这个函数就会触发,让数据发生更新) 

七、课后作业(动手才能真会)

  1. 个人资料卡
    <script setup> 做一个可编辑的“姓名、年龄、邮箱、简介”卡片,要求支持新增、修改、删除。

  2. 购物车 2.0
    在上文购物车案例基础上,增加:

    • 商品数量加减

    • 优惠券打 9 折

    • 按分类筛

因为我们现在是比较基础的,所以我们最好是逻辑清晰,将这些都理解

作业一:

思路:

卡片:很简单,用一个div盒子装着,将里面的元素全部写在盒子里面,再将里面的元素居中,样式肯定就不会差。

可编辑的“姓名、年龄、邮箱、简介”:用input将他们显示出来,但是我们要获取输入框里面的元素,这个时候我们就需要v-model进行双向绑定

新增、修改、删除.:这些毫无疑问肯定是一堆按钮,所以我们需要写点击按钮,里面函数用一些的方法进行这些操作

最后其实需要显示出来的,将原本一开始的元素显示出来,后面增加的元素也需要事实显示出来。

代码(简陋版)

<script setup lang="ts">
// 练习1:创建一个个人资料编辑组件,要求使用setup语法糖,
// 包含姓名、年龄、邮箱、个人简介等字段,实现数据的增删改查功能。
import {reactive} from "vue";
import { ref } from 'vue'interface Profile {id: numbername: stringage: numberemail: stringintro: string
}// 资料列表
const profiles = ref<Profile[]>([])// 表单数据
const form = ref<Profile>({id: Date.now(),name: '',age: 0,email: '',intro: ''
})function text() {console.log(profiles)console.log("=================")console.log(form)
}const isEdit = ref(false)
let editIndex = -1function resetForm() {form.value = {id: Date.now(),// id: Date.now() 表示用当前的时间戳(即自1970年1月1日以来的毫秒数)来生成一个唯一的数字,作为这个新表单的唯一标识。name: '',age: 0,email: '',intro: ''}isEdit.value = falseeditIndex = -1
}// ... 是扩展运算符,用于“展开”对象里的所有属性。
// { ...form.value } 表示“复制 form.value 里的所有属性到一个新对象”。
// 常用于【对象浅拷贝】、【合并属性】等操作。
// 等价于
// const obj = {
//   name: form.value.name,
//   age: form.value.age,
//   // ...其他所有属性
// }
// 但用 ...form.value 写法更简洁、更灵活。// 新增或保存编辑
function handleSubmit() {if (isEdit.value && editIndex !== -1) {profiles.value[editIndex] = { ...form.value }} else {profiles.value.push({ ...form.value })}resetForm()
}// 编辑
function editProfile(idx: number) {const item = profiles.value[idx]form.value = { ...item }isEdit.value = trueeditIndex = idx
}// 删除
function deleteProfile(idx: number) {profiles.value.splice(idx, 1)if (isEdit.value && editIndex === idx) {resetForm()}
}</script><template><button @click="text">检查</button><div><h2>个人资料编辑</h2><form @submit.prevent="handleSubmit"><div><label>姓名:</label><input v-model="form.name" required /></div><div><label>年龄:</label><input v-model.number="form.age" type="number" required min="0" /></div><div><label>邮箱:</label><input v-model="form.email" type="email" required /></div><div><label>个人简介:</label><textarea v-model="form.intro" required></textarea></div><button type="submit">{{ isEdit ? '保存修改' : '添加' }}</button><button v-if="isEdit" type="button" @click="resetForm">取消编辑</button></form><h3>资料列表</h3><table border="1" cellpadding="5"><thead><tr><th>姓名</th><th>年龄</th><th>邮箱</th><th>个人简介</th><th>操作</th></tr></thead><tbody><tr v-for="(profile, idx) in profiles" :key="profile.id">
<!--          <td>{{ key }}</td>--><td>{{ profile.name }}</td><td>{{ profile.age }}</td><td>{{ profile.email }}</td><td>{{ profile.intro }}</td><td><button @click="editProfile(idx)">编辑</button><button @click="deleteProfile(idx)">删除</button></td></tr></tbody></table></div></template><style scoped></style>
作业二:

接下来的购物车的思路:

其实完全就是名片作业的升级版而已,你将名片变成购物车,进行购物车的增加,删除功能

新功能:进行购物车里面商品的价钱的总和,并且进行优惠价的计算

        

<template><div class="shopping-cart"><h2>购物车(进阶版)</h2><div class="category-filter"><span>分类:</span><buttonv-for="category in categories":key="category":class="{ active: selectedCategory === category }"@click="selectCategory(category)">{{ category }}</button></div><table class="cart-table"><thead><tr><th>商品</th><th>单价</th><th>数量</th><th>小计</th><th>操作</th></tr></thead><tbody><tr v-for="item in filteredCartItems" :key="item.id"><td>{{ item.name }} ({{ item.category }})</td><td>¥{{ item.price.toFixed(2) }}</td><td><button @click="decreaseQuantity(item)">-</button>{{ item.quantity }}<button @click="increaseQuantity(item)">+</button></td><td>¥{{ (item.price * item.quantity).toFixed(2) }}</td><td><button @click="removeItem(item)">删除</button></td></tr></tbody></table><div class="coupon-section"><label for="coupon">优惠券:</label><select id="coupon" v-model="selectedCoupon"><option value="">请选择</option><option v-for="coupon in coupons" :key="coupon.id" :value="coupon.code">{{ coupon.name }} ({{ coupon.discount }}% off)</option></select></div><div class="total">总计:¥{{ total.toFixed(2) }}</div></div>
</template><script>
export default {data() {return {cartItems: [{id: 1,name: "苹果",category: "生鲜",price: 6.5,quantity: 3},{id: 2,name: "T恤",category: "服饰",price: 59,quantity: 1},{id: 3,name: "蓝牙耳机",category: "数码",price: 199,quantity: 1}],categories: ["All", "生鲜", "数码", "服饰"],selectedCategory: "All",coupons: [{ id: 1, code: "COUPON1", name: "满减券", discount: 10 },{ id: 2, code: "COUPON2", name: "折扣券", discount: 15 }],selectedCoupon: ""};},computed: {filteredCartItems() {if (this.selectedCategory === "All") {return this.cartItems;}return this.cartItems.filter(item => item.category === this.selectedCategory);},total() {return this.cartItems.reduce((acc, item) => acc + item.price * item.quantity, 0);}},methods: {selectCategory(category) {this.selectedCategory = category;},increaseQuantity(item) {item.quantity++;},decreaseQuantity(item) {if (item.quantity > 1) {item.quantity--;}},removeItem(item) {const index = this.cartItems.indexOf(item);if (index!== -1) {this.cartItems.splice(index, 1);}}}
};
</script><style scoped>
.shopping-cart {font-family: Arial, sans-serif;
}
.category-filter button {margin-right: 5px;padding: 5px 10px;cursor: pointer;
}
.category-filter button.active {background-color: #4CAF50;color: white;
}
.cart-table {border-collapse: collapse;width: 100%;margin: 20px 0;
}
.cart-table th,
.cart-table td {border: 1px solid #ddd;padding: 8px;text-align: left;
}
.cart-table button {padding: 2px 5px;cursor: pointer;
}
.coupon-section {margin: 10px 0;
}
.total {font-weight: bold;font-size: 1.2em;
}
</style>

基础的语法讲到这里就结束拉!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91645.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91645.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FSMC的配置和应用

一、FSMC 简介与工作原理FSMC&#xff08;Flexible Static Memory Controller&#xff09;是 STM32 微控制器中用于与外部静态存储器&#xff08;如 SRAM、PSRAM、NOR Flash、LCD 等&#xff09;进行通信的一个外设模块。1、支持的设备类型&#xff1a;SRAM / PSRAMNOR FlashNA…

Linux I/O 系统调用完整对比分析

Linux I/O 系统调用完整对比分析 1. 概述 Linux 提供了丰富的 I/O 系统调用&#xff0c;每种都有其特定的用途和优势。本文将详细分析这些系统调用的特点、使用场景和性能特征。 2. 系统调用详细对比 2.1 基本读写函数 pread/pwrite #include <unistd.h>// 位置指定…

TiDB集群部署

架构&#xff1a; tidb–3台&#xff0c;pd–3台&#xff0c;tikv–3台 8c16g200g 1x2.2x.2x7.124 1x2.2x.2x7.148 1x2.2x.2x7.87 1x2.2x.2x7.93 1x2.2x.2x7.127 1x2.2x.2x7.104 pd-3台 4c8g100g 1x2.2x.2x7.143 1x2.2x.2x7.132 1x2.2x.2x7.91 1、下载安装包 #注&#xff1a;我…

C#中对于List的多种排序方式

在 C# 中给 List<AI> 排序&#xff0c;只要 明确排序规则&#xff08;比如按某个字段、某几个字段、或外部规则&#xff09;&#xff0c;就能用下面几种常见写法。下面全部基于这个示例类&#xff1a;public class AI {public int country; // 国家编号public int pr…

Spring框架中Bean的生命周期:源码解析与最佳实践

第1章&#xff1a;Spring Bean生命周期概述1.1 什么是Spring Bean生命周期&#xff1f;定义&#xff1a;Spring Bean生命周期是指从Bean的创建、初始化、使用到销毁的完整过程&#xff0c;由Spring容器严格管理 。核心思想是Spring容器通过IoC&#xff08;控制反转&#xff09;…

【51单片机6位数码管密码锁】2022-10-15

缘由六位密码器设计连接LED-嵌入式-CSDN问答 矩阵51单片机密码锁,回复:https://bbs.csdn.net/topics/392713242_智者知已应修善业的博客-CSDN博客 #include "REG52.h" unsigned char code smgduan[]{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x…

‌我的第一个开源项目:跃动的心

还是一个编程初学者时&#xff0c;我怀着激动的心情完成了人生第一个开源项目——一个用HTML5 Canvas制作的动态跳动爱心效果。这个项目虽然简单&#xff0c;却让我深刻体会到了开源分享的快乐和技术创造的魅力。 壹、项目灵感 这个项目的灵感来源于浏览网页时&#xff0c;被各…

技术演进中的开发沉思-53 DELPHI VCL系列:windows的消息(下):TApplication窗体

今天我们梳理下关于TApplication的窗体消息下半部分的内容。前面也说过&#xff0c;在 Delphi 的世界里&#xff0c;TApplication 就像一位经验丰富的总工程师&#xff0c;而主窗体则是它倾注心血打造的核心建筑。如果你第一次在实验室里敲出 Delphi 代码时&#xff0c;屏幕上弹…

cesium FBO(四)自定义相机渲染到Canvas(离屏渲染)

前面几节的例子是将Cesium默认的相机渲染到纹理&#xff08;RTT&#xff09;或Canvas&#xff0c;这片文章讲解如何将自定义的一个camera的画面渲染到Canvas上&#xff0c;有了前面几篇的基础了&#xff0c;也能将自定义的画面渲染纹理、也可以灰度处理&#xff0c;原理是一样的…

双机并联无功环流抑制虚拟阻抗VSG控制【simulink仿真模型实现】

双机并联虚拟同步发电机&#xff08;VSG&#xff09;系统中&#xff0c;因线路阻抗不匹配及参数差异&#xff0c;易引发无功环流。本方案在传统VSG控制基础上&#xff0c;引入自适应虚拟阻抗环节。其核心在于&#xff1a;实时检测两机间无功环流分量&#xff0c;据此动态调节各…

python测试总结

测试题的基础知识点总结 1.循环求和 for循环步长&#xff08;range(2,101,2)&#xff09; while循环条件判断&#xff08;i%20&#xff09; 生成器表达式&#xff08;sum(i for i in range )&#xff09; 所以&#xff1a;sum(range(1,101,2))&#xff08;奇数和&#xff09;和…

识别和分类恶意软件样本的工具YARA

YARA 是一个用于识别和分类恶意软件样本的工具,广泛应用于恶意软件分析、威胁情报、入侵检测等领域。它通过编写规则(YARA Rules)来匹配文件中的特定字符串、十六进制模式、正则表达式等特征。 一、YARA 的基本使用方法 1. 安装 YARA Linux(Ubuntu/Debian) sudo apt-ge…

GaussDB 约束的语法

1 约束的作用约束是作用于数据表中列上的规则&#xff0c;用于限制表中数据的类型。约束的存在保证了数据库中数据的精确性和可靠性。约束有列级和表级之分&#xff0c;列级约束作用于单一的列&#xff0c;而表级约束作用于整张数据表。下面是 GaussDB SQL 中常用的约束。NOT …

SecurityContextHolder 管理安全上下文的核心组件详解

SecurityContextHolder 管理安全上下文的核心组件详解在 Spring Security 中&#xff0c;SecurityContextHolder 是​​安全上下文&#xff08;Security Context&#xff09;的核心存储容器​​&#xff0c;其核心作用是​​在当前线程中保存当前用户的认证信息&#xff08;如用…

c++详解系列(引用指针)

目录 1.什么是引用 2.引用的定义 3.引用的特性 4.引用的使用 4.1引用传参 4.2传引用返回 5.const引用&#xff08;在引用的定义前用const修饰&#xff09; 5.1对于引用 5.2对于指针 6.引用&指针 总结 1.什么是引用 引用就是给变量起别名&#xff0c;一个变量可以…

深度学习loss总结(二)

对于目前深度学习主流任务学习,loss的设置至关重要。下面就不同任务的loss设置进行如下总结: (1)目标检测 2D/3D目标检测中的 Loss(损失函数)是训练模型时优化目标的核心,通常包括位置、类别、尺寸、方向等多个方面。以下是目前 常见的 2D 和 3D 目标检测 Loss 分类与…

【Linux网络】netstat 的 -anptu 各个参数各自表示什么意思?

netstat 是一个网络统计工具&#xff0c;它可以显示网络连接、路由表、接口统计、伪装连接和多播成员资格。在 netstat 命令中&#xff0c;不同的参数可以用来定制输出的内容。 你提到的 -anptu 参数组合各自的功能如下&#xff1a; -a (all): 显示所有活动的连接和监听端口。它…

[硬件电路-115]:模拟电路 - 信号处理电路 - 功能放大器工作分类、工作原理、常见芯片

功能放大器是以特定功能为核心的集成化放大电路&#xff0c;通过将运算放大器与外围电阻、电容等元件集成在单一芯片中&#xff0c;实现标准化、高性能的信号放大功能。其核心优势在于简化设计流程、提升系统稳定性&#xff0c;并针对特定应用场景优化性能参数。以下从定义、分…

双网卡UDP广播通信机制详解

UDP广播通信机制详解 一、通信流程分析 发送阶段 通过Client.Bind(192.168.0.3, 60000)将UDP套接字绑定到指定网卡和端口设置RemoteHost "255.255.255.255"实现全网段广播数据流向&#xff1a;192.168.0.3:60000 → 255.255.255.255:50000 接收阶段 设备响应数据应返…

从遮挡难题到精准测量:激光频率梳技术如何实现深孔 3D 轮廓的 2um 级重复精度?

一、深孔 3D 轮廓测量的遮挡困境深孔结构&#xff08;如航空发动机燃油喷嘴孔、模具冷却孔&#xff09;因孔深大&#xff08;常超 100mm&#xff09;、深径比高&#xff08;&#xff1e;10:1&#xff09;&#xff0c;其 3D 轮廓测量长期受限于光学遮挡难题。传统光学测量技术&a…