安装vue3+vite
https://cn.vitejs.dev/guide/ vite官网
需要安装高版本的nodejs http://nodejs.cn/download/
Vite 需要 Node.js 版本 18+,20+。然而,有些模板需要依赖更高的 Node 版本才能正常运行,当你的包管理器发出警告时,请注意升级你的 Node 版本。
1.创建命令
npm create vite@latest
2.具体的配置
可以继续吗?
Ok to proceed?(y)
配置项目名称
Project name:
选择Vue TypeScript
VSCODE 安装插件提示 TypeScript Vue Plugin (Volar)
Vue VSCode Snippets
代码提示快速生成代码插件 比如:vbase
Options API的弊端 vue2语法 配置项API
Options类型的API,数据,方法,计算属性等,是分散在:data,methods,computed中的,若想新增或者修改一个需求,就需要分别修改:data,methods,computed,不便于维护和复用。
vue3 组合式API
Composition API优势:可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起。
setup概述
setup是vue3中一个新的配置项,值是一个函数。
1.vue2 选项式的语法能和vue3 setup共存
2.vue2的语法 里面 是可以读取setup里面的数据的 setup是不可以读取vue2里面的数据的
javascript
<template>
<div>
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<button @click="showTel">查看联系方式</button>
<h2>{{ name1 }}</h2>
<h2>{{ age1 }}</h2>
<button @click="changeAge">修改年龄</button>
<button @click="showTel1">查看联系方式</button>
</div>
</template>
<script lang="ts">
import { ref } from 'vue'
export default {
name:"Person",//组件名
data() {
return {
name:"张三",
age:this.age1,//可以读到setup里面的数据
tel:"1827368288",
}
},
methods:{
showTel(){
alert(this.tel)
}
},
setup(){
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,此时的name1,tel1都不是响应式的数据
let name1="李四" //注意此时的name1不是响应式的
let age1=ref(19)
let tel1="17283478219"
//方法
function showTel1(){
alert(tel1)
}
function changeAge(){
age1.value +=1;
}
//将数据,方法交出去,模板中才可以使用
return {
name1,age1,showTel1,changeAge
}
//setup的返回值也可以是一个渲染函数,可以直接指定渲染的内容,上面的那些模板就没有什么作用了。
//return ()=>"哈哈"
}
}
</script>
setup语法糖
减少了setup函数 和return
javascript
<template>
<div>
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<button @click="showTel">查看联系方式</button>
<h2>{{ name1 }}</h2>
<h2>{{ age1 }}</h2>
<button @click="changeAge">修改年龄</button>
<button @click="showTel1">查看联系方式</button>
</div>
</template>
<script lang="ts">
export default {
name:"Person",//组件名
data() {
return {
name:"张三",
age:this.age1,//可以读到setup里面的数据
tel:"1827368288",
}
},
methods:{
showTel(){
alert(this.tel)
}
},
}
</script>
<script lang="ts" setup>
import { ref } from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,此时的name1,tel1都不是响应式的数据
let name1="李四" //注意此时的name1不是响应式的
let age1=ref(19)
let tel1="17283478219"
//方法
function showTel1(){
alert(tel1)
}
function changeAge(){
age1.value +=1;
}
</script>
组件命名
给组件命名需要安装一个插件
npm i vite-plugin-vue-setup-extend -D
还需要修改vite.config,ts配置文件
javascript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
//给组件命名需要安装这个插件
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
VueSetupExtend()
],
})
直接script在上面name="Person123" 就可以命名组件名了
javascript
<template>
<div>
<h2>{{ name1 }}</h2>
<h2>{{ age1 }}</h2>
<button @click="changeAge">修改年龄</button>
<button @click="showTel1">查看联系方式</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { ref } from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,此时的name1,tel1都不是响应式的数据
let name1="李四" //注意此时的name1不是响应式的
let age1=ref(19)
let tel1="17283478219"
//方法
function showTel1(){
alert(tel1)
}
function changeAge(){
age1.value +=1;
}
</script>
ref
响应式 ref 对应基本类型的数据,对象类型数据,reactive 对应对象类型数据
ref创建的变量必须使用.value
javascript
<template>
<div>
<h2>{{ user.name }}</h2>
<h2>{{ age1 }}</h2>
<button @click="changeAge">修改年龄</button>
<button @click="changeName">修改名称</button>
<button @click="showTel1">查看联系方式</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { ref,reactive } from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,
let user=reactive({
name:"李四"
})
let age1=ref(19)
let tel1="17283478219"
function changeName(){
user.name="wangwu"
}
//方法
function showTel1(){
alert(tel1)
}
function changeAge(){
age1.value +=1;
}
</script>
toRef和toRefs
javascript
<template>
<div>
<h2>{{ user.name }}</h2>
<h2>{{ user.age }},{{nl}}</h2>
<button @click="changeAge">修改年龄</button>
<button @click="changeName">修改名称</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { ref,reactive,toRefs ,toRef} from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,
let user=reactive({
name:"李四",
age:19
})
//toRefs 接收一个由 reactive所定义的响应式对象 并且把响应式对象里面的每一组key和value形成一个新的对象
//let {name,age} =user; //解构出来的不是响应式的
let {name,age} =toRefs(user); //这样就是响应式的 toRefs变成一个一个由ref响应式数据 toRefs这个是取所有
let nl=toRef(user,"age"); //toRef这个只能一个一个取 解构拿出来 具备响应式
console.log(nl.value)
function changeName(){
name.value +="~"
console.log(name.value,user.name) //
}
function changeAge(){
age.value +=1;
}
</script>
computed计算属性
javascript
<template>
<div>
姓:<input type="text" v-model="user">
名:<input type="text" v-model="name">
全名:<span>{{username}}</span>
<button @click="changeFullName">将全名改为li-si</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { ref,computed} from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,
let user=ref("zhang")
let name=ref("san")
//要求第一个字母大写 computed要求必须有返回值
//这么定义的username是一个计算属性,且是只读的
// let username= computed(()=>{
// return user.value.slice(0,1).toUpperCase()+user.value.slice(1)+'-'+name.value
// })
//这么定义的username是一个计算属性,可读可写
let username= computed({
get(){
return user.value.slice(0,1).toUpperCase()+user.value.slice(1)+'-'+name.value
},
set(val){
const [str1,str2]= val.split('-')
user.value=str1
name.value=str2
console.log('set',val)
}
})
function changeFullName(){
username.value="li-si"
}
</script>
watch 很重要
作用:监视数据的变化(和vue2中的watch作用一致)
特点:vue3中的watch只能监视以下四种数据:
1.ref定义的数据。
2.reactive定义的数据
3.函数返回一个值
4.一个包含上述内容的数组
情况一:监视ref定义的基本类型数据:直接写数据名即可,监视的是其value值的改变。
javascript
<template>
<div>
<h1>情况一:监视 ref 定义的 基本类型 数据</h1>
<h2>当前求和为:{{ sum }}</h2>
<button @click="changeSum">点我sum+1</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { ref,watch} from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,
let sum=ref(0)
//方法
function changeSum(){
sum.value +=1
}
//监视sum 不用写value 新值 旧值
const stopWatch= watch(sum,(newValue,oldValue)=>{
console.log("sum变化了",newValue,oldValue)
//解除监视
if(newValue >=10){
stopWatch()
}
})
</script>
情况二:
若修改的是ref定义的对象中的属性,newValue和oldValue都是新值,因为它们是同一个对象。
若修改整个ref定义的对象,newValue是新值,oldValue是旧值,因为不是同一个对象了。
javascript
<template>
<div>
<h1>情况二:监视 ref 定义的 对象类型 数据</h1>
<h2>姓名:{{ user.name }}</h2>
<h2>年龄:{{ user.age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changeUser">修改整个人</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { ref,watch} from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,
let user=ref({
name:'张三',
age:19
})
//方法
function changeName(){
user.value.name +="~"
}
function changeAge(){
user.value.age +=1
}
function changeUser(){
user.value={name:"李四",age:30}
}
//监视 情况一:监视 ref 定义的 对象类型 数据 监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视deep:true 第三个参数
//wacth的第一个参数是:被监视的数据
//wacth的第二个参数是:监视的回调
//wacth的第三个参数是:配置对象(deep,immediate等等...)
watch(user,(newValue,oldValue)=>{
console.log("user变化了",newValue,oldValue)
},{deep:true,immediate:true}) //immediate:true这个作用是立即监视 一上来就先执行一下监视
</script>
情况三:监视reactive定义的 对象类型 数据,且默认开启了深度监视。
javascript
<template>
<div>
<h1>情况三:监视 reactive 定义的 对象类型 数据</h1>
<h2>姓名:{{ user.name }}</h2>
<h2>年龄:{{ user.age }}</h2>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changeUser">修改整个人</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { reactive,watch} from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,
let user=reactive({
name:'张三',
age:19
})
//方法
function changeName(){
user.name +="~"
}
function changeAge(){
user.age +=1
}
function changeUser(){
Object.assign(user,{name:"李四",age:30})
}
//监视,情况三:监视 reactive 定义的 对象类型 数据,且默认是开启深度监视的 并且该深度监视无法关闭
watch(user,(newValue,oldValue)=>{
console.log("user变化了",newValue,oldValue)
})
</script>
情况四 :监视ref或reactive定义的对象类型 数据中的某个属性,注意点如下:
1.若该属性不是 对象类型,需要写成函数形式,
2.若该属性值依然是 对象类型,可直接编,也可写成函数,不过建议写成函数。
结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。
javascript
<template>
<div>
<h2>姓名:{{user.name}}</h2>
<h2>年龄:{{user.age}}</h2>
<h2>汽车:{{user.car.c1}},{{ user.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="Person123">
import { reactive,watch} from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,
let user=reactive({
name:"张三",
age:18,
car:{
c1:"奔驰",
c2:"宝马"
}
})
//方法
function changeName(){
user.name += "~"
}
function changeAge(){
user.age += 1
}
function changeC1(){
user.car.c1="奥迪"
}
function changeC2(){
user.car.c2="大众"
}
function changeCar(){
user.car={c1:"特斯拉",c2:"比亚迪"}
}
//只想监视name 写成getter函数 ()=>{return user.name}
//监视,情况四,监视响应式对象中的某个值,且该属性是基本类型的,要写成函数式
watch(()=>{return user.name},(newValue,oldValue)=>{
console.log("user.name变化了",newValue,oldValue)
})
//监视,情况四,监视响应式对象中的某个值,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
//结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。
watch(()=>user.car,(newValue,oldValue)=>{
console.log("user.car变化了",newValue,oldValue)
},{deep:true})
</script>
情况五:
javascript
<template>
<div>
<h2>情况五:监视上述多个数据</h2>
<h2>姓名:{{user.name}}</h2>
<h2>年龄:{{user.age}}</h2>
<h2>汽车:{{user.car.c1}},{{ user.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="Person123">
import { reactive,watch} from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,
let user=reactive({
name:"张三",
age:18,
car:{
c1:"奔驰",
c2:"宝马"
}
})
//方法
function changeName(){
user.name += "~"
}
function changeAge(){
user.age += 1
}
function changeC1(){
user.car.c1="奥迪"
}
function changeC2(){
user.car.c2="大众"
}
function changeCar(){
user.car={c1:"特斯拉",c2:"比亚迪"}
}
//监视,情况五:监视上述多个数据 监视人的名字 人的第一台车 数组里面写函数 不一定要写函数 比如[()=>user.name,user.car] user.car是一个对象类型
watch([()=>user.name,()=>user.car.c1],(newValue,oldValue)=>{
console.log("user变化了",newValue,oldValue)
},{deep:true})
</script>
watchEffect 实用
watch对比watchEffect
1.都能监听响应式数据的变化,不同的是监听数据变化的方式不同
2.watch:要明确指出监视的数据
3.watchEffect :不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)
javascript
<template>
<div>
<h2>需求:当水温达到60度,或水位达到80cm时,给服务器发请求</h2>
<h2>当前水温:{{ temp }}℃</h2>
<h2>当前水位:{{ height }}cm</h2>
<button @click="changeTemp">水温+10</button>
<button @click="changeHeight">水位+10</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import { ref,watch,watchEffect} from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//数据,原来是写在data中的,
let temp =ref(10);
let height =ref(0);
function changeTemp(){
temp.value +=10
}
function changeHeight(){
height.value +=10
}
//监视 watch需要明确的指出监视的对象
// watch([temp,height],(value)=>{
// //从value中获取最新的水温newTemp 最新的水位newHeight
// let [newTemp,newHeight] =value
// //逻辑
// if(newTemp>=60 || newHeight >=80){
// console.log("向后台发送请求")
// }
// })
// 监视 watchEffect 直接用就可以了 不需要指定监视的对象 比如多个对象 七八个 这个好用全自动
watchEffect(()=>{
if(temp.value >=60 || height.value >=80){
console.log("向后台发送请求")
}
})
</script>
标签的ref属性
用在普通的DOM标签上,获取的是DOM节点
javascript
<template>
<div>
<h1>中国</h1>
<h2 ref="title2">北京</h2>
<h3>硅谷</h3>
<button @click="showlog">点我输出h2这个元素</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import {ref,defineExpose} from 'vue'
//setup函数中的this是undefined vue3中已经弱化this了
//创建一个title2,用于存储ref标记的内容
//defineExpose 导出的意思
let title2=ref()
let a=ref(0)
let b=ref(1)
function showlog(){
console.log(title2.value)
}
//导出给父组件用。不然不让用
defineExpose({a,b})
</script>
用在组件标签上,获取的是组件实例对象。
javascript
<template>
<!-- vue2 需要根标签,vue3不需要根标签 小升级 -->
<Preson ref="ren" />
<button @click="showLog">点我输出</button>
</template>
<!-- vue3里面也可以写vue2语法 -->
<script lang="ts" setup name="App">
import Preson from './components/Preson.vue';
import {ref} from 'vue'
let ren=ref()
function showLog(){
console.log(ren.value)
}
</script>
TS定义接口类型
javascript
//定义一个接口,用于限制person对象的具体属性 需要暴露出去export 限制单个人
export interface PersonInter {
id:string, //在ts里面一定是小写
name:string,
age:number,
x?:number //x设置可以可无
}
//一个自定义类型 限制多个人
//export type Persons =Array<PersonInter>
export type Persons =PersonInter[] //简洁的写法
引入注意加 type
javascript
<template>
<div>
</div>
</template>
<script lang="ts" setup name="Person123">
import {type PersonInter,type Persons} from '../types'
let person:PersonInter={id:'h3u243',name:'张三',age:40}
//数组泛型
let personList:Persons=[
{id:'h3u243',name:'张三',age:40},
{id:'h3u243',name:'李四',age:20}
]
</script>
props的使用
defineProps 宏函数不需要引入 也不会报错
子组件
javascript
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">{{ item.name }}--{{ item.age }}</li>
</ul>
</div>
</template>
<script lang="ts" setup name="Person123">
import { withDefaults } from 'vue';
import { type Persons} from "../types"
//一个也是数组接收 接收a
//defineProps(["a"])
//接收list,同时将props保存起来
//let x= defineProps(["list"])
//接收list+限制类型,同时将props保存起来
//let x= defineProps<{list:Persons}>()
//withDefaults 给默认值
//接收list+限制类型+限制必要性+指定默认值,同时将props保存起来
withDefaults(defineProps<{list?:Persons}>(),{
list:()=>[{id:'fi3',name:"咖啡机",age:12}]
})
</script>
父组件
javascript
<template>
<!-- vue2 需要根标签,vue3不需要根标签 小升级 -->
<Preson ref="ren" a="哈哈" :list="personList" />
</template>
<!-- vue3里面也可以写vue2语法 -->
<script lang="ts" setup name="App">
import Preson from './components/Preson.vue';
import { reactive } from 'vue';
import { type Persons} from "./types"
let personList=reactive<Persons>([
{id:"dsf3",name:"张三",age:23},
{id:"fsd2",name:"李四",age:33,x:23},
])
</script>
vue2生命周期
创建(创建前 beforeCreate,创建完毕 created)
挂载(挂载前 beforeMount,挂载完毕 mounted)
更新(更新前 beforeUpdate,更新完毕updated)
销毁(销毁前 beforeDestroy,销毁完毕destroyed)
vue3生命周期 驼峰命名
创建(setup)
挂载(挂载前 onBeforeMount,挂载完毕 onMounted)
更新(更新前 onBeforeUpdate,更新完毕onUpdated)
卸载(卸载前 onBeforeUnmount,卸载完毕onUnmounted)
常用的钩子:(挂载完毕 onMounted) (更新完毕onUpdated)(卸载前 onBeforeUnmount)
javascript
<template>
<div>
{{ sum }}
<button @click="changeSum">更新sum+1</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
let sum=ref(2)
function changeSum(){
sum.value +=1
}
//创建
console.log("创建")
//挂载前
onBeforeMount(()=>{
console.log("挂载前")
})
//挂载完毕
onMounted(()=>{
console.log("子---挂载完毕")
})
//更新前
onBeforeUpdate(()=>{
console.log("更新前")
})
//更新完毕
onUpdated(()=>{
console.log("更新完毕")
})
//卸载前
onBeforeUnmount(()=>{
console.log("卸载前")
})
//卸载完毕
onUnmounted(()=>{
console.log("卸载完毕")
})
</script>
卸载子组件
javascript
<template>
<!-- vue2 需要根标签,vue3不需要根标签 小升级 -->
<Preson v-if="isShow" />
<button @click="changeShow">点我卸载</button>
</template>
<!-- vue3里面也可以写vue2语法 -->
<script lang="ts" setup name="App">
import Preson from './components/Preson.vue';
import { ref,onMounted } from 'vue';
let isShow=ref(true)
function changeShow(){
isShow.value=false
}
//挂载完毕
onMounted(()=>{
console.log("父---挂载完毕")
})
</script>
hooks组合封装
文件命名规则 use开头 比如useDog.ts 这样就可以把独立的数据和方法放入一个文件里面 组合式API 需要一个函数包裹,还需要向外部提供东西
useDog.ts
javascript
import {reactive,onMounted} from 'vue'
import axios from 'axios';
//export default 直接跟值 比如 export default 1
export default function (){
//数据
let dogList=reactive([
"https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg"
])
//方法
async function chagneDog(){
try {
let result=await axios.get("https://dog.ceo/api/breed/pembroke/images/random")
dogList.push(result.data.message)
} catch (error) {
alert(error)
}
}
//钩子
onMounted(()=>{
chagneDog()
})
//向外部提供东西
return {dogList,chagneDog}
}
useSum.ts
javascript
import { ref,onMounted,computed} from 'vue'
export default function (){
//数据
let sum=ref(2)
let bigSum=computed(()=>{
return sum.value*10
})
//方法
function changeSum(){
sum.value +=1
}
//钩子
onMounted(()=>{
changeSum()
})
//向外部提供东西
return {sum,changeSum,bigSum}
}
组件引用
javascript
<template>
<div>
{{ sum }}
<h2>{{ bigSum }}</h2>
<button @click="changeSum">更新sum+1</button>
<img :src="item" v-for="(item,index) in dogList" :key="index">
<button @click="chagneDog">再来一只狗</button>
</div>
</template>
<script lang="ts" setup name="Person123">
import useDog from '../hooks/useDog';
import useSum from '../hooks/useSum';
//解构
const {sum,changeSum,bigSum}=useSum()
const {dogList,chagneDog}=useDog()
</script>
<style scoped>
img{
height: 100px;
margin-right: 10px;
}
</style>
路由 很重要
npm i axios
npm i vue-router
1.history模式
优点:URL更加美观,不带有#号,更接近传统的网站URL
缺点:后期项目上线,需要服务器配合处理路径问题,否则刷新会有404错误
2.hash模式
优点:兼容性好,因为不需要服务器端处理路径
缺点:URL带有#不太美观,且在SEO优化方面相对较差
路由的props配置
嵌套路由
router/index.ts
javascript
//创建一个路由器,并暴露出去
//第一步:引入createRouter 带#号hash模式 不带#号history模式
import { createRouter,createWebHashHistory,createWebHistory } from "vue-router";
//引入一个一个可能要呈现的组件
import Home from '../pages/Home.vue'
import News from '../pages/News.vue'
import About from '../pages/About.vue'
import Detal from '../components/Detal.vue'
//第二步:创建路由器
const router =createRouter({
//设置路由器的工作模式 带#号
history:createWebHashHistory(), //hash模式
//路由
routes:[ //一个一个的路由规则
{
name:'zhuye',
path:'/home',
component:Home
},
{
name:'xinwen',
path:'/news',
component:News,
children:[ //子路由
{
name:'xiangqing',
path:'detail/:id/:title/:count?', //不需要加斜杆 会自动匹配 可以这样写'detail/:id/:title' :count? 加了问号是可传可不传 params传参 占位/:id/:title 可以直接这样使用 detail/23/哈哈
component:Detal,
//第一种写法:将路由收到的所有params参数作为props传给路由组件 这个只能和params打配合
props:true, //加了这个 组件 相当于添加了这些属性 <Detal id=?? title=?? count=??> params参数
//第二种写法: 函数写法,可以自己决定将什么作为props给路由组件 query参数 上面的参数就不能有占位符 否则报错
// props(route){
// return route.query
// }
//第三种写法:对象写法,可以自己决定将什么作为props给路由组件 写死了数据 不推荐
// props:{
// id:1,
// title:2
// }
}
]
},
{
name:'guanyu', //命名路由
path:'/about',
component:About
},
{
path:"/",
redirect:'/home' //重定向 让指定的路径重新定位到另一个路径
}
]
})
//暴露出去router
export default router
main.ts
javascript
import { createApp } from 'vue' //引入createApp用于创建应用
import App from './App.vue' //引入App根组件
//引入路由器
import router from './router'
const app=createApp(App); //创建前端应用
//使用路由器
app.use(router);
app.mount('#app'); //所有组件的根组件,挂载到id为app的容器里面
App.vue
javascript
<template>
<div>
<div>
<!-- active-class被激活的样式 to的两种写法-->
<!-- 字符串跳转 -->
<RouterLink to="/home" active-class="active">首页</RouterLink>
<!-- 名字跳转 -->
<RouterLink :to="{name:'xinwen'}" active-class="active">新闻</RouterLink>
<!-- path跳转 -->
<RouterLink :to="{path:'/about'}" active-class="active">关于</RouterLink>
</div>
<div>
<RouterView></RouterView>
</div>
</div>
</template>
<script lang="ts" setup name="App">
import { RouterView,RouterLink } from 'vue-router';
</script>
<style>
.active{
color: red;
}
</style>
传递参数 编程式路由导航跳转
备注1:传递params参数时,若使用to的对象写法,必须使用name配置项,不能用path。
备注2:传递params参数时,需要提前在规则中占位。
News.vue
javascript
<template>
<div>
<!-- 这里的路由要写完整的 -->
<!-- <RouterLink :to="{path:'/news/detail'}" active-class="active">news</RouterLink> -->
<!-- query传参第一种写法 -->
<!-- <RouterLink :to="`/news/detail?id=${id}&title=${title}`" active-class="active">news</RouterLink> -->
<!-- query传参第二种种写法 -->
<!-- <RouterLink :to="{name:'xiangqing',query:{id,title}}" active-class="active">news</RouterLink> -->
<!-- params传参第一种写法 路由配置需要占位 比如:'detail/:id/:title' -->
<!-- <RouterLink :to="`/news/detail/${id}/${title}`" active-class="active">news</RouterLink> -->
<!-- params传参第二种写法 路由配置需要占位 比如:'detail/:id/:title' 不能用path了 ,只能用name 不能传数组-->
<RouterLink :to="{name:'xiangqing',params:{id,title}}" active-class="active">news</RouterLink>
<button @click="routpath">编程式跳转</button>
<div>
<!-- 嵌套路由 子路由 -->
<RouterView></RouterView>
</div>
</div>
</template>
<script lang="ts" setup name="News">
import { RouterView,RouterLink,useRouter } from 'vue-router';
const router=useRouter(); //路由器
let id=23
let title='对话框'
function routpath(){
//router.push("/news")
//params
//push有历史记录 replace没有历史记录
router.replace({
name:'xiangqing',params:{id,title}
})
// router.push({
// name:'xiangqing',params:{id,title}
// })
//query
// router.push({
// name:'xiangqing',query:{id,title}
// })
}
</script>
Detal.vue 接收参数
javascript
<template>
<div>
Detal
</div>
</template>
<script lang="ts" setup name="Detal">
// import{toRefs} from 'vue'
// import { useRoute} from 'vue-router'
// let route=useRoute();
//直接解构会失去响应式 需要加toRefs
// let { query} =toRefs(route)
// console.log(query)
// console.log(route.params)
//路由规则需要配置 props:true 就可以直接接收 上面的代码可以省略
let d= defineProps(['id','title','count'])
console.log(d)
</script>
状态管理 vue2是vuex vue3是pinia
npm i pinia
main.ts
javascript
import { createApp } from 'vue' //引入createApp用于创建应用
import App from './App.vue' //引入App根组件
//引入路由器
import router from './router'
//引入pinia
import {createPinia} from 'pinia'
//第二步:创建pinia
const pinia=createPinia()
const app=createApp(App); //创建前端应用
//第三步:安装pinia
app.use(pinia)
//使用路由器
app.use(router);
app.mount('#app'); //所有组件的根组件,挂载到id为app的容器里面
store/count.ts
javascript
import {defineStore} from 'pinia'
//使用和计数相关的仓库 useCountStore
//对外暴露 count分类id
export const useCountStore=defineStore('count',{
//要求state写成一个函数
//真正存储数据的地方
state(){
return {
sum:6,
school:"fhskkh",
address:"北京"
}
},
//actions里面放置的是一个一个的方法,用于响应组件中的'动作'
actions:{
increment(value:number){
console.log("increment被调用了",value)
if(this.sum < 10){ //这里就体现了 意义所在了 可以做限制
//修改数据 (this是当前的store)
this.sum += value
}
}
},
getters:{
//2种写法 一个是state 不用this就可以写成箭头函数
bigSum(state){
return state.sum * 10
},
//一个是this
upperSchool():string{
return this.school.toUpperCase()
}
}
})
store/loveTalk.ts
javascript
import {defineStore} from 'pinia'
import axios from 'axios';
import { nanoid} from 'nanoid'
//使用和计数相关的仓库 useCountStore
//对外暴露 talk分类id
//第一种写法
// export const useTalkStore=defineStore('talk',{
// //要求state写成一个函数
// //真正存储数据的地方
// state(){
// return {
// talkList:JSON.parse(localStorage.getItem("talkList") as string) || []
// }
// },
// actions:{
// async getATalk(){
// //解构 再解构 然后重命名 下面的这行写法是:连续解构赋值+重命名
// let {data:{content:title}}=await axios.get("https://api.uomg.com/api/rand.qinghua?format=json")
// let obj={id:nanoid(),title}
// this.talkList.unshift(obj)
// }
// }
// })
//第二种写法 可以写成函数 组合式
import { reactive} from 'vue'
export const useTalkStore=defineStore('talk',()=>{
// talkList就是state
const talkList=reactive(
JSON.parse(localStorage.getItem("talkList") as string) || []
)
// getATalk函数相当于action
async function getATalk(){
//解构 再解构 然后重命名 下面的这行写法是:连续解构赋值+重命名
let {data:{content:title}}=await axios.get("https://api.uomg.com/api/rand.qinghua?format=json")
let obj={id:nanoid(),title}
talkList.unshift(obj)
}
return {talkList,getATalk}
})
获取数据 修改数据
Count.vue
javascript
<template>
<h2>当前求和为:{{ sum }}</h2>
<h2>{{ school }} /{{ address }}</h2>
<h2>{{ bigSum }}/{{ upperSchool }}</h2>
<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="minum">减</button>
</template>
<script lang="ts" setup name="Count">
import {ref} from 'vue'
import {storeToRefs} from 'pinia'
//引入 useCountStore
import {useCountStore} from '../store/count'
//使用 useCountStore 得到一个专门保存count相关的store
const countStore=useCountStore()
//storeToRefs只会关注sotre中数据,不会对方法进行ref包裹
let {sum,school,address,bigSum,upperSchool} =storeToRefs(countStore)
//获取数据 以下两种方式都可以拿到state中的数据
// console.log(countStore.sum)
// console.log(countStore.$state.sum)
let n=ref(1)
function add(){
//直接就可以修改数据 第一种修改方式
// countStore.sum += n.value
//第二种修改方式 批量修改
// countStore.$patch({
// sum:888,
// school:'封疆大吏',
// address:"学历"
// })
//第三种修改方式
countStore.increment(n.value)
}
function minum(){
countStore.sum -= n.value
}
</script>
LoveTalk.vue
javascript
<template>
<div>
<button @click="getLove">获取一句土味情话</button>
<ul>
<li v-for="item in talkList" :key="item.id">{{ item.title }}</li>
</ul>
</div>
</template>
<script lang="ts" setup name="LoveTalk">
import {storeToRefs} from 'pinia'
//引入
import {useTalkStore} from '../store/loveTalk'
//调用
const talkListStore=useTalkStore()
//storeToRefs只会关注sotre中数据,不会对方法进行ref包裹
let {talkList}=storeToRefs(talkListStore)
//获取数据 以下两种方式都可以拿到state中的数据
//console.log(talkListStore.talkList)
//mutate本次修改的信息 state修改的数据
//订阅 $subscribe
talkListStore.$subscribe((mutate,state)=>{
console.log("talkListStore里面保存的数据发生了变化",mutate,state)
//存储本地 刷新不丢失
localStorage.setItem("talkList",JSON.stringify(state.talkList))
})
async function getLove(){
talkListStore.getATalk()
}
</script>
组件通讯方式props 很重要
若父传子:属性值是非函数
若子传父:属性值是函数
自定义事件:子传父
父组件
javascript
<template>
<div>
<h2>父组件</h2>
<h3>父的车:{{ car }}</h3>
<h3 v-if="toy">子给的玩具:{{ toy }}</h3>
<!-- 传递数据 haha自定义事件名 给子组件绑定事件 -->
<Child :car="car" :sendToy="getToy" @haha="xyz" />
</div>
</template>
<script lang="ts" setup name="Father">
import Child from '../components/Child.vue'
import {ref} from 'vue'
//数据
let car=ref("奔驰")
let toy=ref("")
//方法
function getToy(value:string){
console.log("父",value)
toy.value=value
}
//用于保存传递过来的玩具
function xyz(value:any){
console.log("xyz",value)
toy.value=value
}
</script>
子组件
javascript
<template>
<div>
<h2>子组件</h2>
<h3>子的玩具:{{ toy }}</h3>
<h4>父给的车:{{ car }}</h4>
<button @click="sendToy(toy)">把玩具给父亲</button>
<button @click="emitabe('haha',toy)">哈哈</button>
</div>
</template>
<script setup lang="ts" name="Child">
import {ref,onMounted} from 'vue'
//数据
let toy=ref("奥特曼")
// 接收数据
defineProps(["car","sendToy"])
//声明事件 也是接收数组 规范命名是emit
const emitabe= defineEmits(['haha'])
onMounted(()=>{
setTimeout(()=>{
//调用
emitabe('haha',666)
},3000)
})
</script>
mitt 可以任意组件通讯
安装 npm i mitt
utils/emitter.ts
javascript
//引入mitt
import mitt from "mitt";
//调用 mitt得到emitter,emitter能绑事件 触发事件
const emitter =mitt()
//绑定事件on
// emitter.on('test1',()=>{
// console.log('test1被调用了')
// })
// emitter.on('test2',()=>{
// console.log('test2被调用了')
// })
// //触发事件emit
// setTimeout(()=>{
// emitter.emit('test1')
// emitter.emit('test2')
// },2000)
// //解绑事件off
// setTimeout(()=>{
// emitter.off("test1")
// emitter.all.clear() //清空所有事件 解绑all.clear()
// },3000)
//暴露emitter
export default emitter
父组件
javascript
<template>
<div>
<h2>父组件</h2>
<Child1></Child1>
<Child2></Child2>
</div>
</template>
<script setup lang="ts" name="Defel">
import Child1 from '../components/Child1.vue';
import Child2 from '../components/Child2.vue';
</script>
子组件1
javascript
<template>
<div>
<h2>子组件1</h2>
<h3>玩具:{{toy}}</h3>
<button @click="toysend">玩具给弟弟</button>
</div>
</template>
<script setup lang="ts" name="Child1">
import { ref } from 'vue';
import emitter from '../utils/emitter';
let toy=ref('奥特曼')
//触发事件emit
function toysend(){
emitter.emit("send-toy",toy.value)
}
</script>
子组件2
javascript
<template>
<div>
<h2>子组件2</h2>
<h3>电脑:{{computer}}</h3>
<h3 v-if="toy">哥哥给的玩具:{{ toy }}</h3>
</div>
</template>
<script setup lang="ts" name="Child2">
import { ref,onUnmounted } from 'vue';
import emitter from '../utils/emitter';
//数据
let computer=ref('联想')
let toy=ref()
//给emitter绑定send-toy事件
emitter.on("send-toy",(value)=>{
console.log('send-toy',value)
toy.value=value
})
//在组件卸载时 解绑send-toy事件
onUnmounted(()=>{
//解绑事件off
emitter.off("send-toy")
})
</script>
v-mode UI组件库底层原理
既能父传子 也能子传父
e v e n t 到底是啥?啥时候能 . t a r g e t 对于原生事件, event到底是啥?啥时候能 .target 对于原生事件, event到底是啥?啥时候能.target对于原生事件,event就是事件对象 =》能.target
对于自定义事件,$event就是触发事件时,所传递的数据=》不能.target
父组件
javascript
<template>
<div>
<h2>父组件</h2>
<h4>{{ username }}</h4>
<h4>{{ password }}</h4>
<!-- v-model用在html标签上 底层原理就是一个动态的value值 配合@input事件-->
<!-- <input type="text" v-model="username"> -->
<!-- 下面的是本质原生的input -->
<!-- <input type="text" :value="username" @input="username=(<HTMLInputElement>$event.target).value"> -->
<!-- v-model用在组件标签上 -->
<!-- <AtgInput v-model="username" /> -->
<!-- 下面的是本质vue3的 底层事件和值 update:modelValue就是一个事件名 -->
<!-- <AtgInput :modelValue="username" @update:modelValue="username=$event" /> -->
<!-- 修改modelValue -->
<AtgInput v-model:ming="username" v-model:mima="password" />
</div>
</template>
<script setup lang="ts" name="Defel">
import AtgInput from '../components/AtgInput.vue'
import { ref } from 'vue';
let username=ref('zhangsan')
let password=ref('123456')
</script>
子组件
javascript
<template>
<div>
<!-- 用户名1: <input type="text" :value="modelValue" @input="emit('update:modelValue',(<HTMLInputElement>$event.target).value)">
-->
用户名2: <input type="text" :value="ming" @input="emit('update:ming',(<HTMLInputElement>$event.target).value)">
密码: <input type="text" :value="mima" @input="emit('update:mima',(<HTMLInputElement>$event.target).value)">
</div>
</template>
<script setup lang="ts" name="AtgInput">
// //接收参数
// defineProps(["modelValue"])
// //接收事件名
// const emit= defineEmits(['update:modelValue'])
//接收参数
defineProps(["ming",'mima'])
//接收事件名
const emit= defineEmits(['update:ming','update:mima'])
</script>
$attrs
用于实现当前组件的父组件,向当前组件的子组件通信(祖--->孙)
打扰了中间人 子组件
具体说明: a t t r s 是一个对象,包含所有父组件传入的标签属性。注意: attrs是一个对象,包含所有父组件传入的标签属性。 注意: attrs是一个对象,包含所有父组件传入的标签属性。注意:attrs会自动排除props中声明的属性(可以人为声明过的props被子组件自己'消费'了
父组件
javascript
<template>
<div>
<h2>父组件</h2>
<h3>a:{{ a }}</h3>
<h3>b:{{ b }}</h3>
<h3>c:{{ c }}</h3>
<h3>d:{{ d }}</h3>
<!--v-bind="{x:100,y:200}" 就相当于 :x="100" :y="200" -->
<Father :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA" />
</div>
</template>
<script setup lang="ts" name="Father">
import Father from '../components/Father.vue';
import { ref } from 'vue';
let a =ref(1);
let b =ref(2);
let c =ref(3);
let d =ref(4);
function updateA(value:number){
a.value += value
}
</script>
子组件
javascript
<template>
<div>
<h2>子组件</h2>
<h4>父组件的a:{{ a }}</h4>
<!-- 父组件传了 但是没有接收 -->
<h4>其他:{{ $attrs }}</h4>
<GranChild v-bind="$attrs"/>
</div>
</template>
<script setup lang="ts" name="Child4">
import GranChild from './GranChild.vue';
//收取数据
defineProps(["a"])
</script>
孙组件
javascript
<template>
<div>
<h2>孙组件</h2>
<h4>祖的b:{{ b }}</h4>
<h4>祖的c:{{ c }}</h4>
<button @click="updateA(6)">点我将爷爷那的a更新</button>
</div>
</template>
<script setup lang="ts" name="GranChild">
//接收数据
defineProps(['a','b','c','d','updateA'])
</script>
$refs
用于:父---》子 :值为对象,包含所有被ref属性标识的DOM元素或组件实例
$parent
用于:子----》父 :值为对象,当前组件的父组件实例对象
父组件
javascript
<template>
<h2>父组件</h2>
<h4>房产:{{house}}</h4>
<button @click="changeToy">修改chilid1的玩具</button>
<button @click="changeComputer">修改chilid2的电脑</button>
<button @click="changeAll($refs)">获取所有的子组件实例对象</button>
<Child1 ref="c1" />
<Child2 ref="c2" />
</template>
<script setup lang="ts" name="Father">
import Child1 from '../components/Child1.vue';
import Child2 from '../components/Child2.vue';
import { ref } from 'vue';
//数据
let house=ref(4)
let c1=ref()
let c2=ref()
function changeToy(){
console.log(c1.value)
c1.value.toy="小猪佩奇"
}
function changeComputer(){
c2.value.computer="pad"
}
function changeAll(x:any){
//遍历对象
for(let key1 in x){
console.log(x[key1])
//让所有孩子的书变多
x[key1].book +=3
}
}
//把数据交给外部
defineExpose({house})
</script>
子组件1
javascript
<template>
<h2>子组件1</h2>
<h4>玩具:{{toy}}</h4>
<h4>书籍:{{book}}</h4>
<button @click="minuHouse($parent)">干掉父亲的一套房产</button>
</template>
<script setup lang="ts" name="Child1">
import { ref } from 'vue';
let toy=ref("奥特曼")
let book=ref(3)
//把数据交给外部
defineExpose({toy,book})
function minuHouse(y:any){
console.log(y)
y.house -=1
}
</script>
子组件2
javascript
<template>
<h2>子组件2</h2>
<h4>电脑:{{computer}}</h4>
<h4>书籍:{{book}}</h4>
</template>
<script setup lang="ts" name="Child2">
import { ref } from 'vue';
let computer=ref("联想")
let book=ref(6)
//把数据交给外部
defineExpose({computer,book})
</script>
provide inject
组件通信 祖孙中间通讯
存在的目的,是子孙之间 是不打扰子的
父组件
javascript
<template>
<h2>父组件</h2>
<h4>银子:{{money}}</h4>
<h4>车:一辆{{car.brand}}车,价值{{ car.prcie }}万元</h4>
<Child />
</template>
<script setup lang="ts" name="Father">
import Child from '../components/Child.vue';
import { ref,reactive,provide } from 'vue';
let money=ref(100)
let car=reactive({
brand:"奔驰",
prcie:100
})
function updateMoney(value:number){
money.value -=value
}
//provide 两个参数 1.名字 2.值
//向后代提供数据
provide('qian',{money,updateMoney})
provide('che',car)
</script>
子组件
javascript
<template>
<div>
<h2>子组件</h2>
<Child1 />
</div>
</template>
<script setup lang="ts" name="Child">
import Child1 from './Child1.vue';
</script>
孙组件
javascript
<template>
<h2>孙组件</h2>
<h4>银子:{{ money }}</h4>
<h4>车:一辆{{car.brand}}车,价值{{ car.prcie }}万元</h4>
<button @click="updateMoney(6)">修改钱</button>
</template>
<script setup lang="ts" name="Child1">
import { inject } from 'vue';
//inject 注入参数 1.名字,2.默认值
let {money,updateMoney}=inject("qian",{money:0,updateMoney:(x:number)=>{}})
let car=inject("che",{brand:'未知',prcie:0})
</script>
插槽slot
默认插槽
父组件
javascript
<template>
<h2>父组件</h2>
<div style="display: flex;justify-content: space-between;">
<Child title="热门游戏" >
<h4>上党课</h4>
</Child>
<Child title="今日美食" >
<img :src="imgUrl" style="height: 100px;" >
</Child>
<Child title="今日影视" >
<video :src="videoUrl" style="width: 100px;"></video>
</Child>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from '../components/Child.vue';
import { ref } from 'vue';
let imgUrl=ref("https://z1.ax1x.com/2023/11/19/piNxLo4.jpg")
let videoUrl=ref("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
</script>
子组件
javascript
<template>
<div>
<h3>{{ title }}</h3>
<!-- 插槽 -->
<slot>默认内容</slot>
</div>
</template>
<script setup lang="ts" name="Child">
defineProps(["title"])
</script>
具名插槽 具有名字的插槽 有默认名字 name="default"
父组件
javascript
<template>
<h2>父组件</h2>
<div style="display: flex;justify-content: space-between;">
<Child title="热门游戏" v-slot:s2>
<h4 >上党课</h4>
</Child>
<Child title="今日美食" >
<!-- 有名称需要加template v-slot:s2这个只能加在组件上和template上,不能加在标签上 -->
<template #s2>
<img :src="imgUrl" style="height: 100px;" >
</template>
<!-- 简写#s1 -->
<template #s1>
<h4 >上党课</h4>
</template>
</Child>
<Child title="今日影视" >
<template v-slot:s2>
<video :src="videoUrl" style="width: 100px;"></video>
</template>
</Child>
</div>
</template>
<script setup lang="ts" name="Father">
import Child from '../components/Child.vue';
import { ref } from 'vue';
let imgUrl=ref("https://z1.ax1x.com/2023/11/19/piNxLo4.jpg")
let videoUrl=ref("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
</script>
子组件
javascript
<template>
<div>
<h3>{{ title }}</h3>
<!-- 插槽 -->
<slot name="s1">默认内容1</slot>
<slot name="s2">默认内容2</slot>
</div>
</template>
<script setup lang="ts" name="Child">
defineProps(["title"])
</script>
作用域插槽
父组件
javascript
<template>
<h2>父组件 数据在子列表</h2>
<Child>
<!-- a 是子组件 slot上面所有的数据 -->
<template v-slot:qwe="a">
<ul>
<li v-for="g in a.youxi" :key="g.id">{{ g.name }}</li>
</ul>
</template>
</Child>
<Child>
<template #qwe="a">
<ol>
<li v-for="g in a.youxi" :key="g.id">{{ g.name }}</li>
</ol>
</template>
</Child>
<Child>
<!-- 可以直接解构 -->
<template v-slot:qwe="{youxi}">
<h3 v-for="g in youxi" :key="g.id">{{ g.name }}</h3>
</template>
</Child>
</template>
<script setup lang="ts" name="Father">
import Child from '../components/Child.vue';
</script>
子组件
javascript
<template>
<div>
<slot name="qwe" :youxi="games" x="哈哈" y="你好"></slot>
</div>
</template>
<script setup lang="ts" name="Child">
import { reactive } from 'vue';
let games=reactive([
{id:'dsf21',name:"英雄联盟"},
{id:'dsf22',name:"王者荣耀"},
{id:'dsf23',name:"红色警戒"}
])
</script>
其他API 比较常用的
浅层次的ref shallowRef 只负责第一层的响应式
浅层次的reactive shallowReactive 只负责第一层的响应式
通过使用shallowRef() 和shallowReactive()来绕开深度响应,浅层式API创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提升性能。
readonly 只读 不能修改数据 对数据的一种保护
javascript
<template>
<div>
{{ sum1 }}
</div>
</template>
<script setup lang="ts" name="Father">
import { ref,readonly } from 'vue';
let sum1=ref(0)
let sum2=readonly(sum1); //只读属性 不能修改值
</script>
shallowReadonly 浅层次的只读 只负责第一层的只读
toRaw
作用:用于获取一个响应式对象的原始数据,toRow放回的对象不再是响应式的,不会触发视图更新。
markRaw 作用:标记一个对象,使其永远不会变成响应式的。
javascript
<template>
<div>
{{ person.name }} /{{ person.age }}
</div>
</template>
<script setup lang="ts" name="Father">
import { reactive,toRaw ,markRaw} from 'vue';
let person=reactive({
name:'tony',
age:18
})
//把响应式对象 变成原始的对象
let person2=toRaw(person)
console.log("响应式数据",person)
console.log("原始数据",person2)
let car=markRaw({brand:'本想',piarc:100})
let car2=reactive(car)
console.log(car)
console.log(car2)
</script>
自定义ref customRef 很重要 防抖
作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行逻辑控制。
主要的是 track() trigger()
javascript
<template>
<div>
{{msg}}
<br />
<input type="text" v-model="msg">
</div>
</template>
<script setup lang="ts" name="Father">
import { ref,customRef} from 'vue';
//使用vue提供的默认ref定义响应式数据,数据一变,页面就更新
let msg1=ref('你好')
//数据一变 等一秒更新 使用vue提供的customRef定义响应式数据
let initValue="你好"
let timer:number
// track跟踪 trigger触发
let msg=customRef((track,trigger)=>{
return {
// get 何时调用 ?---msg被读取时候调用
get() {
track() //告诉vue数据msg很重要,你要对msg进行持续关注。一旦msg变化就去更新
// console.log('get')
return initValue
},
// set 何时调用 ?msg被修改时
set(value) {
clearTimeout(timer)
timer= setTimeout(() => {
// console.log('set',value)
initValue=value
trigger() //通知vue一下数据msg变化了
}, 1000);
}
}
})
</script>
封装成hooks
hooks/useMsgRef.ts
javascript
import {customRef} from 'vue';
export default function(initValue:string,delay:number){
//数据一变 等一秒更新 使用vue提供的customRef定义响应式数据
let timer:number
// track跟踪 trigger触发
let msg=customRef((track,trigger)=>{
return {
// get 何时调用 ?---msg被读取时候调用
get() {
track() //告诉vue数据msg很重要,你要对msg进行持续关注。一旦msg变化就去更新
// console.log('get')
return initValue
},
// set 何时调用 ?msg被修改时
set(value) {
clearTimeout(timer)
timer= setTimeout(() => {
// console.log('set',value)
initValue=value
trigger() //通知vue一下数据msg变化了
}, delay);
}
}
})
return { msg}
}
调用
javascript
<template>
<div>
{{msg}}
<br />
<input type="text" v-model="msg">
</div>
</template>
<script setup lang="ts" name="Father">
import useMsgRef from '../hooks/useMsgRef';
//使用useMsgRef来定义一个响应式数据且有延迟效果
let {msg} =useMsgRef('你好',2000)
</script>
vue3 新组件
teleport 传送门 模态框使用 to 是以哪个为定位
是一种能够将我们的组件html结构移动到指定位置的技术。
javascript
<template>
<button @click="open" v-if="!isShow">展示弹窗</button>
<!-- to 就是 一会弹窗 塞到哪个里面去 body里面 -->
<!-- 因为css的filter 滤镜会影响定位 所以要使用teleport包裹-->
<teleport to="body">
<div v-if="isShow">
<h2>标题</h2>
<h3>内容</h3>
<button @click="isShow=false">点击关闭弹窗</button>
</div>
</teleport>
</template>
<script setup lang="ts" name="Child">
import { ref } from 'vue';
let isShow=ref(false)
function open(){
isShow.value=true
}
</script>
Suspense 等待异步组件时渲染一些额外内容,让应用有更好的用户体验
父组件
javascript
<template>
<div>
<h2>APP组件</h2>
<Suspense>
<!-- 请求完成就会显示这个 -->
<template #default>
<!-- 如果子组件有异步任务的话 就需要Suspense去包裹一个template 然后在把组件丢进去 -->
<Child />
</template>
<!-- 网速慢 没有请求过来数据 就会加载这个 -->
<template #fallback>
<h2>加载中...</h2>
</template>
</Suspense>
</div>
</template>
<script setup lang="ts" name="Father">
import { Suspense } from 'vue';
import Child from '../components/Child.vue';
</script>
子组件
javascript
<template>
<h2>子组件</h2>
</template>
<script setup lang="ts" name="Child">
import axios from 'axios';
let {data:{content}}=await axios.get("https://api.uomg.com/api/rand.qinghua?format=json")
console.log(content)
</script>
全局组件
注册全局组件app.component
全局属性 app.config.globalProperties
注册全局指令 app.directive
挂载 app.mount
卸载 app.unmount
安装插件 app.use
main.ts
javascript
import { createApp } from 'vue' //引入createApp用于创建应用
import App from './App.vue' //引入App根组件
//引入路由器
import router from './router'
import Child from './components/Child.vue'
//引入pinia
import {createPinia} from 'pinia'
//第二步:创建pinia
const pinia=createPinia()
const app=createApp(App); //创建前端应用
//注册全局组件
app.component('hello',Child)
//全局属性
app.config.globalProperties.x=99 //建议少用
//去除ts的报警
declare module 'vue' {
interface ComponentCustomProperties {
x:number
}
}
//注册全局指令
app.directive('haha',(element,{value})=>{
element.innerText +=value
element.style.color ='green'
})
//第三步:安装pinia
app.use(pinia)
//使用路由器
app.use(router);
app.mount('#app'); //所有组件的根组件,挂载到id为app的容器里面
// setTimeout(()=>{
// app.unmount() //卸载
// },2000)
组件使用
javascript
<template>
<div>
<h2>APP组件{{ x }}</h2>
<hello/>
<h4 v-haha="sum">好开心</h4>
</div>
</template>
<script setup lang="ts" name="Father">
import { ref } from 'vue';
let sum =ref(1)
</script>
非兼容性改变 vue2和vue3的区别
过渡类名 v-enter 修改为v-enter-from,过渡类名 v-leave修改为v-leave-from
keyCode 作为 v-on修饰符的支持
v-model 指令在组件上的使用已经被重新设计,替换掉了v-bind.sync
v-if和 v-for 在同一个元素身上使用时的优先级发生了变化
移除了 o n , on, on,off和 o n c e 实例方法移除了过滤器 f i l t e r 移除了 once实例方法 移除了过滤器 filter 移除了 once实例方法移除了过滤器filter移除了children实例propert