vue3(整合版)

news2024/9/22 1:24:58

创建第一个vue项目

1.安装node.js

cmd输入node查看是否安装成功

2.vscode开启一个终端,配置淘宝镜像

# 修改为淘宝镜像源
npm config set registry https://registry.npmmirror.com

输入如下命令创建第一个Vue项目

3.下载依赖,启动项目

访问5173端口

第一个Vue项目的目录结构

我们先打开main.ts,这是我们vue工程的入口文件

在这里创建vue程序,并把它挂载到id为app的标签下(ps:这里的App.vue是我们的根组件)

再打开index.html

这里将main.js引入,并挂载了app程序到这里。

最后打开App.vue,也就是我们的根组件

先不看其他编码的细节,我们可以大胆猜测HelloWorld.vue被挂到App.vue里面,证实了它是根组件。

简单验证

Hello.vue

<template>
    <h1>{{ msg }}</h1>
</template>
    
<script lang="ts" setup>
    let msg="Hello"
</script>
    
<style>
    
</style>

App.vue

<template>
<Hello></Hello>
<h1>{{msg}}</h1>
<Hello></Hello>
</template>

<script lang="ts" setup>
    import Hello from './components/Hello.vue';//引入Hello.vue
    let msg="App"
</script>

<style>
</style>

setup概述

setup语法糖

setup函数有一个语法糖,可以让我们把setup函数从script里面独立出去

<script setup lang="ts">
  console.log(this) //undefined
  
  // 数据(注意:此时的name、age、tel都不是响应式数据)
  let name = '张三'
  let age = 18
  let tel = '13888888888'

  // 方法
  function changName(){
    name = '李四'//注意:此时这么修改name页面是不变化的
  }
  function changAge(){
    console.log(age)
    age += 1 //注意:此时这么修改age页面是不变化的
  }
  function showTel(){
    alert(tel)
  }
</script>

Vue的生命周期

setup函数会在beforeCreate函数前调用

响应式数据

基本类型的响应式数据

只能用ref

<template>
<!--     alt+shift+a 注释 -->
    <h1>{{ msg }}</h1>
    <!-- 在template中,不用age.value 会自动帮我们 .value -->
    <h1>年龄{{ age }}</h1>
    <button @click="f1">点我年龄+1</button>
</template>
    
<script lang="ts" setup name="Hello">
    import { ref } from 'vue';
    let msg="Hello"
    let age=ref(18)//用ref包裹,让他成为响应式数据

    function f1(){
        age.value+=1 //在script中,需要age.value才能得到它的值
    }
</script>

在js中操作数据需要 .value 在模板中不需要

对象类型的响应式数据

可用reactive,也可用ref

reactive

//对象类型的直接用reactive包起来即可
    let s1=reactive({
        id:"1",
        name:"张三",
        age:"28",
        sex:"男",
        classroom:"3"
    })

    //取值不需要 .value
    function changeS1Name(){
        s1.name="张十三"
    }

ref

仍需要 .value

由下图可知,ref实现响应式对象的底层还是使用了reactive

ref和reactive的区别

<template>
    <ul>
        <li>品牌:{{ car1.brand }}</li>
        <li>价格:{{ car1.price }}</li>
    </ul>
    <button @click="f1">ref改变</button>
    <br>
    <ul>
        <li>品牌:{{ car2.brand }}</li>
        <li>价格:{{ car2.price }}</li>
    </ul>
    <button  @click="f2">reactive改变1</button>
    <button  @click="f3">reactive改变2</button>
</template>

<script  lang="ts" setup>
    import { ref,reactive } from 'vue';
    //选中shift+ (
    let car1=ref({
        brand:"雪佛兰",
        price:666
    })


    let car2=reactive({
        brand:"奔驰",
        price:123
    })

    function f1(){
        car1.value={
            brand:"宝马",
            price:987
        }
    }//可以

    function f2(){
        car2={
            brand:"宝马",
            price:987
        }
/*        car2.brand="wda" */ 
    }//失效  无法获得Car的响应式对象,要改整体只能成员属性一个个地改

    function f3(){
        Object.assign(car2,{brand:"宝马",price:987})
    }//这样可以,直接分配一个新对象给car2

</script>

解构响应式对象

如果我们要处理一个有很多成员的响应式对象,且我们只希望对其中几个成员进行修改,那么就可以对其进行解构

<template>
姓名<h1>{{name }}</h1>
<br>
年龄<h1>{{ age }}</h1>
<br>
性别<h1>{{ sex }}</h1>
其他我们不想实时修改的属性<h1>{{ person.others }}</h1>

<button @click="changeMsg">修改</button>
</template>

<script lang="ts" setup>
    import { reactive,toRefs,toRef} from 'vue';
    let person=reactive({
        name:"张胜男",
        age:22,
        sex:"女",
        others:"其他属性..."
    })
    //将解构出来的成员编程ref响应式对象
    let {name,age}=toRefs(person)//解构多个
    let sex=toRef(person,"sex")//解构单个

    function changeMsg(){
        //因为解构出来的是ref对象,所以要 .value
        name.value="李四";
        age.value=99;
        sex.value="男"
    //此时页面可以变化
        console.log(name,age,sex);
    }

/*     let {name,age}=person
    let sex=person.sex
    function changeMsg(){
        name="李四";
        age=99;
        sex="男"
        
        //    李四,99,男
        //    但是页面上的姓名和年龄并没有改变
        //    这是因为解构出来的成员并不是响应式的
        
        console.log(name,age,sex);
    } */

</script>

计算属性

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。

它的格式如下:

const 我们要计算的属性=computed(function(){
        ...
        return  计算结果
    })

案例

<template>
姓<input v-model="xing">
<br>
名<input v-model="ming">
<br>
姓名<span>{{ name }}</span>
</template>


<script lang="ts" setup>
    import { computed,ref } from 'vue';
    let xing=ref("yang")
    let ming=ref("wei")
    let name=computed(function (){
        return xing.value.slice(0,1).toUpperCase()+xing.value.slice(1)+"-"+ming.value
    })


</script>


<style>
span{
    border: 1px solid red;
}
</style>

计算属性优于函数调用的地方在于它有缓存机制,刷新页面时函数会重新计算,计算属性检查调用的变量的值,没有变动的话直接返回上次计算的结果。

此外,计算属性的值是只读的

要对他实现可读可写要在computed函数里面提供get和set方法

<template>
姓<input v-model="xing">
<br>
名<input v-model="ming">
<br>
姓名<span>{{ name }}</span>
<br>
<button @click="changeValue">对计算属性的直接写操作会调用set方法,并且值会给到set方法的形参,但是还是得通过更改调用的变量值才能复写</button>
</template>


<script lang="ts" setup>
    import { computed,ref } from 'vue';
    let xing=ref("yang")
    let ming=ref("wei")
    let name=computed({
        get(){
            return xing.value.slice(0,1).toUpperCase()+xing.value.slice(1)+"-"+ming.value
        },
        set(val){
            let[str1,str2]=val.split("-")
            xing.value=str1
            ming.value=str2
        }
    })
    function changeValue(){
            name.value="Li-si"
    }
</script>

对计算属性的直接写操作会调用set方法,并且值会给到set方法的形参,但是还是得通过更改调用的变量值才能复写

Vue常用指令

条件渲染

案例

<!-- 条件渲染
 -->
<template>
    <span v-if="flag">flag为true时显示这个span</span>
    <br>
    <span v-show="flag">条件为真时渲染</span>
    <br>
    <span v-if="sex==1">🚹</span>
    <span v-else-if="sex==0">🚺</span>
    <span v-else>不男不女</span>
    <button @click="f1">改变sex值</button>
    <br>

</template>

<script lang="ts" setup>
    import { ref } from 'vue';
    let flag=true
    let sex=ref(0)

    function f1(){
        if(sex.value==0){
            sex.value=1
            return
        }
        else{
            sex.value=0
        }
    }
</script>

v-if和v-show的区别,当判定条件为假时,v-show只是不显示它所包裹的内容,会保留它的结构,而v-if时直接删除,不保留

列表渲染

案例

<!-- 列表渲染 -->
<template>
    <table>
        <tr v-for="stu in students" :key="stu.id">
            <td>{{stu.id}}</td>
            <td>{{stu.name}}</td>
            <td>{{stu.age}}</td>

            <td v-if="stu.sex==0">🚹</td>
            <td v-else>🚺</td>
            
            <td>{{ stu.classroom }}</td>
        </tr>
    </table>

    <!-- v-for   -->
    <span>**************************</span>
    <div v-for="value in myObject">
        <span>{{ value }}</span>
    </div>
    <span>**************************</span>
    <div v-for="(stu, key) in students">
    {{ key }}: {{ stu }}
    </div>
    <span>**************************</span>
    <div v-for="(value, key, index) in myObject">
    {{ index }}. {{ key }}: {{ value }}
    </div>

</template>

<script lang="ts" setup>
    import { reactive } from 'vue';
        let students=reactive([
        {
        id:"2",
        name:"李四",
        age:"36",
        sex:1,
        classroom:"2"
        },
        {
        id:"3",
        name:"周天",
        age:"77",
        sex:0,
        classroom:"1"
        },
        {
        id:"4",
        name:"王五",
        age:"99",
        sex:0,
        classroom:"8"
        },
        {
        id:"5",
        name:"刘六",
        age:"10",
        sex:1,
        classroom:"5"
        }
    ])

    const myObject = reactive({
    title: 'How to do lists in Vue',
    author: 'Jane Doe',
    publishedAt: '2016-04-10'
})

watch

作用:监视数据的变化

牢记watch只能监视如下4种数据:

  1. ref定义的数据。
  2. reactive定义的数据。
  3. 函数返回一个值(getter函数)。
  4. 一个包含上述内容的数组。

因为ref可以定义基本类型和对象类型,所以watch对数据的监视可以分为5种情况

1.watch监视ref定义的基本类型数据

<template>
    <div class="person">
      <h1>情况一:监视ref定义的基本类型数据</h1>
      <h2>当前求和为:{{sum}}</h2>
      <button @click="changeSum">点我sum+1</button>
    </div>
  </template>
  
  <script lang="ts" setup >
    import {ref,watch} from 'vue'
    // 数据
    let sum = ref(0)
    // 方法
    function changeSum(){
      sum.value += 1
    }

    /*
    watch参数    要监视的数据     一个回调函数(在被监视的数据的地址值发生改变时被调用) 
    watch返回一个停止监视函数 调用它停止对watch的监视
    */
    const  stop=watch(sum,(newValue,oldValue)=>{
        console.log("新值:"+newValue+"--"+"旧值"+oldValue)
        if(newValue>=10){
            stop() //停止监视
        }
    })
  </script>

重点:回调函数在数据的地址值发生变化时被调用

2.watch监视ref定义的对象类型数据

<template>
    <div class="person">
      <h1>情况二:监视【ref】定义的【对象类型】数据</h1>
      <h2>姓名:{{ person.name }}</h2>
      <h2>年龄:{{ person.age }}</h2>
      <button @click="changeName">修改名字</button>
      <button @click="changeAge">修改年龄</button>
      <button @click="changePerson">修改整个人</button>
    </div>
  </template>
  
  <script lang="ts" setup name="Person">
    import {ref,watch} from 'vue'
    // 数据
    let person = ref({
      name:'张三',
      age:18
    })
    // 方法
    function changeName(){
      person.value.name += '~'
    }
    function changeAge(){
      person.value.age += 1
    }
    function changePerson(){
      person.value = {name:'李四',age:90}
    }
    /* 
      监视,情况一:监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
      watch的第一个参数是:被监视的数据
      watch的第二个参数是:监视的回调
      watch的第三个参数是:配置对象(deep、immediate等等.....) 
    */
    watch(person,(newValue,oldValue)=>{
      console.log('person变化了',newValue,oldValue)
    },{deep:true})
    
  </script>

可以看到,开启深度监视后,name和age字段的改变也会被我们监视到

****************************************

如果watch是这样写的(没有开启深度监视,那么只有修改整个人,回调函数才会被调用)

watch(person,(newValue,oldValue)=>{
      console.log('person变化了',newValue,oldValue)
    })

3.watch监视reactive定义的对象类型数据

<template>
    <h2>姓名:{{ student.name }}</h2>
    <h2>年龄:{{ student.age }}</h2>
    <button @click="f1">改变姓名</button>
    <button @click="f2">改变年龄</button>
    <button @click="f3">改变整个人</button>
</template>

<script setup>
    import { reactive, watch } from 'vue';
    let student=reactive({
        "name":"张三",
        "age":13
    })

    function f1(){
        student.name=student.name+"~"
    }

    function f2(){
        student.age=student.age+1
    }

    function f3(){
        Object.assign(student,{
        "name":"李四",
        "age":99
    })
    }

    watch(student,(newValue,oldValue)=>{
        console.log("发生变化",newValue,oldValue)
    })

/*
    这样是不行的  因为reactive定义的整个对象会失去响应式
  function f3(){
        student={
        "name":"李四",
        "age":99
    } 
    }*/
</script>

注意看,这里的newValue和oldValue都是一样的

我们首先需要明确的是:

1.回调函数只有被监视数据的地址发生改变时才被调用

2.reactive监视的数据默认开启深度监视,也就是student里面的name和age会被监视到

以改变姓名举例,watch监视到name地址值改变,回调函数被调用,但是我们监视的是student这个整体,它的地址值未曾改变(Object.assign只是覆盖了两个字段的值),因此这时候拿到的newValue和oldValue都是修改后的值。

在实际开发中,我们一般只用到修改后的值,因此不需要太纠结。

watch(person,(value)=>{
    console.log('我只要修改后的值',value)
  })

4.watch监视ref或reactive定义的对象类型里面的某个属性

reactive

<template>
    <div class="person">
      <h1>情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h1>
      <h2>姓名:{{ person.name }}</h2>
      <h2>年龄:{{ person.age }}</h2>
      <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
      <button @click="changeName">修改名字</button>
      <button @click="changeAge">修改年龄</button>
      <button @click="changeC1">修改第一台车</button>
      <button @click="changeC2">修改第二台车</button>
      <button @click="changeCar">修改整个车</button>
    </div>
  </template>
  
  <script lang="ts" setup>
    import {reactive,watch} from 'vue'
  
    // 数据
    let person = reactive({
      name:'张三',
      age:18,
      car:{
        c1:'奔驰',
        c2:'宝马'
      }
    })
    // 方法
    function changeName(){
      person.name += '~'
    }
    function changeAge(){
      person.age += 1
    }
    function changeC1(){
      person.car.c1 = '奥迪'
    }
    function changeC2(){
      person.car.c2 = '大众'
    }
    function changeCar(){
      person.car = {c1:'雅迪',c2:'爱玛'}
    }
  </script>

如果要监视的是对象里面的一个基本数据类型,要用getter方法

// 监视,情况四:监视响应式对象中的某个属性,且该属性是基本类型的,要写成函数式
    watch(()=> person.name,(newValue,oldValue)=>{
        console.log('person.name变化了',newValue,oldValue)
    })

如果监视的是对象里面的一个对象类型,可以用getter方法,也可以不用,如果需要监视这个被监视的对象的里面,还是要开启深度监视

// 监视,情况四:监视响应式对象中的某个属性,且该属性是对象类型的,可以直接写,也能写函数,更推荐写函数
     watch(()=>person.car,(newValue,oldValue)=>{
        console.log('person.car变化了',newValue,oldValue)
    },{deep:true})

ref(同理reactive)

<template>
    <div class="person">
      <h1>情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性</h1>
      <h2>姓名:{{ person.name }}</h2>
      <h2>年龄:{{ person.age }}</h2>
      <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
      <button @click="changeName">修改名字</button>
      <button @click="changeAge">修改年龄</button>
      <button @click="changeC1">修改第一台车</button>
      <button @click="changeC2">修改第二台车</button>
      <button @click="changeCar">修改整个车</button>
    </div>
  </template>
  
  <script lang="ts" setup>
    import {ref,watch} from 'vue'
  
    // 数据
    let person = ref({
      name:'张三',
      age:18,
      car:{
        c1:'奔驰',
        c2:'宝马'
      }
    })
    // 方法
    function changeName(){
        person.value.name+="~"
    }
    function changeAge(){
      person.value.age += 1
    }
    function changeC1(){
      person.value.car.c1 = '奥迪'
    }
    function changeC2(){
      person.value.car.c2 = '大众'
    }
    function changeCar(){
      person.value.car = {c1:'雅迪',c2:'爱玛'}
    }


  </script>

如果要监视的是对象里面的一个基本数据类型,要用getter方法

watch(()=>person.value.name,(newValue,oldValue)=>{
    console.log("name改变了",newValue,oldValue)
    })

监视对象里面的一个对象类型,开启深度监视

watch(()=>person.value.car,(newValue,oldValue)=>{
    console.log("car改变了",newValue,oldValue)
   },{deep:true})

5.监视多个数据

统一用getter方法 然后开深度监视

<template>
    <div class="person">
      <h1>情况五:监视上述的多个数据</h1>
      <h2>姓名:{{ person.name }}</h2>
      <h2>年龄:{{ person.age }}</h2>
      <h2>汽车:{{ person.car.c1 }}、{{ person.car.c2 }}</h2>
      <button @click="changeName">修改名字</button>
      <button @click="changeAge">修改年龄</button>
      <button @click="changeC1">修改第一台车</button>
      <button @click="changeC2">修改第二台车</button>
      <button @click="changeCar">修改整个车</button>
    </div>
  </template>
  
  <script lang="ts" setup >
    import {reactive,watch} from 'vue'
  
    // 数据
    let person = reactive({
      name:'张三',
      age:18,
      car:{
        c1:'奔驰',
        c2:'宝马'
      }
    })
    // 方法
    function changeName(){
      person.name += '~'
    }
    function changeAge(){
      person.age += 1
    }
    function changeC1(){
      person.car.c1 = '奥迪'
    }
    function changeC2(){
      person.car.c2 = '大众'
    }
    function changeCar(){
      person.car = {c1:'雅迪',c2:'爱玛'} //因为这里是直接赋值 所以地址值改变了
    }
  
    // 监视,情况五:监视上述的多个数据
    watch(()=>[person.car,person.age],(newValue,oldValue)=>{
        console.log("car或age被修改",newValue,oldValue)
    },{deep:true})
  
  </script>

watchEffect

官网:立即运行一个函数(相当于{immediate}),同时响应式地追踪其依赖,并在依赖更改时重新执行该函数。

说的通俗点就是watch的升级版,直接写你的代码逻辑,他会根据你用到的去监听,不需要你自己去告诉它你要监视哪些数据。

<template>
    <div class="person">
      <h1>需求:水温达到50℃,或水位达到20cm,则联系服务器</h1>
      <h2 id="demo">水温:{{temp}}</h2>
      <h2>水位:{{height}}</h2>
      <button @click="changePrice">水温+10</button>
      <button @click="changeSum">水位+10</button>
    </div>
  </template>
  
  <script lang="ts" setup>
    import {ref,watch,watchEffect} from 'vue'
    // 数据
    let temp = ref(0)
    let height = ref(0)
  
    // 方法
    function changePrice(){
      temp.value += 10
    }
    function changeSum(){
      height.value += 10
    }
  
    /*
    // 用watch实现,需要明确的指出要监视:temp、height
    watch([temp,height],(value)=>{
      // 从value中获取最新的temp值、height值
      const [newTemp,newHeight] = value
      // 室温达到50℃,或水位达到20cm,立刻联系服务器
      if(newTemp >= 50 || newHeight >= 20){
        console.log('联系服务器')
      }
    })
    */
    // 用watchEffect实现,直接写你的代码逻辑,他会根据你用到的去监听
    //格式:watchEffect( ()=>{}    )
    const stopWtach = watchEffect(()=>{
      // 室温达到50℃,或水位达到20cm,立刻联系服务器
      if(temp.value >= 50 || height.value >= 20){
        console.log(document.getElementById('demo')?.innerText)
        console.log('联系服务器')
      }
      // 水温达到100,或水位达到50,取消监视
      if(temp.value === 100 || height.value === 50){
        console.log('清理了')
        stopWtach()
      }
    })
  </script>

标签的ref属性

用ref标记dom元素

取代了用id来标识标签,因为用id来标识标签,如果父组件和子组件中有id一样的,会引起混淆。

用ref标记组件

子组件

向父亲暴露name,age,classroom(非响应式) sex不暴露

父组件

得到子组件的实例,测试按钮用于测试能否获取子组件的数据

测试

TS中的接口,泛型


在src中新建一个和组件文件夹同级的types文件夹,里面写ts文件

定义一个学生接口,用于规范学生内的成员(记得导出)


使用接口规范数据格式


集合可用泛型


age 加上一个 ? 表示这个字段是可选的

没有age也不报错


reactive直接传泛型也可以

props的使用(父组件给子组件传数据)

第一种:接收并保存到props里面

父组件

子组件

测试


第二种:接收+限制类型

父组件

同上

如果父组件给出的数据和子组件要求的不一样,会报错

子组件

测试

同上


第三种:

父组件

故意不传myList

子组件

怎么写:第二种情况的语句 作为withDefaults的 第一个参数,第二个参数用大括号包裹(因为有多个不同类型的数据,是对象)起来,里面 key: value 其中value要用返回值的形式给出(这里用箭头函数)

测试

Vue的生命周期

总述

测试Vue3的生命周期

父组件

<!-- 父组件App.vue -->
<template>
    <TestVueLifeCycle></TestVueLifeCycle>
</template>
  
<script lang="ts" setup name="App">
    import TestVueLifeCycle from './components/TestVueLifeCycle.vue';
    import { onBeforeMount,onMounted } from 'vue';


    onBeforeMount(()=>{
        console.log("父-挂载前")
            })
    onMounted(()=>{
        console.log("父-挂载完成")
    })
   </script>

子组件

<template>
<h2>{{ count }}</h2>
<button @click="f1">点我count+1</button>
</template>

<script lang="ts" setup>
    import { ref } from 'vue';
    import { onBeforeMount,onMounted } from 'vue';//挂载前,挂载完成
    import { onBeforeUpdate,onUpdated } from 'vue';//更新前,更新完成
    import { onBeforeUnmount,onUnmounted } from 'vue';//销毁前,销毁完成

    let count=ref(0)
    function f1(){
        count.value++
    }
    
    console.log("setup完成vue2中创建前,创建完成工作")


    onBeforeMount(()=>{
        console.log("子-挂载前")
            })
    onMounted(()=>{
        console.log("子-挂载完成")
    })

    onBeforeUpdate(()=>{
        console.log("更新前")
    })
    onUpdated((()=>{
        console.log("更新完成")
    }))

    onBeforeUnmount(()=>{
        console.log("销毁前")
    })
    onUnmounted(()=>{
        console.log("销毁完成")
    })
</script>

测试

更新的钩子函数的执行不难理解(执行了两次按钮点击的效果),那前面的钩子为什么会这样执行呢?

答:

1.运行我们的vue工程,首先来到index.html,这是我们的入口html,它调用了main.ts

2.来到main.ts,这里调用了App.vue,并尝试创建它并将它挂载到index.html的名为app的div标签下(输出 父-setup 父-挂载前)

3.来到App.vue 它自上而下执行,所以会先执行所有孩子的创建和挂载工作,等所有的孩子都创建和挂载完成后,App.vue才执行onMounted函数

自定义hooks

准备工作:首先导入axios的依赖

案例

首先写一个子组件,如下

<template>
<h2>{{ sum }}</h2>
<button @click="addSum">点我sum+1</button>
<hr>

<img v-for="(dog,index) in dogs" :key="index" :src="dog.src">
<br>
<button @click="addDog">点我狗狗+1</button>
</template>

<script lang="ts" setup>
    import axios from 'axios';
    import { ref,reactive } from 'vue';
    let sum=ref(0)
    let dogs=reactive([{src:"https://images.dog.ceo/breeds/pembroke/n02113023_6826.jpg"}])
    
    function addSum(){
        sum.value++;
    }

    function addDog(){
        axios.get('https://dog.ceo/api/breed/pembroke/images/random').then(
            response=>{
                let msg={src:response.data.message}
                console.log(msg)
                dogs.push(msg)
            }
        ).catch(error=>{
            console.log(error,"找不到路径")
        }
        )
    }

</script>

<style>
img{
    height: 100px;
    margin-left: 5px;
}
</style>

这个组件实现的效果如下(有两个功能,点击按钮sum+1 点击按钮出现一张新的狗狗的图片)

注意观察代码的结构,如果一个组件要实现的功能多起来时,方法和数据都放在一个vue里面,会导致代码特别臃肿,要同时找到一个方法所需要的数据和方法也十分困难,这时候hook就派上用场了。

什么是hook?hook有什么优势?

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin
  • 自定义hook的优势:复用代码, 让setup中的逻辑更清楚易懂。

用自定义hook对上述代码进行改进

1.首先在src目录下新建一个hooks目录

2.编写自定义hook(按功能分类,将一个功能的方法和数据抽离出来,用函数包裹,最后把需要暴露的数据和方法交出去)

注意hook的命名规范为: useXxxxx

useSum.ts

useDog.ts

3.在vue中调用hook

<template>
<h2>{{ sum }}</h2>
<button @click="addSum">点我sum+1</button>
<hr>

<img v-for="(dog,index) in dogs" :key="index" :src="dog.src">
<br>
<button @click="addDog">点我狗狗+1</button>
</template>

<script lang="ts" setup>
/*     引入 */
    import useSum from '../hooks/useSum';
    import useDog from '../hooks/useDog';
/*     调用 */
    let {sum,addSum}=useSum()
    let {dogs,addDog}=useDog()
</script>

<style>
img{
    height: 100px;
    margin-left: 5px;
}
</style>

十分清爽,这里体现了vue3组合式api的思想

Vue中的路由

用路由器实现一个基本的切换效果

首先先安装路由器依赖

1.在src文件夹中创建一个router文件夹

2.准备好需要的vue们

这里准备了一个Main.vue作为主界面,要在这里面切换Community,Friend,Home三个组件

2.写一个router

MyRouter.ts

import { createRouter,createWebHistory } from "vue-router";
import community from "../components/Community.vue";
import friend from "../components/Friend.vue";
import home from "../components/Home.vue";

const router=createRouter({
    history:createWebHistory(),
    routes:[
        {
            path:"/community",
            component:community
        },
        {
            path:"/friend",
            component:friend
        },
        {
            path:"/home",
            component:home
        }
    ]

})

3.在main.ts里面注册这个router

import { createApp } from "vue";
import App from "./App.vue";

//引入路由器
import router from "./router/MyRouter";

const app=createApp(App)

app.use(router)//注册路由器

app.mount('#app')

4.在主界面上用RouteLink组件绑定对路由路径的响应

Main.vue

<!-- 登录成功后的主界面 -->
 <template>
<h2>登录成功后的主界面</h2>
<div class="app">
      <!-- 导航区 -->
      <div class="navigate">
        <RouterLink to="/community" active-class="active" class="navi-style">社区</RouterLink>
        <RouterLink to="/friend" active-class="active" class="navi-style">交友</RouterLink>
        <RouterLink to="/home" active-class="active" class="navi-style">个人中心</RouterLink>
      </div>
      <!-- 展示区 -->
      <div class="main-content">
        <RouterView></RouterView>
      </div>
    </div>
 </template>


<script lang="ts" setup>
    import {RouterLink,RouterView} from 'vue-router'  

</script>

<style>
.active{
    color: red;
}
.navi-style{
    margin-left: 10px;
}
</style>

Community.vue(其他两个结构同这个一样)

<!-- 社区模块 -->
<template>
    <h2>社区模块</h2>
    </template>
    <script lang="ts" setup>
    </script>
    <style>
    </style>

效果

两个注意点

1.路由组件通常存放在pagesviews文件夹,一般组件通常存放在components文件夹。(ps:路由组件就是通过路由器才能被渲染的组件,也就是写在RouterLink里面的组件)

2.通过点击导航,视觉效果上“消失” 了的路由组件,默认是被卸载掉的,需要的时候再去挂载

路由器工作模式

history模式

hash模式

怎么写要跳转的路由(三种方法)

to的两种写法

<!-- 第一种:to的字符串写法 -->
<router-link active-class="active" to="/home">主页</router-link>

<!-- 第二种:to的对象写法 -->
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>

命名路由(后面可以用来简化路由跳转和传参的问题)

在路由器中给路由规则命名

在Main.vue中这样写

可以看到第一种是to的字符串写法,后面两种是to的对象写法

<!--         <RouterLink to="/community" active-class="active" class="navi-style">社区</RouterLink> -->

<!-- 里面只能用单引号 -->
<!--         <RouterLink :to="{path:'/community'}" active-class="active" class="navi-style">社区</RouterLink>  --> 

        <RouterLink :to="{name:'shequ'}" active-class="active" class="navi-style">社区</RouterLink>

嵌套路由

要实现的效果:点击社区模块显示所有的帖子,点击帖子标题在下方显示帖子的详情

1.编写Post.vue

2.将Post.vue 配置成Community.vue的子路由

const router=createRouter({
    history:createWebHistory(),
    routes:[
        {
            path:"/community",
            name:"shequ",
            component:community,
            //配置子路由
            children:[
                {
                    path:"post",
                    name:"tiezi",
                    component:post
                }
            ]
        },
        {
            path:"/friend",
            component:friend
        },
        {
            path:"/home",
            component:home
        }
    ]

})

export default router

3.在Community中写跳转

前两种都要加完整路径

<!-- 社区模块 -->
<template>
    <h2>社区模块</h2>
          <li v-for="post in postList" :key="post.postId">
            <!-- <RouterLink to="/community/post">{{ post.title }}</RouterLink> -->
             <!-- <RouterLink :to="{path:'/community/post'}">{{ post.title }}</RouterLink> -->
            <RouterLink :to="{name:'tiezi'}">{{ post.title }}</RouterLink>
          </li>

          <router-view></router-view>
    </template>
    <script lang="ts" setup>
    import { onMounted,onUnmounted } from 'vue';
    

    let postList=[
      {postId:"1",title:"今天天气真好",content:"我想出去玩"},
      {postId:"2",title:"最近有电影好看?",content:"推荐一下"},
      {postId:"3",title:"再也不万元神了",content:"是不可能的"},
      {postId:"4",title:"一眼顶针",content:"鉴定为假"},
      {postId:"5",title:"哈哈哈哈",content:"1234889"}
    ]

    //切换到这里挂载,切换到别的view被卸载
    onMounted(()=>{
        console.log("community被挂载了")
    })

    onUnmounted(()=>{
        console.log("community被卸载了")
    })
    </script>

路由传参

query参数传递

传递参数
<!-- 传死数据 -->
             <!-- <RouterLink to="/community/post?postId=1&title=去干嘛&content=114514">{{ post.title }}</RouterLink> -->

            <!-- 模板字符串嵌入js -->
             <!-- <RouterLink :to="`/community/post?postId=${post.postId}&title=${post.title}&content=${post.content}`">{{ post.title }}</RouterLink> -->
            
            <!-- 携带query参数 -->
            <RouterLink :to="
                {
                    path:'/community/post',
                    query:
                    {
                        postId:post.postId,
                        title:post.title,
                        content:post.content
                    }
                }
            ">{{ post.title }}</RouterLink>
接收参数
<template>
    <div class="post-content">
    <ul>
        <li>id:  {{route.query.postId }}</li>
        <li>标题:  {{ route.query.title }}</li>
        <li>内容:  {{ route.query.content }}</li>
    </ul>
</div>
 </template>


<script lang="ts" setup>
    //注意是useRoute   不是useRouter
    import { useRoute } from 'vue-router';
    const route=useRoute()

</script>

param参数传递

传递参数

首先需要在路由规则里面添加占位

//param参数占位版
            children:[
                {
                    path:"post/:postId/:title/:content?",  //content加个?表示可选
                    name:"tiezi",
                    component:post
                }
            ]
<!-- 传死数据 -->
        <!-- <RouterLink to="/community/post/1/出去玩/谁一起">{{ post.title }}</RouterLink> -->
        <!-- content是可选的 -->
        <!-- <RouterLink to="/community/post/1/出去玩">{{ post.title }}</RouterLink> -->   
        
        <!-- 模板字符串嵌入js -->
        <!-- <RouterLink :to="`/community/post/${post.postId}/${post.title}/${post.content}`">{{ post.title }}</RouterLink> -->

        <!-- 携带param参数              用param传递参数时,必须使用 name -->
        <RouterLink :to="
            {
                name:'tiezi',  //养成习惯,用单引号
                params:{
                    postId:post.postId,
                    title:post.title,
                    content:post.content
                }
            }
        ">{{ post.title }}</RouterLink>
接收参数
<template>
    <div class="post-content">
    <ul>
        <li>id:  {{route.params.postId }}</li>
        <li>标题:  {{ route.params.title }}</li>
        <li>内容:  {{ route.params.content }}</li>
    </ul>
</div>
 </template>


<script lang="ts" setup>
    //注意是useRoute   不是useRouter
    import { useRoute } from 'vue-router';
    const route=useRoute()

</script>

路由的props配置

作用:用于简化接收数据的写法

1.param

2.query (当然param也可以这么写)

replace属性

  1. 作用:控制路由跳转时操作浏览器历史记录的模式。
  2. 浏览器的历史记录有两种写入方式:分别为pushreplace
  • push是追加历史记录(默认值)。
  • replace是替换当前记录。

在默认的push模式下,可以点击按钮回滚

开启replace

回滚不了了

路由:编程式路由导航

我们前面写的代码,只能通过<RouterLink>标签实现路由跳转,假设我们要实现一个登录功能,会发现我们根本实现不了这个功能,因为<RouterLink>是个标签,不能放在函数里,而我们进行逻辑判断肯定要用到方法。

一个小案例

用button实现和下面<RouteLink>一样的效果

<button @click="showPost(post)">使用编程式导航来跳转路由,显示帖子内容</button>
             <RouterLink :to="
                {
                    path:'/community/post',
                    query:
                    {
                        postId:post.postId,
                        title:post.title,
                        content:post.content
                    }
                }
            ">{{ post.title }}</RouterLink>

1.拿到路由器对象

import { useRouter } from 'vue-router';
    const router=useRouter()

2.要跳到哪里,传什么参数(原来 :to 里面怎么写,这个里面就怎么写)

function showPost(post:any){
        router.push(   {
                    path:'/community/post',
                    query:
                    {
                        postId:post.postId,
                        title:post.title,
                        content:post.content
                    }
                })
    }

选择router.push 或router.replace 决定是否能回溯

测试:点击按钮一样能实现之前的效果

路由:重定向

作用:将特定的路径,重新定向到已有路由。

案例:

需求:刚进来啥也没有,我想要刚进来就显示社区的内容


在路由器中定义一条新路由, 这条路由会在访问

的时候重定向路由到

这样,我们一登录成功就能看到社区的帖子了

pinia

搭建pinia环境

1.引入依赖

npm i pinia

2.在main.ts中引入pinia

准备一个基本的效果

<template>
    <h2>测试一下pinia</h2>
    <br>
    <span>当前求和为:{{ sum }}</span>
    <br>
    <select v-model.number="n">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
    </select>
    <button @click="add">加</button>
    <button @click="minus">减</button>
</template>


<script lang="ts" setup>
    import { ref } from 'vue';
    let sum=ref(0)
    let n=ref(1)

    function add(){
        sum.value+=n.value
    }

    function minus(){
        sum.value-=n.value
    }

</script>

1.存储,读取数据

在src文件夹下定义一个store文件夹

count.ts

import { defineStore } from "pinia";

//定义并暴露一个store  第一个参数是id
export const useCountStore=defineStore('count',{

    //数据
    state() {
        return{
            sum:0,
            n:1
        }
    },

    //方法
    actions:{

    },
    
})

用pinia读取数据

2.修改数据

修改数据的三种方式如下

<template>
    <h2>测试一下pinia</h2>
    <br>
    <span>当前求和为:{{ countStore.sum }}</span>
    <br>
    <select v-model.number="countStore.n">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
    </select>
    <button @click="add">加</button>
    <button @click="minus">减</button>
    <br>
    <button @click="change">修改pinia里面的数据</button>
</template>


<script lang="ts" setup>
    import { ref } from 'vue';
    import { useCountStore } from '../store/count';
import { sum } from 'element-plus/es/components/table-v2/src/utils.mjs';
    let countStore=useCountStore()

    function add(){
        countStore.sum+=countStore.n
    }

    function minus(){
        countStore.sum-=countStore.n
    }

    function change(){
        //1.直接修改
        //countStore.sum=666


        //2.批量修改
/*         countStore.$patch(
            {
                sum:999,
                n:88
            }        ) */

        //3.借助action修改(action中可以编写一些业务逻辑)  
        countStore.Multiply(10) 

    }

</script>

count.ts

import { defineStore } from "pinia";

//定义并暴露一个store  第一个参数是id
export const useCountStore=defineStore('count',{

    //数据
    state() {
        return{
            sum:0,
            n:1
        }
    },

    //方法
    actions:{
        Multiply(value:number){
            this.sum*=value
        }
    },
    
})

3.storeToRefs(toRefs)

  • 借助storeToRefsstore中的数据转为ref对象,方便在模板中使用。
  • 注意:pinia提供的storeToRefs只会将数据做转换,而VuetoRefs会转换store中的所有进行转换(包括数据和方法,而方法是不需要我们进行转换的)

4.getters(computed)

定义

使用

这个东西相当于前面学过的computed

5.$subscribe的使用(watch)

通过 store 的 $subscribe() 方法侦听 state 及其变化

//mutate:本次修改的信息
    //state:发生了什么变化
    countStore.$subscribe((mutate,state)=>{
        
        console.log("这个仓库里面的数据发生了改变",mutate,state)
    
    })

点击”加“按钮

这个东西相当于前面学过的watch

tips

1.快捷键

alt+shift 多行写

alt+shift+a 注释

shift+( 用()包裹

2.别名插件

引入一个拓展插件,让Hello.vue被引入App.vue的时候可以起别名

在vite.config.js中引入

3.v-model(适用表单元素)和v-bind(适用表单属性)

数据绑定方向

  • v-model:是双向数据绑定。它不仅能够将 Vue 实例中的数据渲染到页面上,还能够监听用户的输入,并将用户输入的数据更新回 Vue 实例中的数据。这种双向绑定特别适用于表单元素,如 <input><select><textarea>

  • v-bind:是单向数据绑定。它主要用于将 Vue 实例中的数据绑定到 HTML 元素的属性上,但不会自动将用户输入的数据更新回 Vue 实例。虽然通过配合事件处理可以实现双向绑定,但 v-bind 本身只负责单向的数据流。

适用范围

  • v-model:主要用于表单控件或自定义组件的双向数据绑定。它会自动根据控件类型(如文本、单选按钮、复选框、选择框等)选择正确的方法来更新元素。

  • v-bind:几乎可以用于绑定任何 HTML 元素的属性,包括但不限于 classstylehrefsrc 等。此外,它还可以用于绑定表达式和 HTML 内容(尽管绑定 HTML 内容时需要使用不同的语法)。

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

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

相关文章

年度巨献 | OpenCSG开源最大中文合成数据集Chinese Cosmopedia

01 背景 近年来&#xff0c;生成式语言模型&#xff08;GLM&#xff09;的飞速发展正在重塑人工智能领域&#xff0c;尤其是在自然语言处理、内容创作和智能客服等领域展现出巨大潜力。然而&#xff0c;大多数领先的语言模型主要依赖于英文数据集进行训练&#xff0c;中文数据…

python:给1个整数,你怎么判断是否等于2的幂次方?

最近在csdn上刷到一个比较简单的题目&#xff0c;题目要求不使用循环和递归来实现检查1个整数是否等于2的幂次方&#xff0c;题目如下&#xff1a; 题目的答案如下&#xff1a; def isPowerofTwo(n):z bin(n)[2:]print(bin(n))if z[0] ! 1:return Falsefor i in z[1:]:if i !…

NXP官方或正点原子mfgtool下载系统报错initialize the library falied error code:29

这是因为mfgtool版本或者源文件被破坏了&#xff0c;你可以重新下载一个被改过的mfgtool程序&#xff0c;我就是去原子官网重新在linux包里找了新的更迭过的mfgtool

VMware虚拟机因磁盘空间不足黑屏无法登录

在虚拟机里存储了一些文件之后&#xff0c;再打开发现进不去了&#xff0c;只有光标一直在左上角&#xff0c;登录的框都是黑的&#xff0c;具体如下&#xff1a; 明明知道登录框的存在却怎么也触碰不到它T_T &#xff0c;先说解决方法&#xff1a; 产生这个问题的原因是因为磁…

yolov5/8/9模型在COCO分割数据集上的应用【代码+数据集+python环境+GUI系统】

yolov5/8/9模型在COCO分割数据集上的应用【代码数据集python环境GUI系统】 yolov5/8/9模型在COCO分割数据集上的应用【代码数据集python环境GUI系统】 1.COCO数据集介绍 COCO数据集&#xff0c;全称为Microsoft Common Objects in Context&#xff0c;是微软于2014年出资标注的…

多态与绑定例题

答案&#xff1a; B D C 知识点&#xff1a; 多态是相同方法不同的表现&#xff0c;分为重写和重载 重写体现在父类与子类不同表现&#xff0c;主要表现为子类重现父类的方法 重载体现在同一个类中的不同表现 绑定分为动态绑定和静态绑定 动态绑定是在运行时 静态绑定是…

动态规划算法:09.路径问题_最小路径和_C++

目录 题目链接&#xff1a;LCR 099. 最小路径和 - 力扣&#xff08;LeetCode&#xff09; 一、题目解析 题目&#xff1a; 解析&#xff1a; 二、算法原理 1、状态表示 2、状态转移方程 3、初始化 dp表初始化: 特殊位置初始化&#xff1a; 4、填表顺序 5、返回值 …

【HTTP】认识 URL 和 URL encode

文章目录 认识 URLURL 基本格式**带层次的文件路径****查询字符串****片段标识符** URL encode 认识 URL 计算机中非常重要的概念&#xff0c;并不仅仅是在 HTTP 中使用。用来描述一个网络资源所处的位置&#xff0c;全称“唯一资源定位符” URI 是“唯一资源标识符“严格的说…

超越极限!Qwen2.5 助力多领域智能应用

前沿科技速递&#x1f680; 近日&#xff0c;Qwen2.5 系列重磅发布&#xff0c;成为开源语言模型领域的又一里程碑。作为一款全新的通用语言模型&#xff0c;Qwen2.5 在支持自然语言处理的基础上&#xff0c;还在编程、数学等领域进行了专项优化。Qwen2.5 模型支持长文本生成&a…

黑群晖安装教程

黑群晖&#xff08;一种非官方的群晖NAS系统安装方式&#xff09;的安装教程相对复杂&#xff0c;但按照以下步骤操作&#xff0c;可以顺利完成安装。请注意&#xff0c;由于黑群晖涉及非官方操作&#xff0c;安装过程中可能遇到各种不确定因素&#xff0c;建议具备一定的计算机…

十四、运算放大电路

运算放大电路 1、理想运算放大器的概念。运放的输入端虚拟短路、虚拟断路之间的区别; 2、反相输入方式的运放电路的主要用途&#xff0c;以及输入电压与输出电压信号的相位 3、同相输入方式下的增益表达式(输入阻抗、输出阻抗)

英语<数词>

1.基数 one two three 整数 1 2 3 小数 1.1 2.2 3.2 分数 分子用基数&#xff0c;分母用序数 例子 1/3 one third 分子>1 2/3 two thirds 百分数 2.序数 first second

【软考】传输层协议TCP与UDP

目录 1. TCP1.1 说明1.2 三次握手 2. UDP3. 例题3.1 例题1 1. TCP 1.1 说明 1.TCP(Transmission Control Protocol&#xff0c;传输控制协议)是整个 TCP/IP 协议族中最重要的协议之一。2.它在IP提供的不可靠数据服务的基础上为应用程序提供了一个可靠的、面向连接的、全双工的…

[UTCTF2020]sstv

用goldwave和010editor打开均未发现线索&#xff0c; 网上搜索sstv&#xff0c;豆包回答如下&#xff1a; 慢扫描电视&#xff08;Slow Scan Television&#xff0c;简称 SSTV&#xff09;是一种通过无线电传输和接收静态图像的技术。 一、工作原理 SSTV 通过将图像逐行扫描并…

十九、石英晶体振荡电路

石英晶体振荡电路 1、石英晶体的特点、等效电路、特性曲线; 2、石英晶体振动器的特点&#xff0c; 3、石英晶体振动器的振荡频率

Vision Transformer (ViT)、Swin Transformer 和 Focal Transformer

1. Vision Transformer (ViT) Vision Transformer详解-CSDN博客https://blog.csdn.net/qq_37541097/article/details/118242600?ops_request_misc%257B%2522request%255Fid%2522%253A%2522F8BBAFBF-A4A1-4D38-9C0F-9A43B56AF6DB%2522%252C%2522scm%2522%253A%252220140713.13…

互联网全景消息(5)之RocketMq快速入门(下)

一、RocketMQ的存储设计 1.1 Domain Model 1.1.1 Message Message是RocketMQ消息引擎中的主体。messageId是全局唯一的。MessageKey是业务系统(生产者)生成的,所以如果要结合业务,可以使用MessageKey作为业务系统的唯一索引。 1.1.2 Topic subTopics==Message Queue,其实…

机器学习——Stacking

Stacking&#xff1a; 方法&#xff1a;训练多个模型(可以是强模型)&#xff0c;然后将这些模型的预测结果作为新的特征&#xff0c;输入到下一层新的模型&#xff08;可以是多个&#xff09;中进行训练&#xff0c;从而得到最终的预测结果。 代表&#xff1a;Stacking本身并没…

跨游戏引擎的H5渲染解决方案(腾讯)

本文是腾讯的一篇H5 跨引擎解决方案的精炼。 介绍 本文通过实现基于精简版的HTML5&#xff08;HyperText Mark Language 5&#xff09;来屏蔽不同引擎&#xff0c;平台底层的差异。 好处&#xff1a; 采用H5的开发方式&#xff0c;可以将开发和运营分离&#xff0c;运营部门自…

新峰商城之订单(一):确认页面开发

新峰商城订单从生成到处理结束&#xff0c;主要以下几个流程&#xff1a; &#xff08;1&#xff09;提交订单&#xff08;商城用户发起&#xff09; &#xff08;2&#xff09;订单入库&#xff08;后台逻辑&#xff09; &#xff08;3&#xff09;支付订单&#xff08;商城…