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

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

相关推荐
前端小白从0开始32 分钟前
Vue3项目实现WPS文件预览和内容回填功能
前端·javascript·vue.js·html5·wps·文档回填·文档在线预览
難釋懷1 小时前
Vue解决开发环境 Ajax 跨域问题
前端·vue.js·ajax
挑战者6668882 小时前
vue入门环境搭建及demo运行
前端·javascript·vue.js
程序猿ZhangSir4 小时前
Vue3 项目的基本架构解读
前端·javascript·vue.js
亲亲小宝宝鸭4 小时前
写了两个小需求,终于搞清楚了表格合并
前端·vue.js
Face5 小时前
路由Vue-router 及 异步组件
前端·javascript·vue.js
风之舞_yjf6 小时前
Vue基础(14)_列表过滤、列表排序
前端·javascript·vue.js
疯狂的沙粒7 小时前
uni-app 项目支持 vue 3.0 详解及版本升级方案?
前端·vue.js·uni-app
Lhuu(重开版7 小时前
Vue:Ajax
vue.js·ajax·okhttp
国家不保护废物8 小时前
从刀耕火种到现代框架:DOM编程 vs Vue/React 进化史
前端·vue.js·react.js