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种数据:
ref
定义的数据。reactive
定义的数据。- 函数返回一个值(
getter
函数)。 - 一个包含上述内容的数组。
因为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.路由组件通常存放在pages
或 views
文件夹,一般组件通常存放在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属性
- 作用:控制路由跳转时操作浏览器历史记录的模式。
- 浏览器的历史记录有两种写入方式:分别为
push
和replace
:
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)
- 借助
storeToRefs
将store
中的数据转为ref
对象,方便在模板中使用。 - 注意:
pinia
提供的storeToRefs
只会将数据做转换,而Vue
的toRefs
会转换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 元素的属性,包括但不限于
class
、style
、href
、src
等。此外,它还可以用于绑定表达式和 HTML 内容(尽管绑定 HTML 内容时需要使用不同的语法)。
相关文章:

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

复制他人 CSDN 文章到自己的博客
文章目录 0.前言步骤 0.前言 在复制别人文章发布时,记得表明转载哦 步骤 在需要复制的csdn 文章页面,打开浏览器开发者工具(F12)Ctrl F 查找"article_content"标签头 右键“Copy”->“Copy element”新建一个 tx…...

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

Cisco Packet Tracer的安装加汉化
这个工具学计算机网络的同学会用到 1.下载安装 网盘链接:https://pan.baidu.com/s/1CmnxAD9MkCtE7pc8Tjw0IA 提取码:frkb 点击第一个进行安装,按步骤来即可。 2.汉化 (1)复制chinese.ptl文件 (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.…...

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

Redis的一些通用指令
首先我们需要先连接客户端服务器,此时我们需要通过redis-cli和redis服务器进行交互,输入ping来确保通路的流畅 (一)get和set redis中最核心的两个命令就是get和set,get就是根据key来取出对应value,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流程控制 目前只会配置-编译调试-打包发布,并且不会workflow控制 后续学习配置-编译调试-测试-打包发布,workflow控制,理解整个流程,目前对流程控制理解也不够。 1.CMake Presets 先于Cmakelist文件,指导项…...

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

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

mozilla/pdf.js view.html加载指定页码
mozilla/pdf.js view.html加载指定页码 在Mozilla’s PDF.js中,如果你想要在viewer.html加载时直接跳转到指定的页码,你可以通过修改URL来实现。 PDF.js使用查询参数来处理URL,其中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…...

求二叉树的高度(递归和非递归)
假设二叉树采用二叉链表存储结构,设计一个算法求二叉树的高度。 递归: 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普通分块查找 分块原则: 块内无序,块间有序:前一块中的最大数据,小于后一块中所有的数据,块与块之间不能有数据重复的交集。块的数量一般等于数字个数开根号 核心思路ÿ…...

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

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

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

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

「JavaScript深入」一文说明白JS的执行上下文与作用域
JavaScript深入 — 执行上下文与作用域 上下文执行上下文生命周期创建阶段执行阶段回收阶段 执行栈作用域链作用域词法作用域(静态作用域) 上下文 变量或函数的上下文决定了它们可以访问哪些数据,以及它们的行为。 每个上下文都有一个关联的…...

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

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

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

快速理解TCP协议(二)——TCP协议中的拥塞控制机制详解
在计算机网络中,TCP(传输控制协议)是一种广泛使用的面向连接的、可靠的、基于字节流的传输层通信协议。TCP协议通过一系列复杂的机制来确保数据的可靠传输,其中拥塞控制是至关重要的一环。本文将深入探讨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 的潜在应用场景似乎无穷无尽。虽然这令人兴奋,但也可能让人不知所措。因此,团队在使用这项技术时需要有明确的目标:关键是要明确生成式 AI 在团队工作中能产生哪些实质性影响。 在软件工程中,一个引人注目的应用场景是…...

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

毕业设计选题:基于ssm+vue+uniapp的面向企事业单位的项目申报小程序
开发语言:Java框架:ssmuniappJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:M…...

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

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