title: vue_1 date: 2025-01-28 12:00:00 tags: - 前端 categories: - 前端
vue3
Webpack ~ vite
vue3是基于vite创建的
vite 更快一点
一些准备工作
准备后如图所示
插件
Main.ts
// 引入createApp用于创建应用 import {createApp} from 'vue' // 引入App根组件 import App from './App.vue' createApp(App).mount('#app')
App 是根组件,createApp是盆
npm run dev
APP.vue
<template> <div class="app"> <h1>你好啊!</h1> </div> </template> <script lang="ts"> export default { name:'App' //组件名 } </script> <style> .app { background-color: #ddd; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } </style>
简单效果实现
<template> <div class="person"> <h2>姓名:{% raw %}{ {name}}{% endraw %}</h2> <h2>年龄:{% raw %}{ {age}}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="showTel">查看联系方式</button> </div> </template> <script lang="ts"> export default { name:'Person', data(){ return { name:'张三', age:18, tel:'13888888888' } }, methods:{ // 修改姓名 changeName(){ this.name = 'zhang-san' }, // 修改年龄 changeAge(){ this.age += 1 }, // 展示联系方式 showTel(){ alert(this.tel) } } } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } </style>
这个代码分别是cue组件的三个主要部分,分别对应的模版,脚本和样式
模版部分:也就是trmplate
模版
模版是vue组件的视图部分,定义了组件的html结构
在vue中。模版部分是通过高{% raw %}{ {}}{% endraw %}语法实现数据绑定的,{% raw %}{ {name}}{% endraw %} 会绑定到 data 中的 name 数据。
其中的@click 指令绑定点击事件,点击按钮时执行相应的方法,chageName,changeAge,changeTel
<template> <div class="person"> <h2>姓名:{% raw %}{ {name}}{% endraw %}</h2> <h2>年龄:{% raw %}{ {age}}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="showTel">查看联系方式</button> </div> </template>
脚本部分
脚本定义了vue组件的逻辑部分
-
data :组件的状态,即组件中使用的数据
-
Methods:定义组件的方法,这些方法可以在模版中调用来响应用户事件
-
name : 组件的名字,这里是'Person',在vue开发工具中查看到这个组件
<script lang="ts"> export default { name:'Person', data(){ return { name:'张三', age:18, tel:'13888888888' } }, methods:{ // 修改姓名 changeName(){ this.name = 'zhang-san' }, // 修改年龄 changeAge(){ this.age += 1 }, // 展示联系方式 showTel(){ alert(this.tel) } } } </script>
这段代码是一个 Vue 组件 的脚本部分,使用的是 TypeScript 语法。它定义了组件的 数据、方法 和 组件的名称
export default { name: 'Person', 这里就是组件的名称 data() { ... }, methods: { ... } }
export default 是一个 ES6 模块语法,它表示该文件导出的内容是一个对象,并且该对象是默认导出的。这使得 Vue 在加载组件时可以导入这个对象并使用它来渲染组件
样式部分
<style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } </style>
scoped 属性确保这些样式只作用于当前组件,不会影响到其他组件。这是因为 Vue 会为每个组件自动加上独特的属性选择器(如 .person 只作用于当前组件的 .person 元素)。
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>
Export default{}
export default { name: 'App', components: { Person } }
export default 语句使得这个对象成为当前模块的默认导出。这意味着在其他地方使用 import 时,就可以导入这个对象。该对象定义了一个 Vue 组件,组件的名字是 App(通过 name: 'App' 设置)。App 是这个 Vue 组件的名称,它通常是根组件或父组件的名称。
• name 属性指定了当前组件的名字。在 Vue 开发工具中,App 组件将显示为 App,它通常是 Vue 应用的根组件。
• App 组件是父组件,而 Person 组件是它的子组件。
• components 是一个 Vue 组件选项,表示当前组件所使用的子组件。
• 这里通过 components: { Person } 注册了 Person 组件,意味着在 App 组件的模板中可以使用 <Person 标签来引用 Person 组件。
• 由于我们已经通过 import Person from './components/Person.vue' 导入了 Person 组件,所以在 components 中注册它时,只需要直接写 Person 就可以了。
【OptionsAPI 与 CompositionAPI】
Options API 的弊端
Options
类型的 API
,数据、方法、计算属性等,是分散在:data
、methods
、computed
中的,若想新增或者修改一个需求,就需要分别修改:data
、methods
、computed
,不便于维护和复用。
Composition API 的优势
可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。
<template> <div class="person"> <h2>姓名:{% raw %}{ {name}}{% endraw %}</h2> <h2>年龄:{% raw %}{ {age}}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="showTel">查看联系方式</button> </div> </template> <script lang="ts"> export default { name:'Person', beforeCreate(){ console.log('beforeCreate') }, setup(){ console.log(this) //setup中的this是undefined,Vue3在弱化this了 // 数据,原来是写在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} } } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } </style>
这段代码展示了一个vue3组件的setup()函数的使用,并且展示了一些vue3中的特性
Setup与compositionAPI
-
组件名称
export default { name: 'Person', beforeCreate() { console.log('beforeCreate') },
beforeCreate() 是一个 生命周期钩子,它会在 Vue 实例被创建之前调用。这是 Vue 2 和 Vue 3 中都存在的生命周期钩子,但是在 Vue 3 的 Composition API 中,生命周期钩子的使用方式发生了变化,通常会使用 onBeforeMount 等新的钩子函数(在 setup() 函数中)。
-
setup函数
setup() { console.log(this) // setup中的this是undefined,Vue3在弱化this了
• setup() 函数是 Vue 3 引入的 Composition API 的一部分,目的是简化组件的状态和行为的管理。
• 在 setup() 中,this 是 undefined,这与 Vue 2 中的 this(指向当前组件实例)不同。Vue 3 通过 Composition API 进行了优化,弱化了对 this 的依赖,增强了对组合逻辑的关注。此时,你应该通过返回的对象来访问数据和方法,而不是通过 this。
-
定义数据
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) }
changeName()、changeAge() 和 showTel() 是组件的方法。修改 name 和 age 时,你会发现页面没有发生变化。这是因为 name 和 age 是普通的 JavaScript 变量,而不是响应式数据。
如果你希望这些数据能够自动反映到模板中,应该使用 Vue 3 的响应式 API(如 ref() 或 reactive())。
-
返回数据与方法
return { name, age, tel, changeName, changeAge, showTel }
setup与optionAPI&xompositionAPI
-
模版部分
<template> <div class="person"> <h2>姓名:{% raw %}{ {name}}{% endraw %}</h2> <h2>年龄:{% raw %}{ {age}}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="showTel">查看联系方式</button> <hr> <h2>测试1:{% raw %}{ {a}}{% endraw %}</h2> <h2>测试2:{% raw %}{ {c}}{% endraw %}</h2> <h2>测试3:{% raw %}{ {d}}{% endraw %}</h2> <button @click="b">测试</button> </div> </template>
• name、age:显示 data 或 setup 中的数据。
• a、c、d:展示的是通过 data() 中的数据。
• 还有一些按钮和方法,如 changeName、changeAge 和 showTel。
-
脚本部分
beforeCreate(){ console.log('beforeCreate') }
记清楚这个模版
-
data
data(){ return { a: 100, c: this.name, // 这里的 `this.name` 会在 `data` 被调用时是 `undefined`,因为 `name` 在 Vue 3 的 `setup()` 中还没有定义。 d: 900, age: 90 } }
-
methods
methods:{ b(){ console.log('b') } }
定义了一个方法b,他会在点击按钮时输出b
-
setup
setup(){ let name = '张三' let age = 18 let tel = '13888888888' function changeName() { name = 'zhang-san' console.log(name) } function changeAge() { age += 1 console.log(age) } function showTel() { alert(tel) } return {name, age, tel, changeName, changeAge, showTel} }
在setup()中,你定义了name, age,tel,他们是普通的javascript 变量,因此他们不是响应式的。修改的时候页面不会刷新
相比较下,我更偏向于上一个写法
setup语法糖
<template> <div class="person"> <h2>姓名:{% raw %}{ {name}}{% endraw %}</h2> <h2>年龄:{% raw %}{ {age}}{% endraw %}</h2> <h2>地址:{% raw %}{ {address}}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="showTel">查看联系方式</button> </div> </template> <script lang="ts" setup name="Person"> // 数据,原来是写在data中的,此时的name、age、tel都不是响应式的数据 let name = '张三' let age = 18 let tel = '13888888888' let address = '北京昌平区宏福苑·宏福科技园' // 方法 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) } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } </style>
重点是分析脚本
<script lang="ts" setup name="Person"> // 数据,原来是写在data中的,此时的name、age、tel都不是响应式的数据 let name = '张三' let age = 18 let tel = '13888888888' let address = '北京昌平区宏福苑·宏福科技园' // 方法 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) } </script>
<script lang="ts" setup name="Person"> // 数据,原来是写在data中的,此时的name、age、tel都不是响应式的数据
这里发生了改变
1. <script setup:
• setup 是 Vue 3 的 Composition API 的新语法。它简化了组件的声明,并且没有 export default,所有的变量和方法都自动暴露给模板。
• name="Person" 是组件的名称,但是在 <script setup> 中并不需要显式定义,可以直接通过文件名推导。
2. 定义的变量:
• let name = '张三',let age = 18,let tel = '13888888888',let address = '北京昌平区宏福苑·宏福科技园':这些变量是普通的 JavaScript 变量,它们 不是响应式的。如果你修改这些变量,视图不会自动更新。
3. 修改数据的函数:
• changeName 和 changeAge 中直接修改了 name 和 age,但是由于没有使用 Vue 的响应式 API,页面不会随着这些变量的变化而自动重新渲染。
• 这是一个问题,因为 Vue 3 中的响应式系统(通过 ref 或 reactive)才能让数据变化时自动更新视图。
ref创建
哪一个是响应式的,就改变哪一个
<template> <div class="person"> <h2>姓名:{% raw %}{ {name}}{% endraw %}</h2> <h2>年龄:{% raw %}{ {age}}{% endraw %}</h2> <h2>地址:{% raw %}{ {address}}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="showTel">查看联系方式</button> </div> </template> <script lang="ts" setup name="Person"> import {ref} from 'vue' // 数据,原来是写在data中的,此时的name、age、tel都不是响应式的数据 let name = ref('张三') let age = ref(18) let tel = '13888888888' let address = '北京昌平区宏福苑·宏福科技园' // 方法 function changeName() { name.value = 'zhang-san' // JS中操作ref对象时候需要.value console.log(name.value) } function changeAge() { age.value += 1 console.log(age.value) // JS中操作ref对象时候需要.value } function showTel() { alert(tel) } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } </style>
这段代码已经解决了你之前提到的 响应式数据 问题,通过使用 ref 将 name 和 age 变成了响应式变量,确保它们的变化能够自动更新到视图中。让我们来逐部分解释这个代码:
ref()用于创建单一响应式数据,而reactive()用于创建对象的响应式数据
import { ref } from 'vue'; <script lang="ts" setup name="Person"> // 使用 ref() 创建响应式数据 let name = ref('张三'); let age = ref(18); let tel = ref('13888888888'); let address = ref('北京昌平区宏福苑·宏福科技园'); // 方法 function changeName() { name.value = 'zhang-san'; // 修改时需要通过 `.value` 访问和修改 ref 数据 console.log(name.value); } function changeAge() { age.value += 1; console.log(age.value); } function showTel() { alert(tel.value); } </script>
Reactive-对象类型
<template> <div class="person"> <h2>汽车信息:一辆{% raw %}{ {car.brand}}{% endraw %}车,价值{% raw %}{ {car.price}}{% endraw %}万</h2> <button @click="changePrice">修改汽车的价格</button> <br> <h2>游戏列表:</h2> <ul> <li v-for="g in games" :key="g.id">{% raw %}{ {g.name}}{% endraw %}</li> </ul> <button @click="changeFirstGame">修改第一个游戏的名字</button> <hr> <h2>测试:{% raw %}{ {obj.a.b.c}}{% endraw %}</h2> <button @click="changeObj">测试</button> </div> </template> <script lang="ts" setup name="Person"> import { reactive } from 'vue' // 定义数据类型 type Car = { brand: string; price: number; } type Game = { id: string; name: string; } type Obj = { a: { b: { c: number; } } } // 响应式数据 let car = reactive<Car>({ brand: '奔驰', price: 100 }) let games = reactive<Game[]>([ { id: 'aysdytfsatr01', name: '王者荣耀' }, { id: 'aysdytfsatr02', name: '原神' }, { id: 'aysdytfsatr03', name: '三国志' } ]) let obj = reactive<Obj>({ a: { b: { c: 666 } } }) // 方法 function changePrice() { car.price += 10 console.log(car.price) } function changeFirstGame() { games[0].name = '流星蝴蝶剑' } function changeObj() { obj.a.b.c = 999 } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
何时使用 reactive:
1. 对象和数组的响应式数据:
• reactive 主要用于对象和数组类型的响应式处理。因为它会深度递归地将对象的每一个属性都变成响应式的。
• 如果你想让一个复杂的对象(例如包含嵌套对象或数组的对象)自动反应数据变化并更新视图时,就应该使用 reactive。
2. 对象需要嵌套响应式:
• 如果你有多层嵌套的数据结构,并且想要使所有的嵌套属性都成为响应式的,reactive 是非常合适的。比如,一个对象内部有数组,数组内部又有对象等。
3. 非原始数据类型:
• reactive 适用于对象和数组,但不适用于原始类型(例如:number、string、boolean)。如果你只需要处理一个原始数据类型,可以使用 ref。
4. 复杂的数据结构:
• 当你有一个比较复杂的对象,且其中可能包含许多不同类型的属性,reactive 能够方便地使这些属性变得响应式,避免手动为每个属性都使用 ref 或者其他方法来设置响应式。
<template> <div class="person"> <h2>姓名:{% raw %}{ { person.name }}{% endraw %}</h2> <h2>年龄:{% raw %}{ { person.age }}{% endraw %}</h2> <h2>地址:{% raw %}{ { person.address }}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="showTel">查看联系方式</button> </div> </template> <script lang="ts" setup name="Person"> import { reactive } from 'vue' // 使用 reactive 来定义响应式对象 const person = reactive({ name: '张三', age: 18, tel: '13888888888', address: '北京昌平区宏福苑·宏福科技园' }) // 修改姓名 function changeName() { person.name = 'zhang-san' console.log(person.name) } // 修改年龄 function changeAge() { person.age += 1 console.log(person.age) } // 展示联系方式 function showTel() { alert(person.tel) } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } </style>
<template> <div class="person"> <h2>汽车信息:一辆{% raw %}{ {car.brand}}{% endraw %}车,价值{% raw %}{ {car.price}}{% endraw %}万</h2> <button @click="changeBrand">修改汽车的品牌</button> <button @click="changePrice">修改汽车的价格</button> <button @click="changeCar">修改汽车</button> <hr> <h2>当前求和为:{% raw %}{ {sum}}{% endraw %}</h2> <button @click="changeSum">点我sum+1</button> </div> </template> <script lang="ts" setup name="Person"> import {ref,reactive} from 'vue' // 数据 let car = reactive({brand:'奔驰',price:100}) let sum = ref(0) // 方法 function changeBrand(){ car.brand = '宝马' } function changePrice(){ car.price += 10 } function changeCar(){ // car = {brand:'奥拓',price:1} //这么写页面不更新的 // car = reactive({brand:'奥拓',price:1}) //这么写页面不更新的 // 下面这个写法页面可以更新 Object.assign(car,{brand:'奥拓',price:1}) } function changeSum(){ // sum = ref(9) //这么写页面不更新的 sum.value += 1 } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
// 修改汽车(对象赋值的问题) function changeCar() { // 通过 Object.assign 保持响应式 Object.assign(car, { brand: '奥拓', price: 1 }) }
这里是重点
Torefs && toref
类似于解构
<template> <div class="person"> <h2>姓名:{% raw %}{ {person.name}}{% endraw %}</h2> <h2>年龄:{% raw %}{ {person.age}}{% endraw %},{% raw %}{ {nl}}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> </div> </template> <script lang="ts" setup name="Person"> import {reactive,toRefs,toRef} from 'vue' // 数据 let person = reactive({ name:'张三', age:18 }) // 使用toRefs从person这个响应式对象中,解构出name、age,且name和age依然是响应式的 // name和age的值是ref类型,其value值指向的是person.name和person.age let {name,age} = toRefs(person) let nl = toRef(person,'age') console.log(nl.value) // 方法 function changeName(){ name.value += '~' console.log(name.value,person.name) } function changeAge(){ age.value += 1 } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
1. reactive 和 toRefs 的使用:
• reactive:用于创建响应式对象。你使用 reactive 来创建 person 对象,该对象包含 name 和 age。
• toRefs:将 reactive 对象的属性解构出来,变成单独的响应式 ref。你通过 toRefs(person) 将 person 对象中的 name 和 age 分别解构为 ref 类型的响应式数据。这样,name 和 age 的每个属性都保持响应式。**
• toRef:用来将对象的特定属性转换为 ref。你通过 toRef(person, 'age') 将 person 对象的 age 属性变成一个 ref 类型。
2. changeName 和 changeAge 方法:
• changeName:修改 name 的值,通过 name.value 更新。由于 name 是通过 toRefs 解构出来的,它依然是响应式的,因此当修改 name.value 时,视图会自动更新。
• changeAge:同样修改 age 的值,age 也是一个 ref 类型,通过 age.value 来更新。
computed计算属性
<template> <div class="person"> 姓:<input type="text" v-model="firstName"> <br> 名:<input type="text" v-model="lastName"> <br> <button @click="changeFullName">将全名改为li-si</button> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> </div> </template> <script lang="ts" setup name="Person"> import {ref,computed} from 'vue' let firstName = ref('zhang') let lastName = ref('san') // fullName是一个计算属性,且是只读的 /* let fullName = computed(()=>{ console.log(1) return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value }) */ // fullName是一个计算属性,可读可写 let fullName = computed({ // 当fullName被读取时,get调用 get(){ return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value }, // 当fullName被修改时,set调用,且会收到修改的值 set(val){ const [str1,str2] = val.split('-') firstName.value = str1 lastName.value = str2 } }) function changeFullName(){ fullName.value = 'li-si' } </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
在这里面有一个计算属性,她的作用是基于已有的响应式数据计算新的值,计算属性的返回值会自动缓存,只有当它依赖的数据发生变化时,才会重新计算。
计算属性是只读的,也可以是可读可导的
现在展开对代码进行讲解
-
模版部分
<template> <div class="person"> 姓:<input type="text" v-model="firstName"> <br> 名:<input type="text" v-model="lastName"> <br> <button @click="changeFullName">将全名改为li-si</button> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> 全名:<span>{% raw %}{ {fullName}}{% endraw %}</span> <br> </div> </template>
其中那么多全名是为了对齐进行测试
-
脚本部分
<script lang="ts" setup name="Person"> import {ref,computed} from 'vue' let firstName = ref('zhang') let lastName = ref('san') // fullName是一个计算属性,且是可读可写的 let fullName = computed({ // 当fullName被读取时,get调用 get(){ return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value }, // 当fullName被修改时,set调用,且会收到修改的值 set(val){ const [str1,str2] = val.split('-') firstName.value = str1 lastName.value = str2 } }) function changeFullName(){ fullName.value = 'li-si' } </script>
只读
// fullName是一个计算属性,且是只读的 /* let fullName = computed(()=>{ console.log(1) return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value }) */
可读可写
// fullName是一个计算属性,可读可写 let fullName = computed({ // 当fullName被读取时,get调用 get(){ return firstName.value.slice(0,1).toUpperCase() + firstName.value.slice(1) + '-' + lastName.value }, // 当fullName被修改时,set调用,且会收到修改的值 set(val){ const [str1,str2] = val.split('-') firstName.value = str1 lastName.value = str2 } })
2. computed:
• computed 是 Vue 3 中的 计算属性,它会根据其他响应式数据的变化动态计算一个值,并且当相关数据改变时,计算属性会自动更新。
• fullName 是一个 可读可写的计算属性,通过 get 和 set 方法来控制:
• get():当 fullName 被读取时,它会拼接 firstName 和 lastName,并且确保 firstName 的首字母大写。
• set(val):当 fullName 被修改时,set 会接收到新的值,并将其拆分为 firstName 和 lastName,更新这两个响应式变量。
3. changeFullName 方法:
• 当点击按钮时,会调用 changeFullName 方法,并设置 fullName 的值为 'li-si'。由于 fullName 是计算属性,这会触发 set 方法,从而更新 firstName 和 lastName 的值为 'li' 和 'si'。
watch监视
情况一
<template> <div class="person"> <h1>情况一:监视【ref】定义的【基本类型】数据</h1> <h2>当前求和为:{% raw %}{ {sum}}{% endraw %}</h2> <button @click="changeSum">点我sum+1</button> </div> </template> <script lang="ts" setup name="Person"> import {ref,watch} from 'vue' // 数据 let sum = ref(0) // 方法 function changeSum(){ sum.value += 1 } // 监视,情况一:监视【ref】定义的【基本类型】数据 const stopWatch = watch(sum,(newValue,oldValue)=>{ console.log('sum变化了',newValue,oldValue) if(newValue >= 10){ stopWatch() } }) </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
<script lang="ts" setup name="Person"> import { ref, watch } from 'vue' // 数据 let sum = ref(0) // 方法 function changeSum() { sum.value += 1 } // 监视,情况一:监视【ref】定义的【基本类型】数据 const stopWatch = watch(sum, (newValue, oldValue) => { console.log('sum变化了', newValue, oldValue) if (newValue >= 10) { stopWatch() // 停止监听 } }) </script>
watch的基本语法
watch(source, callback, options)
• source:要监视的数据源(可以是一个响应式对象、ref 等)。
• callback:数据变化时调用的回调函数,它会接收到新值和旧值。
• options:可选参数,允许你配置 watch 的行为。
在你的例子中,watch 监视了 sum 的变化,每当 sum 发生变化时,回调函数就会执行,打印出新旧值。如果新值大于或等于 10,就停止监视。
<template> <div class="person"> <h1>情况二:监视【ref】定义的【对象类型】数据</h1> <h2>姓名:{% raw %}{ { person.name }}{% endraw %}</h2> <h2>年龄:{% raw %}{ { person.age }}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changePerson">修改整个人</button> </div> </template> <script lang="ts" setup name="Person"> import {ref,watch} from 'vue' // 数据 let person = ref({ name:'张三', age:18 }) // 方法 function changeName(){ person.value.name += '~' } function changeAge(){ person.value.age += 1 } function changePerson(){ person.value = {name:'李四',age:90} } /* 监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视 watch的第一个参数是:被监视的数据 watch的第二个参数是:监视的回调 watch的第三个参数是:配置对象(deep、immediate等等.....) */ watch(person,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) },{deep:true}) </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
2. watch 监视对象变化:
• watch 是 Vue 3 提供的 API,用来监听响应式数据(如 ref 或 reactive)的变化。当监视的数据变化时,watch 的回调函数会被触发。
你在代码中通过 watch(person, ...) 来监听 person 对象的变化。watch 会接收到两个参数:
• newValue:person 变化后的新值。
• oldValue:person 变化前的旧值。
• deep: true:由于 watch 默认只监视对象的引用变化,如果你需要监视对象内部属性的变化(即对象内部的数据变化),需要设置 { deep: true },这样 Vue 会递归地监视对象内部的每个属性变化。
• 深度监视:
• deep 是 watch 的一个选项,它使得 Vue 能够监听 对象属性的变化。当对象的嵌套属性发生变化时,watch 的回调也会被触发。
• 如果没有开启深度监视,那么只有对象的引用变化(如直接替换整个对象)才会触发回调。如果只是修改对象的某个属性,默认不会触发回调。
3. person.value = { name: '李四', age: 90 }:
• 在 changePerson 方法中,直接给 person 赋值一个新的对象,这会改变 person 的引用,从而触发 watch 监听的回调。
情况3
<template> <div class="person"> <h1>情况三:监视【reactive】定义的【对象类型】数据</h1> <h2>姓名:{% raw %}{ { person.name }}{% endraw %}</h2> <h2>年龄:{% raw %}{ { person.age }}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changePerson">修改整个人</button> <hr> <h2>测试:{% raw %}{ {obj.a.b.c}}{% endraw %}</h2> <button @click="test">修改obj.a.b.c</button> </div> </template> <script lang="ts" setup name="Person"> import {reactive,watch} from 'vue' // 数据 let person = reactive({ name:'张三', age:18 }) let obj = reactive({ a:{ b:{ c:666 } } }) // 方法 function changeName(){ person.name += '~' } function changeAge(){ person.age += 1 } function changePerson(){ Object.assign(person,{name:'李四',age:80}) } function test(){ obj.a.b.c = 888 } // 监视,情况三:监视【reactive】定义的【对象类型】数据,且默认是开启深度监视的 watch(person,(newValue,oldValue)=>{ console.log('person变化了',newValue,oldValue) }) watch(obj,(newValue,oldValue)=>{ console.log('Obj变化了',newValue,oldValue) }) </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
情况四
<template> <div class="person"> <h1>情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h1> <h2>姓名:{% raw %}{ { person.name }}{% endraw %}</h2> <h2>年龄:{% raw %}{ { person.age }}{% endraw %}</h2> <h2>汽车:{% raw %}{ { person.car.c1 }}{% endraw %}、{% raw %}{ { person.car.c2 }}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changeC1">修改第一台车</button> <button @click="changeC2">修改第二台车</button> <button @click="changeCar">修改整个车</button> </div> </template> <script lang="ts" setup name="Person"> import {reactive,watch} from 'vue' // 数据 let person = reactive({ name:'张三', age:18, car:{ c1:'奔驰', c2:'宝马' } }) // 方法 function changeName(){ person.name += '~' } function changeAge(){ person.age += 1 } function changeC1(){ person.car.c1 = '奥迪' } function changeC2(){ person.car.c2 = '大众' } function changeCar(){ person.car = {c1:'雅迪',c2:'爱玛'} } // 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式 /* watch(()=> person.name,(newValue,oldValue)=>{ console.log('person.name变化了',newValue,oldValue) }) */ // 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数 watch(()=>person.car,(newValue,oldValue)=>{ console.log('person.car变化了',newValue,oldValue) },{deep:true}) </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
watch(() => person.car, (newValue, oldValue) => { console.log('person.car变化了', newValue, oldValue) }, { deep: true })
• deep: true:由于 person.car 是一个对象,默认情况下,watch 只会监听对象的引用变化。如果你想监听对象内部的嵌套属性(如 c1 和 c2)的变化,需要设置 { deep: true },这会开启深度监视。
• 在 深度监视 下,watch 不仅会监视 person.car 的引用变化,还会监视 person.car.c1 和 person.car.c2 的变化。
这是对象类型的写法
情况5-监视多个事件
<template> <div class="person"> <h1>情况五:监视上述的多个数据</h1> <h2>姓名:{% raw %}{ { person.name }}{% endraw %}</h2> <h2>年龄:{% raw %}{ { person.age }}{% endraw %}</h2> <h2>汽车:{% raw %}{ { person.car.c1 }}{% endraw %}、{% raw %}{ { person.car.c2 }}{% endraw %}</h2> <button @click="changeName">修改名字</button> <button @click="changeAge">修改年龄</button> <button @click="changeC1">修改第一台车</button> <button @click="changeC2">修改第二台车</button> <button @click="changeCar">修改整个车</button> </div> </template> <script lang="ts" setup name="Person"> import {reactive,watch} from 'vue' // 数据 let person = reactive({ name:'张三', age:18, car:{ c1:'奔驰', c2:'宝马' } }) // 方法 function changeName(){ person.name += '~' } function changeAge(){ person.age += 1 } function changeC1(){ person.car.c1 = '奥迪' } function changeC2(){ person.car.c2 = '大众' } function changeCar(){ person.car = {c1:'雅迪',c2:'爱玛'} } // 监视,情况五:监视上述的多个数据 watch([()=>person.name,person.car],(newValue,oldValue)=>{ console.log('person.car变化了',newValue,oldValue) },{deep:true}) </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
// 监视,情况五:监视上述的多个数据 watch([()=>person.name,person.car],(newValue,oldValue)=>{ console.log('person.car变化了',newValue,oldValue) },{deep:true})
就是加了个中括号
watch- effect
<template> <div class="person"> <h2>需求:当水温达到60度,或水位达到80cm时,给服务器发请求</h2> <h2>当前水温:{% raw %}{ {temp}}{% endraw %}℃</h2> <h2>当前水位:{% raw %}{ {height}}{% endraw %}cm</h2> <button @click="changeTemp">水温+10</button> <button @click="changeHeight">水位+10</button> </div> </template> <script lang="ts" setup name="Person"> import {ref,watch,watchEffect} from 'vue' // 数据 let temp = ref(10) let height = ref(0) // 方法 function changeTemp(){ temp.value += 10 } function changeHeight(){ height.value += 10 } // 监视 -- watch实现 /* watch([temp,height],(value)=>{ // 从value中获取最新的水温(newTemp)、最新的水位(newHeight) let [newTemp,newHeight] = value // 逻辑 if(newTemp >= 60 || newHeight >= 80){ console.log('给服务器发请求') } }) */ // 监视 -- watchEffect实现 watchEffect(()=>{ if(temp.value >= 60 || height.value >= 80){ console.log('给服务器发请求') } }) </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
详细讲解:
watch 的用法:
• watch 是 Vue 3 中的一个 API,用于监视一个或多个响应式数据。当监视的数据发生变化时,watch 会触发回调函数,并将最新的值传递给回调函数。
• watch([temp, height], (value) => {...}):
• temp 和 height 是你监视的两个数据源。
• 当这两个数据发生变化时,watch 会调用回调函数 (value)。
• value 是一个数组,包含了 temp 和 height 的新值。注意:这两个值的顺序与传入 watch 时的顺序一致,value[0] 是 temp 的新值,value[1] 是 height 的新值。
回调函数:
• value:
• value 是一个数组,包含了所有被监视的数据的新值。在这个例子中,value 是 [newTemp, newHeight]。
• 通过解构赋值 let [newTemp, newHeight] = value,可以直接获取 temp 和 height 的新值。
• 条件判断:
• 如果 newTemp(水温)大于等于 60,或者 newHeight(水位)大于等于 80,就执行 console.log('给服务器发请求')。
• 这段逻辑可以用于监测水温或水位达到某个阈值时,触发某些操作(例如,发送请求给服务器)。
标签属性
<template> <div class="person"> <h1>中国</h1> <h2 ref="title2">北京</h2> <h3>尚硅谷</h3> <button @click="showLog">点我输出h2这个元素</button> </div> </template> <script lang="ts" setup name="Person"> import {ref,defineExpose} from 'vue' // 创建一个title2,用于存储ref标记的内容 let title2 = ref() let a = ref(0) let b = ref(1) let c = ref(2) function showLog(){ console.log(title2.value) } defineExpose({a,b,c}) </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
TS
index.ts
// 定义一个接口,用于限制person对象的具体属性 export interface PersonInter { id:string, name:string, age:number } // 一个自定义类型 // export type Persons = Array<PersonInter> export type Persons = PersonInter[]
interface 定义接口
export interface PersonInter { id: string, name: string, age: number }
interface :接口用于定义对象的结构与约束
在这里:PersonInter是一个接口,约束了一个person对象必须具有的属性
• id:类型为 string,表示唯一标识符。
• name:类型为 string,表示人员的姓名。
• age:类型为 number,表示人员的年龄。
• type:类型别名可以用来给复杂的类型(如数组、联合类型等)取一个简短的名字。
• Persons:类型别名定义为一个 PersonInter 对象数组(即 PersonInter[]),表示一个由多个 person 对象组成的列表。
<script lang="ts" setup name="Person"> import {type PersonInter,type Persons} from '@/types' // let person:PersonInter = {id:'asyud7asfd01',name:'张三',age:60} let personList:Persons = [ {id:'asyud7asfd01',name:'张三',age:60}, {id:'asyud7asfd02',name:'李四',age:18}, {id:'asyud7asfd03',name:'王五',age:5} ] </script>
• import { type ... }:
• type 是 TypeScript 的关键字,用来导入类型信息。它能明确表明这是一个类型导入,而不是普通值。
• 从 @/types 文件中导入了两个类型:PersonInter 和 Persons。
• @/types:
• 表示一个类型定义文件的路径。一般情况下,这个文件夹是项目中专门用来存放全局类型定义的。
• 在这个 types 文件中,定义了以下内容(假设内容如下):
• let personList: Persons:
• 通过 :Persons 明确指定了 personList 的类型是 Persons。
• Persons 的定义是 PersonInter[],即一个数组,数组的每个元素都必须符合 PersonInter 接口的结构。
• personList 的用途:
• personList 是一个数组,其中存储了多个 person 对象。
• 每个对象必须具有 id(字符串)、name(字符串)和 age(数字)属性,否则 TypeScript 会抛出类型错误。
• 数据校验示例:
• 如果你尝试添加一个不符合 PersonInter 结构的对象,比如:
let personList: Persons = [ { id: 'asyud7asfd04', name: '赵六' } // 缺少 age 属性 ]
props的使用
<template> <div class="person"> <ul> <li v-for="p in list" :key="p.id"> {% raw %}{ {p.name}}{% endraw %} -- {% raw %}{ {p.age}}{% endraw %} </li> </ul> </div> </template> <script lang="ts" setup name="Person"> import {withDefaults} from 'vue' import {type Persons} from '@/types' // 只接收list // defineProps(['list']) // 接收list + 限制类型 // defineProps<{list:Persons}>() // 接收list + 限制类型 + 限制必要性 + 指定默认值 withDefaults(defineProps<{list?:Persons}>(),{ list:()=> [{id:'ausydgyu01',name:'康师傅·王麻子·特仑苏',age:19}] }) // 接收list,同时将props保存起来 /* let x = defineProps(['list']) console.log(x.list) */ </script> <style scoped> .person { background-color: skyblue; box-shadow: 0 0 10px; border-radius: 10px; padding: 20px; } button { margin: 0 5px; } li { font-size: 20px; } </style>
拆分代码:
-
模版部分
<template> <div class="person"> <ul> <li v-for="p in list" :key="p.id"> {% raw %}{ {p.name}}{% endraw %} -- {% raw %}{ {p.age}}{% endraw %} </li> </ul> </div> </template>
这里面含有v-for循环,那么这个是什么?
-
遍历list数据(通过props传递到组件中)
-
每个p是list数组中的一项
-
:key="p.id" 为每个列表项绑定唯一的 key,提高渲染性能。
• {% raw %}{ {p.name}}{% endraw %} 和 {% raw %}{ {p.age}}{% endraw %} 动态展示每个对象的 name 和 age 属性。
-
脚本部分
-
导入工具与类型
import { withDefaults } from 'vue' import { type Persons } from '@/types'
• withDefaults:
• 用于为 defineProps 定义的 props 设置默认值。它接收两个参数:
• defineProps 的返回值(包含 props 的类型约束)。
• 一个对象,用来指定每个 prop 的默认值。
-
那么对应的index.ts
// 定义一个接口,用于限制person对象的具体属性 export interface PersonInter { id:string, name:string, age:number, } // 一个自定义类型 // export type Persons = Array<PersonInter> export type Persons = PersonInter[]
-