创建Vue项目
在这里我们使用vite来创建
## 1.创建命令
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
完成后,你可以获得如下的文件
在控制台中输入npm run dev
来运行项目
开始编写Vue代码
我们首先删除已经生成src目录、App.vue、mian.ts并且重新创建src目录与App.vue、mian.ts文件,接下来我们将逐个讲解并完成这几个重要文件
src目录
这个目录存放着我们的前端代码。我们首先在目录中创建components
文件夹,这个文件夹将用来存放Vue组件。
在components
文件夹创建和一个组件名为Person.vue
,并写入如下代码
<script setup lang="ts">
import {ref} from "vue";
let car =ref({brand:'xiaomi',price:500})
let games=ref([
{id:'01',name:'csgo'},
{id:'02',name:'lol'},
{id:'03',name:'cod'}
])
console.log(car);
function changePrice(){
car.value.price = 1000;
}
function changeFirstgame(){
games.value[0].name = 'pubg';
}
</script>
<template>
<div class="person">
<h2>一辆{{car.brand}}价值{{car.price}}万</h2>
<button @click="changePrice">修改汽车价格</button>
<br>
<h2>游戏列表</h2>
<ul>
<li v-for="g in games" :key="g.id">{{g.name}}</li>
</ul>
<button @click="changeFirstgame"> 修改第一个游戏的名字</button>
</div>
</template>
<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> <template> <style>三个标签,分别用于写入ts代码,html代码,与样式代码。
让我们逐行来理解代码,首先是<script setup lang="ts">
,setup意味着使用了setup语法糖,lang="ts"
则意味着接下来的代码使用ts作为编程语言。下文我们将会详细解释setup语法糖。
import {ref} from "vue"
意为从vue中引入ref组件来实现响应式数据。
let car =ref({brand:'xiaomi',price:500})
let games=ref([
{id:'01',name:'csgo'},
{id:'02',name:'lol'},
{id:'03',name:'cod'}
])
声明了一个对象数据类型和map数据类型,并使用ref来包裹,意味着对应的变量为响应式的。
function changePrice(){
car.value.price = 1000;
}
function changeFirstgame(){
games.value[0].name = 'pubg';
}
声明了两个函数,请注意,响应式数据需要使用.value来访问到其中的数据。下文我们会详细解释ref。
App.vue
App.vue是vue程序的根组件。如果我们把整个前端项目看做一个树,那么上文提到的Person.vue与其他类似的组件则为树的枝叶,App.vue则是整个大树的根,组件在App.vue中绑定才能正常展现出来。向App.vue中写入如下的内容。
<script lang="ts">
import Person from "./components/Person.vue";
export default {
name:'App',
components:{
Person
},
}
</script>
<template>
<div class="app">
<Person />
</div>
</template>
<style scoped>
</style>
import Person from "./components/Person.vue";
引入了我们之前所写的组件。 其中Person则为起的别名,在下面引入所需
export default {
name:'App',
components:{
Person
},
}
固定格式,在components中写入之前所声明的组件别名。
<template>
<div class="app">
<Person />
</div>
</template>
将组件以标签的形式引入
main.ts
main.ts是应用的入口文件,它负责初始化Vue应用并将其挂载到DOM上。
写入以下内容
import {createApp} from "vue"
import App from "./App.vue";
const app = createApp(App)
app.mount('#app')
import {createApp} from "vue"
这一行从Vue模块中导入`createApp`函数。此函数用于创建一个新的Vue应用程序。
import App from "./App.vue"
从名为"App.vue"的文件中导入名为`App`的组件。
const app = createApp(App)
这一行使用`createApp`函数创建一个新的Vue应用程序,并传递之前导入的`App`组件。
app.mount('#app')
Vue应用程序挂载到具有id为'app'的HTML元素上。这意味着Vue应用程序将在具有id'app'的HTML元素内呈现其内容。
setup语法糖
使用 setup
语法糖,可以直接在 script
标签中使用 setup
属性,而无需显式地定义 setup
函数。所有在 script setup
中定义的变量和函数都会自动成为组件实例的一部分。他有以下几个特点
setup
函数返回的对象中的内容,可直接在模板中使用setup
函数会在beforeCreate
之前调用,它是“领先”所有钩子执行的。script setup
只能在script
标签中使用,并且需要添加setup
属性。不支持与
setup
函数同时使用。
使用ref来创建响应式数据
ref用于创建响应式数据,它能够使得展现在页面上的变量能够根据操作发生变化。你可以使用let xxx = ref(初始值)
来创建ref变量,他将返回一个RefImpl
的实例对象,简称ref对象
或ref
,ref
对象的value
属性是响应式的。
你需要注意以下几点
操作数据需要:
xxx.value
,但模板中不需要.value
,直接使用即可。对于
let name = ref('张三')
来说,name
不是响应式的,name.value
是响应式的。
computed计算属性
computed计算属性
在Vue 3中,computed
是一种特殊的响应式数据,它可以根据其他响应式数据的变化而动态计算出新的值。
以下是一个例子
<template>
<div class="person">
姓:<input type="text" v-model="firstName"> <br>
名:<input type="text" v-model="lastName"> <br>
全名:<span>{{fullName}}</span> <br>
<button @click="changeFullName">全名改为:li-si</button>
</div>
</template>
<script setup lang="ts" name="App">
import {ref,computed} from 'vue'
let firstName = ref('zhang')
let lastName = ref('san')
// 计算属性——只读取,不修改
/* let fullName = computed(()=>{
return firstName.value + '-' + lastName.value
}) */
// 计算属性——既读取又修改
let fullName = computed({
// 读取
get(){
return firstName.value + '-' + lastName.value
},
// 修改
set(val){
console.log('有人修改了fullName',val)
firstName.value = val.split('-')[0]
lastName.value = val.split('-')[1]
}
})
function changeFullName(){
fullName.value = 'li-si'
}
</script>
本质上,我们通过使用计算属性来简化了原本要写在模板中的拼接字符串并改为大写的方法。
在当我们使用既能读取又能修改的计算属性的时候,需要在get函数中写入调用此计算属性时的返回办法,在set函数中写入计算逻辑
Watch监视
watch能够监视数据的变化,它能够监视以下四类数据
ref
定义的数据。reactive
定义的数据。函数返回一个值(
getter
函数)。一个包含上述内容的数组。
监视ref定义的数据
<script setup lang="ts">
import {watch, ref} from 'vue'
let sum = ref(0)
function changeSum() {
sum.value++
}
watch(sum,(newValue,oldValue)=>{
console.log('sum变了', newValue,oldValue)
})
</script>
<template>
<div class="person">
<h2>情况一:监视ref</h2>
<h3>当前的和为:{{ sum }}</h3>
<button @click="changeSum">点我sum加一</button>
</div>
</template>
watch
本质上是一个函数,他在这个例子中接受两个参数,一个是要监视的变量,一个是回调函数,表示监视时要进行的动作。特殊的,毁掉函数中可以包含两个特殊已定义变量:newValue
与oldValue
,我们可以通过使用这两个变量来捕获变化前后的数据状态
监视ref定义的对象类型数据
<script setup lang="ts">
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(person,(newValue,oldValue)=>{
console.log("person改变了",'newValue:',newValue,'oldValue:',oldValue)
},{deep:true})
</script>
<template>
<div class="person">
<h1>情况二:监视【ref】定义的【对象类型】数据</h1>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changePerson">修改整个人</button>
</div>
</template>
注意:
若修改的是
ref
定义的对象中的属性,newValue
和oldValue
都是新值,因为它们是同一个对象。若修改整个
ref
定义的对象,newValue
是新值,oldValue
是旧值,因为不是同一个对象了。
监视reactive定义的对象类型数据
<script setup lang="ts">
import {reactive,watch} from 'vue'
// 数据
let person = reactive({
name:'张三',
age:18
})
// 方法
function changeName(){
person.name += '~'
}
function changeAge(){
person.age += 1
}
function changePerson(){
Object.assign(person,{name:'李四',age:20})
}
watch(person ,(newValue,oldValue)=>{
console.log('person',newValue,oldValue)
})
</script>
<template>
<div class="person">
<h1>情况三:监视【reactive】定义的【对象类型】数据</h1>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changePerson">修改整个人</button>
</div>
</template>
无需开启深度监视
监视ref或reactive定义的【对象类型】数据中的某个属性
在这里我们有着一个最佳实践,那就是无论属性是基本类型还是对象类型,我们都将其包裹在函数中,这样无论是对象的改变,对象属性内的属性值的改变都可以被监视
<template>
<div class="person">
<h1>情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h1>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</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>
共同监视上述所有类型
<template>
<div class="person">
<h1>情况五:监视上述的多个数据</h1>
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</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>
watchEffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数
watch
对比watchEffect
都能监听响应式数据的变化,不同的是监听数据变化的方式不同
watch
:要明确指出监视的数据
watchEffect
:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)。
<script setup lang="ts">
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([temp,height],(value)=>{
// let [tempValue,heightValue]=value
// if (tempValue>=60||heightValue>=80){
// alert('水温或水位过高')
// }
// })
watchEffect(()=>{
if(temp.value>=60||height.value>=80){
alert('水温或水位过高')
}
})
</script>
<template>
<div class="person">
<h2>当前水的温度为{{ temp}}</h2>
<h2>当前水位为{{ height }}</h2>
<button @click="changeTemp">水温加一</button>
<button @click="changeHeight">水位加一</button>
</div>
</template>