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

vue3(整合版)

创建第一个vue项目

1.安装node.js

cmd输入node查看是否安装成功

2.vscode开启一个终端,配置淘宝镜像

# 修改为淘宝镜像源
npm config set registry https://registry.npmmirror.com

输入如下命令创建第一个Vue项目

3.下载依赖,启动项目

访问5173端口

第一个Vue项目的目录结构

我们先打开main.ts,这是我们vue工程的入口文件

在这里创建vue程序,并把它挂载到id为app的标签下(ps:这里的App.vue是我们的根组件)

再打开index.html

这里将main.js引入,并挂载了app程序到这里。

最后打开App.vue,也就是我们的根组件

先不看其他编码的细节,我们可以大胆猜测HelloWorld.vue被挂到App.vue里面,证实了它是根组件。

简单验证

Hello.vue

<template><h1>{{ msg }}</h1>
</template><script lang="ts" setup>let msg="Hello"
</script><style></style>

App.vue

<template>
<Hello></Hello>
<h1>{{msg}}</h1>
<Hello></Hello>
</template><script lang="ts" setup>import Hello from './components/Hello.vue';//引入Hello.vuelet msg="App"
</script><style>
</style>

setup概述

setup语法糖

setup函数有一个语法糖,可以让我们把setup函数从script里面独立出去

<script setup lang="ts">console.log(this) //undefined// 数据(注意:此时的name、age、tel都不是响应式数据)let name = '张三'let age = 18let tel = '13888888888'// 方法function changName(){name = '李四'//注意:此时这么修改name页面是不变化的}function changAge(){console.log(age)age += 1 //注意:此时这么修改age页面是不变化的}function showTel(){alert(tel)}
</script>

Vue的生命周期

setup函数会在beforeCreate函数前调用

响应式数据

基本类型的响应式数据

只能用ref

<template>
<!--     alt+shift+a 注释 --><h1>{{ msg }}</h1><!-- 在template中,不用age.value 会自动帮我们 .value --><h1>年龄{{ age }}</h1><button @click="f1">点我年龄+1</button>
</template><script lang="ts" setup name="Hello">import { ref } from 'vue';let msg="Hello"let age=ref(18)//用ref包裹,让他成为响应式数据function f1(){age.value+=1 //在script中,需要age.value才能得到它的值}
</script>

在js中操作数据需要 .value 在模板中不需要

对象类型的响应式数据

可用reactive,也可用ref

reactive

//对象类型的直接用reactive包起来即可let s1=reactive({id:"1",name:"张三",age:"28",sex:"男",classroom:"3"})//取值不需要 .valuefunction changeS1Name(){s1.name="张十三"}

ref

仍需要 .value

由下图可知,ref实现响应式对象的底层还是使用了reactive

ref和reactive的区别

<template><ul><li>品牌:{{ car1.brand }}</li><li>价格:{{ car1.price }}</li></ul><button @click="f1">ref改变</button><br><ul><li>品牌:{{ car2.brand }}</li><li>价格:{{ car2.price }}</li></ul><button  @click="f2">reactive改变1</button><button  @click="f3">reactive改变2</button>
</template><script  lang="ts" setup>import { ref,reactive } from 'vue';//选中shift+ (let car1=ref({brand:"雪佛兰",price:666})let car2=reactive({brand:"奔驰",price:123})function f1(){car1.value={brand:"宝马",price:987}}//可以function f2(){car2={brand:"宝马",price:987}
/*        car2.brand="wda" */ }//失效  无法获得Car的响应式对象,要改整体只能成员属性一个个地改function f3(){Object.assign(car2,{brand:"宝马",price:987})}//这样可以,直接分配一个新对象给car2</script>

解构响应式对象

如果我们要处理一个有很多成员的响应式对象,且我们只希望对其中几个成员进行修改,那么就可以对其进行解构

<template>
姓名<h1>{{name }}</h1>
<br>
年龄<h1>{{ age }}</h1>
<br>
性别<h1>{{ sex }}</h1>
其他我们不想实时修改的属性<h1>{{ person.others }}</h1><button @click="changeMsg">修改</button>
</template><script lang="ts" setup>import { reactive,toRefs,toRef} from 'vue';let person=reactive({name:"张胜男",age:22,sex:"女",others:"其他属性..."})//将解构出来的成员编程ref响应式对象let {name,age}=toRefs(person)//解构多个let sex=toRef(person,"sex")//解构单个function changeMsg(){//因为解构出来的是ref对象,所以要 .valuename.value="李四";age.value=99;sex.value="男"//此时页面可以变化console.log(name,age,sex);}/*     let {name,age}=personlet sex=person.sexfunction changeMsg(){name="李四";age=99;sex="男"//    李四,99,男//    但是页面上的姓名和年龄并没有改变//    这是因为解构出来的成员并不是响应式的console.log(name,age,sex);} */</script>

计算属性

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。

它的格式如下:

const 我们要计算的属性=computed(function(){...return  计算结果})

案例

<template>
姓<input v-model="xing">
<br>
名<input v-model="ming">
<br>
姓名<span>{{ name }}</span>
</template><script lang="ts" setup>import { computed,ref } from 'vue';let xing=ref("yang")let ming=ref("wei")let name=computed(function (){return xing.value.slice(0,1).toUpperCase()+xing.value.slice(1)+"-"+ming.value})</script><style>
span{border: 1px solid red;
}
</style>

计算属性优于函数调用的地方在于它有缓存机制,刷新页面时函数会重新计算,计算属性检查调用的变量的值,没有变动的话直接返回上次计算的结果。

此外,计算属性的值是只读的

要对他实现可读可写要在computed函数里面提供get和set方法

<template>
姓<input v-model="xing">
<br>
名<input v-model="ming">
<br>
姓名<span>{{ name }}</span>
<br>
<button @click="changeValue">对计算属性的直接写操作会调用set方法,并且值会给到set方法的形参,但是还是得通过更改调用的变量值才能复写</button>
</template><script lang="ts" setup>import { computed,ref } from 'vue';let xing=ref("yang")let ming=ref("wei")let name=computed({get(){return xing.value.slice(0,1).toUpperCase()+xing.value.slice(1)+"-"+ming.value},set(val){let[str1,str2]=val.split("-")xing.value=str1ming.value=str2}})function changeValue(){name.value="Li-si"}
</script>

对计算属性的直接写操作会调用set方法,并且值会给到set方法的形参,但是还是得通过更改调用的变量值才能复写

Vue常用指令

条件渲染

案例

<!-- 条件渲染-->
<template><span v-if="flag">flag为true时显示这个span</span><br><span v-show="flag">条件为真时渲染</span><br><span v-if="sex==1">🚹</span><span v-else-if="sex==0">🚺</span><span v-else>不男不女</span><button @click="f1">改变sex值</button><br></template><script lang="ts" setup>import { ref } from 'vue';let flag=truelet sex=ref(0)function f1(){if(sex.value==0){sex.value=1return}else{sex.value=0}}
</script>

v-if和v-show的区别,当判定条件为假时,v-show只是不显示它所包裹的内容,会保留它的结构,而v-if时直接删除,不保留

列表渲染

案例

<!-- 列表渲染 -->
<template><table><tr v-for="stu in students" :key="stu.id"><td>{{stu.id}}</td><td>{{stu.name}}</td><td>{{stu.age}}</td><td v-if="stu.sex==0">🚹</td><td v-else>🚺</td><td>{{ stu.classroom }}</td></tr></table><!-- v-for   --><span>**************************</span><div v-for="value in myObject"><span>{{ value }}</span></div><span>**************************</span><div v-for="(stu, key) in students">{{ key }}: {{ stu }}</div><span>**************************</span><div v-for="(value, key, index) in myObject">{{ index }}. {{ key }}: {{ value }}</div></template><script lang="ts" setup>import { reactive } from 'vue';let students=reactive([{id:"2",name:"李四",age:"36",sex:1,classroom:"2"},{id:"3",name:"周天",age:"77",sex:0,classroom:"1"},{id:"4",name:"王五",age:"99",sex:0,classroom:"8"},{id:"5",name:"刘六",age:"10",sex:1,classroom:"5"}])const myObject = reactive({title: 'How to do lists in Vue',author: 'Jane Doe',publishedAt: '2016-04-10'
})

watch

作用:监视数据的变化

牢记watch只能监视如下4种数据:

  1. ref定义的数据。
  2. reactive定义的数据。
  3. 函数返回一个值(getter函数)。
  4. 一个包含上述内容的数组。

因为ref可以定义基本类型和对象类型,所以watch对数据的监视可以分为5种情况

1.watch监视ref定义的基本类型数据

<template><div class="person"><h1>情况一:监视ref定义的基本类型数据</h1><h2>当前求和为:{{sum}}</h2><button @click="changeSum">点我sum+1</button></div></template><script lang="ts" setup >import {ref,watch} from 'vue'// 数据let sum = ref(0)// 方法function changeSum(){sum.value += 1}/*watch参数    要监视的数据     一个回调函数(在被监视的数据的地址值发生改变时被调用) watch返回一个停止监视函数 调用它停止对watch的监视*/const  stop=watch(sum,(newValue,oldValue)=>{console.log("新值:"+newValue+"--"+"旧值"+oldValue)if(newValue>=10){stop() //停止监视}})</script>

重点:回调函数在数据的地址值发生变化时被调用

2.watch监视ref定义的对象类型数据

<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><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>

可以看到,开启深度监视后,name和age字段的改变也会被我们监视到

****************************************

如果watch是这样写的(没有开启深度监视,那么只有修改整个人,回调函数才会被调用)

watch(person,(newValue,oldValue)=>{console.log('person变化了',newValue,oldValue)})

3.watch监视reactive定义的对象类型数据

<template><h2>姓名:{{ student.name }}</h2><h2>年龄:{{ student.age }}</h2><button @click="f1">改变姓名</button><button @click="f2">改变年龄</button><button @click="f3">改变整个人</button>
</template><script setup>import { reactive, watch } from 'vue';let student=reactive({"name":"张三","age":13})function f1(){student.name=student.name+"~"}function f2(){student.age=student.age+1}function f3(){Object.assign(student,{"name":"李四","age":99})}watch(student,(newValue,oldValue)=>{console.log("发生变化",newValue,oldValue)})/*这样是不行的  因为reactive定义的整个对象会失去响应式function f3(){student={"name":"李四","age":99} }*/
</script>

注意看,这里的newValue和oldValue都是一样的

我们首先需要明确的是:

1.回调函数只有被监视数据的地址发生改变时才被调用

2.reactive监视的数据默认开启深度监视,也就是student里面的name和age会被监视到

以改变姓名举例,watch监视到name地址值改变,回调函数被调用,但是我们监视的是student这个整体,它的地址值未曾改变(Object.assign只是覆盖了两个字段的值),因此这时候拿到的newValue和oldValue都是修改后的值。

在实际开发中,我们一般只用到修改后的值,因此不需要太纠结。

watch(person,(value)=>{console.log('我只要修改后的值',value)})

4.watch监视ref或reactive定义的对象类型里面的某个属性

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>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:'爱玛'}}</script>

如果要监视的是对象里面的一个基本数据类型,要用getter方法

// 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式watch(()=> person.name,(newValue,oldValue)=>{console.log('person.name变化了',newValue,oldValue)})

如果监视的是对象里面的一个对象类型,可以用getter方法,也可以不用,如果需要监视这个被监视的对象的里面,还是要开启深度监视

// 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数watch(()=>person.car,(newValue,oldValue)=>{console.log('person.car变化了',newValue,oldValue)},{deep:true})

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>import {ref,watch} from 'vue'// 数据let person = ref({name:'张三',age:18,car:{c1:'奔驰',c2:'宝马'}})// 方法function changeName(){person.value.name+="~"}function changeAge(){person.value.age += 1}function changeC1(){person.value.car.c1 = '奥迪'}function changeC2(){person.value.car.c2 = '大众'}function changeCar(){person.value.car = {c1:'雅迪',c2:'爱玛'}}</script>

如果要监视的是对象里面的一个基本数据类型,要用getter方法

watch(()=>person.value.name,(newValue,oldValue)=>{console.log("name改变了",newValue,oldValue)})

监视对象里面的一个对象类型,开启深度监视

watch(()=>person.value.car,(newValue,oldValue)=>{console.log("car改变了",newValue,oldValue)},{deep:true})

5.监视多个数据

统一用getter方法 然后开深度监视

<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 >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.car,person.age],(newValue,oldValue)=>{console.log("car或age被修改",newValue,oldValue)},{deep:true})</script>

watchEffect

官网:立即运行一个函数(相当于{immediate}),同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。

说的通俗点就是watch的升级版,直接写你的代码逻辑,他会根据你用到的去监听,不需要你自己去告诉它你要监视哪些数据。

<template><div class="person"><h1>需求:水温达到50℃,或水位达到20cm,则联系服务器</h1><h2 id="demo">水温:{{temp}}</h2><h2>水位:{{height}}</h2><button @click="changePrice">水温+10</button><button @click="changeSum">水位+10</button></div></template><script lang="ts" setup>import {ref,watch,watchEffect} from 'vue'// 数据let temp = ref(0)let height = ref(0)// 方法function changePrice(){temp.value += 10}function changeSum(){height.value += 10}/*// 用watch实现,需要明确的指出要监视:temp、heightwatch([temp,height],(value)=>{// 从value中获取最新的temp值、height值const [newTemp,newHeight] = value// 室温达到50℃,或水位达到20cm,立刻联系服务器if(newTemp >= 50 || newHeight >= 20){console.log('联系服务器')}})*/// 用watchEffect实现,直接写你的代码逻辑,他会根据你用到的去监听//格式:watchEffect( ()=>{}    )const stopWtach = watchEffect(()=>{// 室温达到50℃,或水位达到20cm,立刻联系服务器if(temp.value >= 50 || height.value >= 20){console.log(document.getElementById('demo')?.innerText)console.log('联系服务器')}// 水温达到100,或水位达到50,取消监视if(temp.value === 100 || height.value === 50){console.log('清理了')stopWtach()}})</script>

标签的ref属性

用ref标记dom元素

取代了用id来标识标签,因为用id来标识标签,如果父组件和子组件中有id一样的,会引起混淆。

用ref标记组件

子组件

向父亲暴露name,age,classroom(非响应式) sex不暴露

父组件

得到子组件的实例,测试按钮用于测试能否获取子组件的数据

测试

TS中的接口,泛型


在src中新建一个和组件文件夹同级的types文件夹,里面写ts文件

定义一个学生接口,用于规范学生内的成员(记得导出)


使用接口规范数据格式


集合可用泛型


age 加上一个 ? 表示这个字段是可选的

没有age也不报错


reactive直接传泛型也可以

props的使用(父组件给子组件传数据)

第一种:接收并保存到props里面

父组件

子组件

测试


第二种:接收+限制类型

父组件

同上

如果父组件给出的数据和子组件要求的不一样,会报错

子组件

测试

同上


第三种:

父组件

故意不传myList

子组件

怎么写:第二种情况的语句 作为withDefaults的 第一个参数,第二个参数用大括号包裹(因为有多个不同类型的数据,是对象)起来,里面 key: value 其中value要用返回值的形式给出(这里用箭头函数)

测试

Vue的生命周期

总述

测试Vue3的生命周期

父组件

<!-- 父组件App.vue -->
<template><TestVueLifeCycle></TestVueLifeCycle>
</template><script lang="ts" setup name="App">import TestVueLifeCycle from './components/TestVueLifeCycle.vue';import { onBeforeMount,onMounted } from 'vue';onBeforeMount(()=>{console.log("父-挂载前")})onMounted(()=>{console.log("父-挂载完成")})</script>

子组件

<template>
<h2>{{ count }}</h2>
<button @click="f1">点我count+1</button>
</template><script lang="ts" setup>import { ref } from 'vue';import { onBeforeMount,onMounted } from 'vue';//挂载前,挂载完成import { onBeforeUpdate,onUpdated } from 'vue';//更新前,更新完成import { onBeforeUnmount,onUnmounted } from 'vue';//销毁前,销毁完成let count=ref(0)function f1(){count.value++}console.log("setup完成vue2中创建前,创建完成工作")onBeforeMount(()=>{console.log("子-挂载前")})onMounted(()=>{console.log("子-挂载完成")})onBeforeUpdate(()=>{console.log("更新前")})onUpdated((()=>{console.log("更新完成")}))onBeforeUnmount(()=>{console.log("销毁前")})onUnmounted(()=>{console.log("销毁完成")})
</script>

测试

更新的钩子函数的执行不难理解(执行了两次按钮点击的效果),那前面的钩子为什么会这样执行呢?

答:

1.运行我们的vue工程,首先来到index.html,这是我们的入口html,它调用了main.ts

2.来到main.ts,这里调用了App.vue,并尝试创建它并将它挂载到index.html的名为app的div标签下(输出 父-setup 父-挂载前)

3.来到App.vue 它自上而下执行,所以会先执行所有孩子的创建和挂载工作,等所有的孩子都创建和挂载完成后,App.vue才执行onMounted函数

自定义hooks

准备工作:首先导入axios的依赖

案例

首先写一个子组件,如下

<template>
<h2>{{ sum }}</h2>
<button @click="addSum">点我sum+1</button>
<hr><img v-for="(dog,index) in dogs" :key="index" :src="dog.src">
<br>
<button @click="addDog">点我狗狗+1</button>
</template><script lang="ts" setup>import axios from 'axios';import { ref,reactive } from 'vue';let sum=ref(0)let dogs=reactive([{src:"https://images.dog.ceo/breeds/pembroke/n02113023_6826.jpg"}])function addSum(){sum.value++;}function addDog(){axios.get('https://dog.ceo/api/breed/pembroke/images/random').then(response=>{let msg={src:response.data.message}console.log(msg)dogs.push(msg)}).catch(error=>{console.log(error,"找不到路径")})}</script><style>
img{height: 100px;margin-left: 5px;
}
</style>

这个组件实现的效果如下(有两个功能,点击按钮sum+1 点击按钮出现一张新的狗狗的图片)

注意观察代码的结构,如果一个组件要实现的功能多起来时,方法和数据都放在一个vue里面,会导致代码特别臃肿,要同时找到一个方法所需要的数据和方法也十分困难,这时候hook就派上用场了。

什么是hook?hook有什么优势?

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin
  • 自定义hook的优势:复用代码, 让setup中的逻辑更清楚易懂。

用自定义hook对上述代码进行改进

1.首先在src目录下新建一个hooks目录

2.编写自定义hook(按功能分类,将一个功能的方法和数据抽离出来,用函数包裹,最后把需要暴露的数据和方法交出去)

注意hook的命名规范为: useXxxxx

useSum.ts

useDog.ts

3.在vue中调用hook

<template>
<h2>{{ sum }}</h2>
<button @click="addSum">点我sum+1</button>
<hr><img v-for="(dog,index) in dogs" :key="index" :src="dog.src">
<br>
<button @click="addDog">点我狗狗+1</button>
</template><script lang="ts" setup>
/*     引入 */import useSum from '../hooks/useSum';import useDog from '../hooks/useDog';
/*     调用 */let {sum,addSum}=useSum()let {dogs,addDog}=useDog()
</script><style>
img{height: 100px;margin-left: 5px;
}
</style>

十分清爽,这里体现了vue3组合式api的思想

Vue中的路由

用路由器实现一个基本的切换效果

首先先安装路由器依赖

1.在src文件夹中创建一个router文件夹

2.准备好需要的vue们

这里准备了一个Main.vue作为主界面,要在这里面切换Community,Friend,Home三个组件

2.写一个router

MyRouter.ts

import { createRouter,createWebHistory } from "vue-router";
import community from "../components/Community.vue";
import friend from "../components/Friend.vue";
import home from "../components/Home.vue";const router=createRouter({history:createWebHistory(),routes:[{path:"/community",component:community},{path:"/friend",component:friend},{path:"/home",component:home}]})

3.在main.ts里面注册这个router

import { createApp } from "vue";
import App from "./App.vue";//引入路由器
import router from "./router/MyRouter";const app=createApp(App)app.use(router)//注册路由器app.mount('#app')

4.在主界面上用RouteLink组件绑定对路由路径的响应

Main.vue

<!-- 登录成功后的主界面 --><template>
<h2>登录成功后的主界面</h2>
<div class="app"><!-- 导航区 --><div class="navigate"><RouterLink to="/community" active-class="active" class="navi-style">社区</RouterLink><RouterLink to="/friend" active-class="active" class="navi-style">交友</RouterLink><RouterLink to="/home" active-class="active" class="navi-style">个人中心</RouterLink></div><!-- 展示区 --><div class="main-content"><RouterView></RouterView></div></div></template><script lang="ts" setup>import {RouterLink,RouterView} from 'vue-router'  </script><style>
.active{color: red;
}
.navi-style{margin-left: 10px;
}
</style>

Community.vue(其他两个结构同这个一样)

<!-- 社区模块 -->
<template><h2>社区模块</h2></template><script lang="ts" setup></script><style></style>

效果

两个注意点

1.路由组件通常存放在pagesviews文件夹,一般组件通常存放在components文件夹。(ps:路由组件就是通过路由器才能被渲染的组件,也就是写在RouterLink里面的组件)

2.通过点击导航,视觉效果上“消失” 了的路由组件,默认是被卸载掉的,需要的时候再去挂载

路由器工作模式

history模式

hash模式

怎么写要跳转的路由(三种方法)

to的两种写法

<!-- 第一种:to的字符串写法 -->
<router-link active-class="active" to="/home">主页</router-link><!-- 第二种:to的对象写法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>

命名路由(后面可以用来简化路由跳转和传参的问题)

在路由器中给路由规则命名

在Main.vue中这样写

可以看到第一种是to的字符串写法,后面两种是to的对象写法

<!--         <RouterLink to="/community" active-class="active" class="navi-style">社区</RouterLink> --><!-- 里面只能用单引号 -->
<!--         <RouterLink :to="{path:'/community'}" active-class="active" class="navi-style">社区</RouterLink>  --> <RouterLink :to="{name:'shequ'}" active-class="active" class="navi-style">社区</RouterLink>

嵌套路由

要实现的效果:点击社区模块显示所有的帖子,点击帖子标题在下方显示帖子的详情

1.编写Post.vue

2.将Post.vue 配置成Community.vue的子路由

const router=createRouter({history:createWebHistory(),routes:[{path:"/community",name:"shequ",component:community,//配置子路由children:[{path:"post",name:"tiezi",component:post}]},{path:"/friend",component:friend},{path:"/home",component:home}]})export default router

3.在Community中写跳转

前两种都要加完整路径

<!-- 社区模块 -->
<template><h2>社区模块</h2><li v-for="post in postList" :key="post.postId"><!-- <RouterLink to="/community/post">{{ post.title }}</RouterLink> --><!-- <RouterLink :to="{path:'/community/post'}">{{ post.title }}</RouterLink> --><RouterLink :to="{name:'tiezi'}">{{ post.title }}</RouterLink></li><router-view></router-view></template><script lang="ts" setup>import { onMounted,onUnmounted } from 'vue';let postList=[{postId:"1",title:"今天天气真好",content:"我想出去玩"},{postId:"2",title:"最近有电影好看?",content:"推荐一下"},{postId:"3",title:"再也不万元神了",content:"是不可能的"},{postId:"4",title:"一眼顶针",content:"鉴定为假"},{postId:"5",title:"哈哈哈哈",content:"1234889"}]//切换到这里挂载,切换到别的view被卸载onMounted(()=>{console.log("community被挂载了")})onUnmounted(()=>{console.log("community被卸载了")})</script>

路由传参

query参数传递

传递参数
<!-- 传死数据 --><!-- <RouterLink to="/community/post?postId=1&title=去干嘛&content=114514">{{ post.title }}</RouterLink> --><!-- 模板字符串嵌入js --><!-- <RouterLink :to="`/community/post?postId=${post.postId}&title=${post.title}&content=${post.content}`">{{ post.title }}</RouterLink> --><!-- 携带query参数 --><RouterLink :to="{path:'/community/post',query:{postId:post.postId,title:post.title,content:post.content}}">{{ post.title }}</RouterLink>
接收参数
<template><div class="post-content"><ul><li>id:  {{route.query.postId }}</li><li>标题:  {{ route.query.title }}</li><li>内容:  {{ route.query.content }}</li></ul>
</div></template><script lang="ts" setup>//注意是useRoute   不是useRouterimport { useRoute } from 'vue-router';const route=useRoute()</script>

param参数传递

传递参数

首先需要在路由规则里面添加占位

//param参数占位版children:[{path:"post/:postId/:title/:content?",  //content加个?表示可选name:"tiezi",component:post}]
<!-- 传死数据 --><!-- <RouterLink to="/community/post/1/出去玩/谁一起">{{ post.title }}</RouterLink> --><!-- content是可选的 --><!-- <RouterLink to="/community/post/1/出去玩">{{ post.title }}</RouterLink> -->   <!-- 模板字符串嵌入js --><!-- <RouterLink :to="`/community/post/${post.postId}/${post.title}/${post.content}`">{{ post.title }}</RouterLink> --><!-- 携带param参数              用param传递参数时,必须使用 name --><RouterLink :to="{name:'tiezi',  //养成习惯,用单引号params:{postId:post.postId,title:post.title,content:post.content}}">{{ post.title }}</RouterLink>
接收参数
<template><div class="post-content"><ul><li>id:  {{route.params.postId }}</li><li>标题:  {{ route.params.title }}</li><li>内容:  {{ route.params.content }}</li></ul>
</div></template><script lang="ts" setup>//注意是useRoute   不是useRouterimport { useRoute } from 'vue-router';const route=useRoute()</script>

路由的props配置

作用:用于简化接收数据的写法

1.param

2.query (当然param也可以这么写)

replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式。
  2. 浏览器的历史记录有两种写入方式:分别为pushreplace
  • push是追加历史记录(默认值)。
  • replace是替换当前记录。

在默认的push模式下,可以点击按钮回滚

开启replace

回滚不了了

路由:编程式路由导航

我们前面写的代码,只能通过<RouterLink>标签实现路由跳转,假设我们要实现一个登录功能,会发现我们根本实现不了这个功能,因为<RouterLink>是个标签,不能放在函数里,而我们进行逻辑判断肯定要用到方法。

一个小案例

用button实现和下面<RouteLink>一样的效果

<button @click="showPost(post)">使用编程式导航来跳转路由,显示帖子内容</button><RouterLink :to="{path:'/community/post',query:{postId:post.postId,title:post.title,content:post.content}}">{{ post.title }}</RouterLink>

1.拿到路由器对象

import { useRouter } from 'vue-router';const router=useRouter()

2.要跳到哪里,传什么参数(原来 :to 里面怎么写,这个里面就怎么写)

function showPost(post:any){router.push(   {path:'/community/post',query:{postId:post.postId,title:post.title,content:post.content}})}

选择router.push 或router.replace 决定是否能回溯

测试:点击按钮一样能实现之前的效果

路由:重定向

作用:将特定的路径,重新定向到已有路由。

案例:

需求:刚进来啥也没有,我想要刚进来就显示社区的内容


在路由器中定义一条新路由, 这条路由会在访问

的时候重定向路由到

这样,我们一登录成功就能看到社区的帖子了

pinia

搭建pinia环境

1.引入依赖

npm i pinia

2.在main.ts中引入pinia

准备一个基本的效果

<template><h2>测试一下pinia</h2><br><span>当前求和为:{{ sum }}</span><br><select v-model.number="n"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button @click="add">加</button><button @click="minus">减</button>
</template><script lang="ts" setup>import { ref } from 'vue';let sum=ref(0)let n=ref(1)function add(){sum.value+=n.value}function minus(){sum.value-=n.value}</script>

1.存储,读取数据

在src文件夹下定义一个store文件夹

count.ts

import { defineStore } from "pinia";//定义并暴露一个store  第一个参数是id
export const useCountStore=defineStore('count',{//数据state() {return{sum:0,n:1}},//方法actions:{},})

用pinia读取数据

2.修改数据

修改数据的三种方式如下

<template><h2>测试一下pinia</h2><br><span>当前求和为:{{ countStore.sum }}</span><br><select v-model.number="countStore.n"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button @click="add">加</button><button @click="minus">减</button><br><button @click="change">修改pinia里面的数据</button>
</template><script lang="ts" setup>import { ref } from 'vue';import { useCountStore } from '../store/count';
import { sum } from 'element-plus/es/components/table-v2/src/utils.mjs';let countStore=useCountStore()function add(){countStore.sum+=countStore.n}function minus(){countStore.sum-=countStore.n}function change(){//1.直接修改//countStore.sum=666//2.批量修改
/*         countStore.$patch({sum:999,n:88}        ) *///3.借助action修改(action中可以编写一些业务逻辑)  countStore.Multiply(10) }</script>

count.ts

import { defineStore } from "pinia";//定义并暴露一个store  第一个参数是id
export const useCountStore=defineStore('count',{//数据state() {return{sum:0,n:1}},//方法actions:{Multiply(value:number){this.sum*=value}},})

3.storeToRefs(toRefs)

  • 借助storeToRefsstore中的数据转为ref对象,方便在模板中使用。
  • 注意:pinia提供的storeToRefs只会将数据做转换,而VuetoRefs会转换store中的所有进行转换(包括数据和方法,而方法是不需要我们进行转换的)

4.getters(computed)

定义

使用

这个东西相当于前面学过的computed

5.$subscribe的使用(watch)

通过 store 的 $subscribe() 方法侦听 state 及其变化

//mutate:本次修改的信息//state:发生了什么变化countStore.$subscribe((mutate,state)=>{console.log("这个仓库里面的数据发生了改变",mutate,state)})

点击”加“按钮

这个东西相当于前面学过的watch

tips

1.快捷键

alt+shift 多行写

alt+shift+a 注释

shift+( 用()包裹

2.别名插件

引入一个拓展插件,让Hello.vue被引入App.vue的时候可以起别名

在vite.config.js中引入

3.v-model(适用表单元素)和v-bind(适用表单属性)

数据绑定方向

  • v-model:是双向数据绑定。它不仅能够将 Vue 实例中的数据渲染到页面上,还能够监听用户的输入,并将用户输入的数据更新回 Vue 实例中的数据。这种双向绑定特别适用于表单元素,如 <input><select><textarea>

  • v-bind:是单向数据绑定。它主要用于将 Vue 实例中的数据绑定到 HTML 元素的属性上,但不会自动将用户输入的数据更新回 Vue 实例。虽然通过配合事件处理可以实现双向绑定,但 v-bind 本身只负责单向的数据流。

适用范围

  • v-model:主要用于表单控件或自定义组件的双向数据绑定。它会自动根据控件类型(如文本、单选按钮、复选框、选择框等)选择正确的方法来更新元素。

  • v-bind:几乎可以用于绑定任何 HTML 元素的属性,包括但不限于 classstylehrefsrc 等。此外,它还可以用于绑定表达式和 HTML 内容(尽管绑定 HTML 内容时需要使用不同的语法)。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • mysql学习教程,从入门到精通,SQL 更新数据(UPDATE 语句)(17)
  • 【开源】LVGL+FreeRTOS 基于STM32F411CEU6的健康助手项目制作
  • 解决uniapp开发的app,手机预览,上下滑动页面,页面出现拉伸,抖动的效果问题,
  • Gitlab学习(009 gitlab冲突提交)
  • Shiro-认证绕过漏洞(CVE-2020-1957)
  • 排序----数据结构
  • Lanterns (dp 紫 线段树 二分 维护dp)
  • Java 入门指南:Java 8 新特性 —— Stream 流
  • golang操作mysql利器-gorm
  • 大学生必看!60万人在用的GPT4o大学数学智能体有多牛
  • 大数据新视界 --大数据大厂之 Node.js 与大数据交互:实现高效数据处理
  • Codeforces Round 973 (Div. 2) - D题
  • 数据库事务中的四大问题:脏读、脏写、不可重复读与幻读详解
  • 【HTTPS】对称加密和非对称加密
  • RocketMQ控制台手动新增主题,报错:clusterName or brokerName can not be all blank
  • 5、React组件事件详解
  • crontab执行失败的多种原因
  • java多线程
  • js对象的深浅拷贝
  • JS实现简单的MVC模式开发小游戏
  • Map集合、散列表、红黑树介绍
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • Vue2 SSR 的优化之旅
  • 动态魔术使用DBMS_SQL
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 技术:超级实用的电脑小技巧
  • 聊聊redis的数据结构的应用
  • 漂亮刷新控件-iOS
  • 如何编写一个可升级的智能合约
  • 深入浅出webpack学习(1)--核心概念
  • 学习笔记:对象,原型和继承(1)
  • 优秀架构师必须掌握的架构思维
  • 回归生活:清理微信公众号
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • # windows 安装 mysql 显示 no packages found 解决方法
  • #{}和${}的区别?
  • #FPGA(基础知识)
  • #include<初见C语言之指针(5)>
  • #QT(智能家居界面-界面切换)
  • #Z2294. 打印树的直径
  • (2024)docker-compose实战 (8)部署LAMP项目(最终版)
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)iOS字体
  • (轉貼) UML中文FAQ (OO) (UML)
  • (自适应手机端)响应式服装服饰外贸企业网站模板
  • .Net Core 微服务之Consul(二)-集群搭建
  • .net 使用ajax控件后如何调用前端脚本
  • .NET 中创建支持集合初始化器的类型
  • .NET/C#⾯试题汇总系列:⾯向对象