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=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值的改变。

<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
        },
        //一个是this
        upperSchool():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就是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

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

<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=value
                trigger() //通知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=value
                trigger() //通知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 +=value
      element.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

相关推荐

  1. vue3+vite

    2024-03-10 00:24:04       18 阅读
  2. Vue3.0+vite vite.config.ts配置与env

    2024-03-10 00:24:04       17 阅读
  3. 配置vue3+vite+eslint+prettierrc项目

    2024-03-10 00:24:04       29 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-10 00:24:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-10 00:24:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-10 00:24:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-10 00:24:04       18 阅读

热门阅读

  1. python深拷贝和浅拷贝

    2024-03-10 00:24:04       24 阅读
  2. 如何快速的搭建一个小程序

    2024-03-10 00:24:04       23 阅读
  3. unity中摄像机跟随

    2024-03-10 00:24:04       21 阅读
  4. Error running ‘Attach debug to process‘

    2024-03-10 00:24:04       24 阅读
  5. BDD测试框架Cucumber学习

    2024-03-10 00:24:04       23 阅读
  6. C# 的一些好用的语法糖介绍

    2024-03-10 00:24:04       20 阅读
  7. linux脚本练习2-文件压缩删除

    2024-03-10 00:24:04       20 阅读
  8. 铭文资产是比特币生态破局者 or 短暂热点?

    2024-03-10 00:24:04       19 阅读
  9. 数据结构-二分查找

    2024-03-10 00:24:04       21 阅读
  10. 如何更好的理解设计模式之桥接模式

    2024-03-10 00:24:04       20 阅读
  11. golang从0到1实战系统四十:处理表单的输入

    2024-03-10 00:24:04       22 阅读