当前位置: 首页 > 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 内容时需要使用不同的语法)。

相关文章:

vue3(整合版)

创建第一个vue项目 1.安装node.js cmd输入node查看是否安装成功 2.vscode开启一个终端&#xff0c;配置淘宝镜像 # 修改为淘宝镜像源 npm config set registry https://registry.npmmirror.com 输入如下命令创建第一个Vue项目 3.下载依赖&#xff0c;启动项目 访问5173端口 …...

复制他人 CSDN 文章到自己的博客

文章目录 0.前言步骤 0.前言 在复制别人文章发布时&#xff0c;记得表明转载哦 步骤 在需要复制的csdn 文章页面&#xff0c;打开浏览器开发者工具&#xff08;F12&#xff09;Ctrl F 查找"article_content"标签头 右键“Copy”->“Copy element”新建一个 tx…...

【算法——二分查找】

理论基础&#xff1a; 程序员面试经典题&#xff0c;二分搜索一个区间&#xff0c;区间查找 (LeetCode 34)_哔哩哔哩_bilibili 手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode&#xff1a;704. 二分查找_哔哩哔哩_bilibili 这个是红蓝法&#xff0c;很牛…...

Cisco Packet Tracer的安装加汉化

这个工具学计算机网络的同学会用到 1.下载安装 网盘链接&#xff1a;https://pan.baidu.com/s/1CmnxAD9MkCtE7pc8Tjw0IA 提取码&#xff1a;frkb 点击第一个进行安装&#xff0c;按步骤来即可。 2.汉化 &#xff08;1&#xff09;复制chinese.ptl文件 &#xff08;2&…...

MMain函数定义为WinMain函数看port1632.h和pwin32.h文件

编译win2k3的源代码的时候有时候看到MMain函数 ..//public/sdk/inc/port1632.h #if defined(WIN16) /* ---------------- Maps to windows 3.0 and 3.1 16-bit APIs ----------------*/ #include "ptypes16.h" #include "pwin16.h" #include "plan16.…...

单词搜索问题(涉及递归等)

目录 一题目&#xff1a; 二思路解释&#xff1a; 三解答代码&#xff1a; 一题目&#xff1a; newcode题目链接&#xff1a; 单词搜索_牛客题霸_牛客网 二思路解释&#xff1a; 思路&#xff1a;个人理解是找到word中的第一个元素&#xff0c;然后去递归的上下左右查找&am…...

Redis的一些通用指令

首先我们需要先连接客户端服务器&#xff0c;此时我们需要通过redis-cli和redis服务器进行交互&#xff0c;输入ping来确保通路的流畅 &#xff08;一&#xff09;get和set redis中最核心的两个命令就是get和set&#xff0c;get就是根据key来取出对应value&#xff0c;set就是把…...

C++中vector类的使用

目录 1.vector类常用接口说明 1.1默认成员函数 1.1.1构造函数(constructor) 1.1.2 赋值运算符重载(operator()) 2. vector对象的访问及遍历操作(Iterators and Element access) 3.vector类对象的容量操作(Capacity) 4. vector类对象的修改及相关操作(Modifiers and Stri…...

cmaklist流程控制——调试及发布

cmaklist流程控制 目前只会配置-编译调试-打包发布&#xff0c;并且不会workflow控制 后续学习配置-编译调试-测试-打包发布&#xff0c;workflow控制&#xff0c;理解整个流程&#xff0c;目前对流程控制理解也不够。 1.CMake Presets 先于Cmakelist文件&#xff0c;指导项…...

制作一个能对话能跳舞的otto机器人

OTTO机器人是一个开源外壳&#xff0c;硬件和软件的桌面机器人项目&#xff0c;非常适合新手研究和拓展。记住&#xff0c;他是一个能移动有表情能声音的机器人。 b站有很多演示和组装的视频&#xff0c;我就不多说了&#xff0c;照着做就好&#xff0c;因为硬件我也是刚入门&…...

git配置SSH

1 打开cmd窗口 2 在窗口中输入如下命令&#xff1a; 配置用户名&#xff1a; git config --global user.name “gyk” 配置邮箱&#xff1a; git config --global user.email “247929163qq.com” 继续在Git命令窗口中输入如下命令&#xff0c;即可生成SSH公钥和私钥 ss…...

mozilla/pdf.js view.html加载指定页码

mozilla/pdf.js view.html加载指定页码 在Mozilla’s PDF.js中&#xff0c;如果你想要在viewer.html加载时直接跳转到指定的页码&#xff0c;你可以通过修改URL来实现。 PDF.js使用查询参数来处理URL&#xff0c;其中page参数用于指定页码。你可以通过修改URL的查询字符串来设…...

Qt之QFuture理解

结构 #mermaid-svg-J9J683RG8QjtEqoM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-J9J683RG8QjtEqoM .error-icon{fill:#552222;}#mermaid-svg-J9J683RG8QjtEqoM .error-text{fill:#552222;stroke:#552222;}#merm…...

求二叉树的高度(递归和非递归)

假设二叉树采用二叉链表存储结构&#xff0c;设计一个算法求二叉树的高度。 递归&#xff1a; int getTreeHight(BiTree T){if(TNULL){return 0;}else {int lh getTreeHight(T->lchild);int rh getTreeHight(T->rchild);return (lh>rh?lh:rh)1;}}时间复杂度O(n)&a…...

Java查找算法——(四)分块查找(完整详解,附有代码+案例)

文章目录 分块查找1.1普通分块查找 分块查找 1.1普通分块查找 分块原则&#xff1a; 块内无序&#xff0c;块间有序:前一块中的最大数据&#xff0c;小于后一块中所有的数据&#xff0c;块与块之间不能有数据重复的交集。块的数量一般等于数字个数开根号 核心思路&#xff…...

进制数知识(2)—— 浮点数在内存中的存储 和 易混淆的二进制知识总结

目录 1. 浮点数在内存中的存储 1.1 浮点数的大V表示法 1.2 浮点数的存储格式 1.3 浮点数的存入规则 1.4 浮点数的读取规则 1.5 补充&#xff1a;移码与掩码 1.6 题目解析 2. 易错的二进制知识 2.0 符号位到底会不会参与运算&#xff1f; 2.0.1 存储前的编码变化运算 …...

类似QQ聊天功能的Java程序

实现一个类似QQ聊天功能的Java程序需要考虑以下几个关键点&#xff1a; 用户界面&#xff1a;用于展示消息和输入消息。网络通信&#xff1a;用于客户端之间的信息传输。用户管理&#xff1a;用于管理用户的登录、注册和状态。消息存储&#xff1a;用于存储聊天记录。 这里提…...

Redis 键值对数据库学习

目录 一、介绍 二、安装以及连接 三、设置连接密码 四、连接报错 五、redis 操作字符串以及过期时间 六、 redis 列表操作 七、redis 集合操作 八、hash 哈希操作 九、redis 发布和订阅操作 十、RDB和AOF的两种数据持久化机制 十一、 其他机器连接redis 十二、 pyt…...

逆向推理+ChatGPT,让论文更具说服力

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 使用ChatGPT辅助“逆向推理”技巧&#xff0c;可以显著提升论文的质量和说服力。逆向推理从结论出发&#xff0c;倒推所需的证据和论点&#xff0c;确保整个论证过程逻辑严密且无漏洞。…...

「JavaScript深入」一文说明白JS的执行上下文与作用域

JavaScript深入 — 执行上下文与作用域 上下文执行上下文生命周期创建阶段执行阶段回收阶段 执行栈作用域链作用域词法作用域&#xff08;静态作用域&#xff09; 上下文 变量或函数的上下文决定了它们可以访问哪些数据&#xff0c;以及它们的行为。 每个上下文都有一个关联的…...

Qt C++设计模式->组合模式

组合模式&#xff08;Composite Pattern&#xff09;是一种结构型设计模式&#xff0c;允许你将对象组合成树形结构以表示部分与整体的层次关系。组合模式使得客户端可以以统一的方式对待单个对象和组合对象&#xff0c;简化了对复杂树形结构的操作。 组合模式的应用场景 组合…...

Acwing Bellman-Ford SPFA

1. Bellman-Ford 该算法适用于有负权边的情况&#xff0c;注意&#xff1a;如果有负权环的话&#xff0c;最短路就不一定存在了。时间复杂度 O ( m n ) . O(mn). O(mn).该算法可以求出来图中是否存在负权回路&#xff0c;但求解负权回路&#xff0c;通常用SPFA算法&#xff0c…...

我能禁止使用某协议的ip禁止访问我的资源吗

是的&#xff0c;你可以禁止使用某个协议的IP地址访问你的资源。这种操作通常涉及网络防火墙、服务器配置或应用程序设置&#xff0c;具体方法取决于你的网络环境和使用的技术。以下是一些常见的实现方法&#xff1a; 1. 使用防火墙 大多数防火墙&#xff08;硬件或软件&…...

快速理解TCP协议(二)——TCP协议中的拥塞控制机制详解

在计算机网络中&#xff0c;TCP&#xff08;传输控制协议&#xff09;是一种广泛使用的面向连接的、可靠的、基于字节流的传输层通信协议。TCP协议通过一系列复杂的机制来确保数据的可靠传输&#xff0c;其中拥塞控制是至关重要的一环。本文将深入探讨TCP协议中的拥塞控制机制&…...

Linux:debug: systemtap: ubacktrace

https://docs.huihoo.com/systemtap/sourceware.org/systemtap/SystemTap_Beginners_Guide/ustack.html 这个函数可以帮助将user level的backtrace打印出来。 stap -d /bin/ls --ldd \ -e probe process("ls").function("xmalloc") {print_usyms(ubacktra…...

使用AI进行需求分析的案例研究

生成式 AI 的潜在应用场景似乎无穷无尽。虽然这令人兴奋&#xff0c;但也可能让人不知所措。因此&#xff0c;团队在使用这项技术时需要有明确的目标&#xff1a;关键是要明确生成式 AI 在团队工作中能产生哪些实质性影响。 在软件工程中&#xff0c;一个引人注目的应用场景是…...

Python内置的re库

Python内置的re库是专门用于处理正则表达式的标准库。它提供了一系列函数和类&#xff0c;使得在Python程序中可以使用正则表达式进行字符串的搜索、替换、分割等操作。re库的使用非常广泛&#xff0c;几乎任何需要复杂文本处理的场景都可以用到它。 主要函数 1、complie函数…...

毕业设计选题:基于ssm+vue+uniapp的面向企事业单位的项目申报小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…...

jQuery 简介⑤属性操作

九、属性操作 jQuery的属性操作方法一览表 $("selector").val(); // 获取第一个匹配元素的value值(一般用于表单控("selector").val("Hello"); // 设置所有匹配元素的value值为"Hello" $("selector").html();// 获取第一个…...

[Linux] Linux操作系统 进程的状态

标题&#xff1a;[Linux] Linux操作系统 进程的状态 个人主页&#xff1a;水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、前置概念的理解 1.并行和并发 2.时间片 3.进程间具有独立性 4.等待的本质 正文开始&#xff1a; 在校的时候&#xff0c;你一定学过《…...