vue3+vite

安装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里面的数据的

<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

<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配置文件

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” 就可以命名组件名了

<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

<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

<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计算属性

<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=str1name.value=str2console.log('set',val)}})function changeFullName(){username.value="li-si"}    
</script>

watch 很重要

作用:监视数据的变化(和vue2中的watch作用一致)
特点:vue3中的watch只能监视以下四种数据:
1.ref定义的数据。
2.reactive定义的数据
3.函数返回一个值
4.一个包含上述内容的数组

情况一:监视ref定义的基本类型数据:直接写数据名即可,监视的是其value值的改变。

<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是旧值,因为不是同一个对象了。

<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定义的 对象类型 数据,且默认开启了深度监视。

<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.若该属性值依然是 对象类型,可直接编,也可写成函数,不过建议写成函数。
结论:监视的要是对象里的属性,那么最好写函数式,注意点:若是对象监视的是地址值,需要关注对象内部,需要手动开启深度监视。

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

情况五:

<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 :不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性)

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

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

用在组件标签上,获取的是组件实例对象。

<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定义接口类型

//定义一个接口,用于限制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

<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 宏函数不需要引入 也不会报错
子组件

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

父组件

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

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

卸载子组件

<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

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

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

组件引用

<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

//创建一个路由器,并暴露出去
//第一步:引入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

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

<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

<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 接收参数

<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

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

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},//一个是thisupperSchool():string{return this.school.toUpperCase()}}
})

store/loveTalk.ts

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就是stateconst  talkList=reactive(JSON.parse(localStorage.getItem("talkList") as string) || [])// getATalk函数相当于actionasync 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

<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

<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 很重要

若父传子:属性值是非函数
若子传父:属性值是函数
自定义事件:子传父
父组件

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

子组件

<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

//引入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

父组件

<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

<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

<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(()=>{//解绑事件offemitter.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
父组件

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

子组件

<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被子组件自己‘消费’了
父组件

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

子组件

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

孙组件

<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

用于:子----》父 :值为对象,当前组件的父组件实例对象

父组件

<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

<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

<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

组件通信 祖孙中间通讯
存在的目的,是子孙之间 是不打扰子的

父组件

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

子组件

<template><div><h2>子组件</h2><Child1 /></div>
</template><script setup lang="ts" name="Child">import Child1 from './Child1.vue';
</script>

孙组件

<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

默认插槽
父组件

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

子组件

<template><div><h3>{{ title }}</h3><!-- 插槽 --><slot>默认内容</slot></div>
</template><script setup lang="ts" name="Child">
defineProps(["title"])
</script>

具名插槽 具有名字的插槽 有默认名字 name=“default”
父组件

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

子组件

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

作用域插槽
父组件

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

子组件

<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 只读 不能修改数据 对数据的一种保护

<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 作用:标记一个对象,使其永远不会变成响应式的。

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

<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=valuetrigger() //通知vue一下数据msg变化了}, 1000);}}
})</script>

封装成hooks
hooks/useMsgRef.ts

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=valuetrigger() //通知vue一下数据msg变化了}, delay);}}
})
return { msg}
}

调用

<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结构移动到指定位置的技术。

<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 等待异步组件时渲染一些额外内容,让应用有更好的用户体验

父组件

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

子组件

<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

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 +=valueelement.style.color ='green'
})//第三步:安装pinia
app.use(pinia)
//使用路由器
app.use(router);
app.mount('#app');  //所有组件的根组件,挂载到id为app的容器里面// setTimeout(()=>{
//   app.unmount() //卸载
// },2000)

组件使用

<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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/732435.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue 下载的插件从哪里上传?npm发布插件详细记录

文章参考&#xff1a; 参考文章一&#xff1a; 封装vue插件并发布到npm详细步骤_vue-cli 封装插件-CSDN博客 参考文章二&#xff1a; npm发布vue插件步骤、组件、package、adduser、publish、getElementsByClassName、important、export、default、target、dest_export default…

智能驾驶规划控制理论学习08-自动驾驶控制模块(轨迹跟踪)

目录 一、基于几何的轨迹跟踪方法 1、基本思想 2、纯追踪 3、Stanly Method 二、PID控制器 三、LQR&#xff08;Linear Quadratic Regulator&#xff09; 1、基本思想 2、LQR解法 3、案例学习 基于LQR的路径跟踪 基于LQR的速度跟踪 4、MPC&#xff08;Mode…

day59 线程

创建线程的第二种方式 实现接口Runnable 重写run方法 创建线程的第三种方式 java.util.concurrent下的Callable重写call()方法 java.util.concurrent.FutureTask 创建线程类对象 获取返回值 线程的四种生命周期 线程的优先级1-10 default为5&#xff0c;优先级越高&#xff0c…

基于梯度统计学的渐变型亮缝识别算法

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 一、场景痛点 在图像处理相关的实际工程中&#xff0c;会出现各式各样的现实复杂问题&#xff0c;有的是因为机械设计导致&#x…

【洛谷 P8668】[蓝桥杯 2018 省 B] 螺旋折线 题解(数学+平面几何)

[蓝桥杯 2018 省 B] 螺旋折线 题目描述 如图所示的螺旋折线经过平面上所有整点恰好一次。 对于整点 ( X , Y ) (X, Y) (X,Y)&#xff0c;我们定义它到原点的距离 dis ( X , Y ) \text{dis}(X, Y) dis(X,Y) 是从原点到 ( X , Y ) (X, Y) (X,Y) 的螺旋折线段的长度。 例如 …

新手做抖音小店,一定要避坑的五件事,非常重要!

大家好&#xff0c;我是电商糖果 现在做抖音小店已经进入一个稳定期&#xff0c;商家只要掌握正确的做店流程&#xff0c;然后把选品做好。 就可以将店铺做起来。 但这一切的前提是我们用对方法&#xff0c;这个很关键&#xff0c;因为这决定了你后面店铺的发展。 这篇文章…

蓝桥杯练习系统(算法训练)ALGO-981 过河马

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 在那个过河卒逃过了马的控制以超级超级多的走法走到了终点之后&#xff0c;这匹马表示它不开心了……   于是&#xff0c…

C++、MFC中操作excel时,CRange中get_Rows()、get_Columns()及get_Count()函数的用法及区别是什么?

在C、MFC中操作Excel时&#xff0c;CRange类中的get_Rows()、get_Columns()和get_Count()函数都是用于获取指定范围的行数、列数或单元格数量的函数&#xff0c;但它们的具体用法和区别如下&#xff1a; get_Rows() 用法&#xff1a;LPDISPATCH get_Rows();功能&#xff1a;返回…

Java教程:RabbitMq讲解与SpringBoot项目如何对接RabbitMq实现生产者与消费者

在往期文章中&#xff0c;我们讲了如何在Windows与Linux环境下安装RabbitMq服务&#xff0c;并访问Web管理端。 有很多同学其实并不知道RabbitMq是用来干嘛的&#xff0c;它起到一个什么作用&#xff0c;并且如何在常见的SpringBoot项目中集成mq并实现消息收发&#xff0c;本章…

Nginx实现高并发

注&#xff1a;文章是4年前在自己网站上写的&#xff0c;迁移过来了。现在看我之前写的这篇文章&#xff0c;描述得不是特别详细&#xff0c;但描述了Nginx的整体架构思想。如果对Nginx玩得透得或者想了解深入的&#xff0c;可以在网上找找其他的文章。 ......................…

day17_订单(结算,提交订单,支付页,立即购买,我的订单)

文章目录 订单模块1 结算1.1 需求说明1.2 获取用户地址1.2.1 UserAddress1.2.2 UserAddressController1.2.3 UserAddressService1.2.4 UserAddressMapper1.2.5 UserAddressMapper.xml 1.3 获取购物项数据1.3.1 CartController1.3.2 CartService1.3.3 openFeign接口定义 1.4 环境…

NIFI从Oracle11G同步数据到Mysql_亲测可用_解决数据重复_数据跟源表不一致的问题---大数据之Nifi工作笔记0065

首先来看一下整体的流程: 可以看到了用到了上面的这些处理器,然后我们主要看看,这里之前 同步的时候,总是出现重复的数据,奇怪. 比如源表中只有166条数据,但是同步过去以后变成了11万条数据了. ${db.table.name:equals(table1):or(${db.table.name:equals(table2)})} 可以看…

【精选好刊】JCR2区SCI仅17天上线见刊,最后10篇版面!

录用案例 JCR2区地质环境类SCI&EI (进展顺) 【期刊简介】IF&#xff1a;3.0-4.0&#xff0c;JCR2区&#xff0c;中科院3/4区&#xff1b; 【检索情况】SCI&EI双检&#xff1b; 【征稿领域】地球观测、环境监测和管理相关或结合研究均可&#xff1b; 【案例分享】重…

[数据集][目标检测]遥感图像游泳池检测数据集VOC+YOLO格式288张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;288 标注数量(xml文件个数)&#xff1a;288 标注数量(txt文件个数)&#xff1a;288 标注类别…

前端面试练习24.3.8

防抖和节流 防抖&#xff08;Debouncing&#xff09;&#xff1a; 防抖是指在短时间内连续触发同一事件时&#xff0c;只执行最后一次触发的事件处理函数。 在实际应用中&#xff0c;常常用于处理用户输入的搜索框或者滚动事件。例如&#xff0c;当用户连续输入搜索关键词时&am…

macos系统中redis如何设置密码

在 macOS 上设置 Redis 密码的步骤与在其他操作系统上大致相同&#xff0c;关键是要找到并编辑 Redis 配置文件&#xff0c;然后重启 Redis 服务。以下是详细步骤&#xff1a; 找到 Redis 配置文件&#xff1a;如果你是通过 Homebrew 安装的 Redis&#xff0c;配置文件通常位于…

业务代码中如何使用装饰器模式?

装饰器模式&#xff08;Decorator Pattern&#xff09;介绍 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;我们可以动态地给一个对象添加额外的职责。而不是通过继承增加子类的方式来扩展对象的功能&#xff0c;装饰器模式使用组合的…

[N1CTF 2018]eating_cms 不会编程的崽

题倒是不难&#xff0c;但是实在是恶心到了。 上来就是登录框&#xff0c;页面源代码也没什么特别的。寻思抓包看一下&#xff0c;数据包直接返回了sql查询语句。到以为是sql注入的题目&#xff0c;直到我看到了单引号被转义。。。挺抽象&#xff0c;似乎sql语句过滤很严格。又…

Java基础知识点之思维导图

一、走进Java编程世界 二、变量常量和运算符 三、if选择结构 四、switch选择结构 五、while循环结构 六、for循环结构 七、数组 八、类与对象 九、深入循环结构 十、类的无参方法 十一、类的带参方法 十二、字符串

读已提交隔离级别下竟然有间隙锁

业务背景 广告主痛点的为进行一次全媒体联合投放&#xff0c;若投放10个媒体&#xff0c;需要制作和上传10个创意、50张不同尺寸和出血区要求的图片和视频素材、近100个元素&#xff0c;投放成本极高。这也是制约部分用户使用新产品投放的原因。 因此进行升级。以三个创意为例…