VUE3 学习记录

好久没写vue好多东西都忘了 正好现在有些事件所以打算重新从头开始学习vue

(没学过vue2直接学的3,啊哈哈哈 用啥学啥)

一`Vue3核心语法

1`Vue3和Vue2的写法区别

1.1vue2采用OptionsAPI           (配置风格)

name:"name",

data(){
//数据
    ruturn{
        name:'lm',
        age:'18',
        tel:'13384218421'
    }
},


methods:{
//函数
    changeName(){
        this.name='liM'
    }
}

watch: {
//监听当数据变化是函数调用
    firstName(newval) {
​        this.fullname = newval + this.lastName
    },
    lastName(newval) {
        this.fullname = this.firstName + newval
    }
​
}

computed: {
//监听深度
    fullname() {
        return this.firstName + this.lastName
    }
}

1.2vue3采用CompositionsAPI (组合风格)                 

普通写法

setup() {
// 数据 此时的数据是非响应式的 
// 就是此时的数据显示当你更改时后台数据会修改但是展示的数据不会修改
let name = 'lll'
let age = 18
let tel = '1333333333'
// 方法
// 修改名字
function changeName(){
     name='zh'
   }
  // 修改年龄
   function changeAge(){
     age++
   }
   // 显示电话号
   function showTel(){
     alert(tel)
   }


  return{name,age,tel,changeName,changeAge,showTel}
}

  setup语法糖写法

<script setup lang="ts">
import router from "@/router";
import Person from "@/components/Person.vue";


function logon() {
  router.push({
//路由跳转
    path: "/log"
    // name:"log"
  })
}

</script>

1.3小结

vue3 setup(){}内不在支持this                                                               

vue2 内的script可以使用script3的内容但是3不能用2的(找不到)     

2` 响应式数据

2.1 ref         (可以定义:基本类型、对象类型 的响应式数据)     

import {defineComponent, ref} from 'vue'

// 数据
// ref() 将基本数据设置为响应式
let name = ref('lll')
let age = ref(18)
let tel = ref('1333333333')

import {reactive} from "vue";
let car = ref({
  //定义car数据
  brand: '奔驰',
  price: 100
})

function changePrice(){
  //定义修改价格的方法
  car.value.price +=10
}

let games = ref([
  //数组
  {id:'game01',name:'英雄联盟'},
  {id:'game02',name:'王者荣耀'},
  {id:'game03',name:'元神启动'},
])

function changeFirstGame(){
  // 修改第一个游戏的名字
  games.value[0].name='手游联盟'
}

//页面内容
<!--  ref-->
<!--  基本类型展示-->
  <h2>姓名{{ name }}</h2>
  <h2>年龄{{ age }}</h2>
  <button @click="changeName">修改名字</button>
  <button @click="changeAge">修改年龄</button>
  <button @click="showTel">查看联系方式</button>
<!--  对象展示-->
  <h2>一辆{{car.brand}}车,价值{{car.price}}万</h2>
  <button @click="changePrice">修改汽车的价格</button>

<!--  数组展示-->
  <h2>游戏列表:</h2>
  <ul>
    <li v-for="g in games" :key="g.id">{{g.name}}</li>
  </ul>
  <button @click="changeFirstGame"></button>

2.2 reactive (只能定义:对象类型的响应式数据)

import {reactive} from "vue";

//recative()将对象设置为响应式
let car = reactive({
  //定义car数据
  brand: '梅赛德斯奔驰',
  price: 100
})

function changePrice(){
  //定义修改价格的方法
  car.price +=10
}

let games = reactive([
  //数组
  {id:'game01',name:'英雄联盟'},
  {id:'game02',name:'王者荣耀'},
  {id:'game03',name:'元神启动'},
])

function changeFirstGame(){
  // 修改第一个游戏的名字
  games[0].name='手游联盟'
}

//页面内容
<!--  reactive-->
<!--  对象展示-->
  <h2>一辆{{car.brand}}车,价值{{car.price}}万</h2>
  <button @click="changePrice">修改汽车的价格</button>

<!--  数组展示-->
  <h2>游戏列表:</h2>
  <ul>
    <li v-for="g in games" :key="g.id">{{g.name}}</li>
  </ul>
  <button @click="changeFirstGame"></button>

2.3 ref对比reactive

宏观角度

ref用来定义:基本类型数据、对象类型数据

reactive用来定义:对象类型数据

区别

ref创建的变量必须使用 .value(可以使用volar插件自动添加 .value)

reactive重新分配一个新对象,会失去响应式(可以使用Object.assign去整体替换)

import {reactive} from "vue";
let car = reactive({
  //定义car数据
  brand: '奔驰',
  price: 100
})

function changeCar() {
  Object.assign(car, {brand: '奥迪',price:'120'})
}

<!--  页面展示-->
  <h2>一辆{{car.brand}}车,价值{{car.price}}万</h2>
  <button @click="changeCar">修改汽车</button>
使用原则

若需要一个基本类型的响应式数据,必须使用ref

若需要一个响应式对象,层级不深,ref、reactive都可以

若需要一个响应式对象,层级较深,推荐使用reactive

拓展

表面上ref虽然可以定义对象类型响应数据,但是实际上底层还是用recative实现的         

2.4 toRefs与toRef

作用:将一个响应式对象中的每一个属性,转换为ref对象

场景:比如将数据解构之后如果直接用那么就不是响应式对象了所以需要用到这俩玩意

<script setup lang="ts" name="Person">
import {reactive, toRefs} from 'vue'

// 数据
let person = reactive({
  name: 'lm',
  age: 18
})

let {name, age} = toRefs(person)


// 方法
function changeName() {
  name.value += '~'
}

function changeAge() {
  age.value += 1
}
</script>

<template>
  <h2>姓名:{{ name }}</h2>
  <button @click="changeName">修改名称</button>
  <h2>年龄:{{ age }}</h2>
  <button @click="changeAge">修改年龄</button>
</template>

<style scoped>

</style>

备注:toRef与toRefs功能一致,但toRefs可以批量转换

3` computed() 属性计算

computed() 是函数 函数存在缓存 所以只需要计算一次 (方法则是没有缓存的每次调用方法都需要单独重新进行计算)

<script setup lang="ts" name="Person">
import {computed, reactive, ref, toRefs} from 'vue'

// 数据
let firstname = ref('l')
let lastname = ref('m')

// // 此时是只读不写
// let fullName = computed(()=>{
//   console.log(1)//此处只执行了一次
//   return firstname.value+'--'+lastname.value
// })

//此时是只读不写
let fullName = computed({
  get(){
    return firstname.value+'--'+lastname.value
  },
  set(val){
    const [str1,str2] = val.split('-')
    firstname.value=str1
    firstname.value=str2
    console.log(val)
  }
})
function changeFullName(){
  fullName.value = 'li-m'
}
</script>

<template>
<!--  姓:<input type="text" v-bind:value="firstname"><br>-->
<!--  v-band: 简写: 绑定属性-->
  姓:<input type="text" v-model="firstname"><br>
<!--  v-model=""   双向绑定-->
  名:<input type="text" v-model="lastname"><br>
  全名: <span>{{fullName}}</span><br>
  <button @click="changeFullName">将全名改为li-M</button>
</template>

<style scoped>

</style>

4` 监视数据

4.1watch() 

watch 作用:监视数据变化

watch 特点:Vue3 中的 watch 可以监视以下4种数据

1.ref定义的数据

2.reactive定义的数据

3.函数返回一个值(`getter函数`)

4.一个包含上述内容的数组

*情况一

监视 ref 定义的 [基本类型] 数据(监视ref的基本数据时不需要也不能加 .value 因为根据Vue3官方文档给出的内容 watch只能监视ref定义的数据 如果加上了 .value 那么监视的就是数据的内容了而不是数据本身 比如 let sum = ref(0) sum是 ref定义的数据 而 sum.value是0)

<script setup lang="ts" name="Person">
import {ref, watch} from 'vue'

//数据
let sum = ref(0)

//函数
function changeSum() {
  sum.value += 1
}

//监视
const stopWatch =watch(sum, (newValue, oldValue) => {
  //在此处给监视事件赋一个只读的值在下方用于结束监视
// watch(谁?,回调函数){
   console.log('sum变化了', newValue, oldValue)
  if (newValue>10)stopWatch()
  // 当newValue的值大于10时停止监视
})

</script>

<template>
  <h1><h2>情况一: 监视 [ref] 定义的 [基本类型] 数据</h2></h1>
  <h2>当前求和为:{{ sum }}</h2>
  <button @click="changeSum">点击加一</button>
</template>

<style scoped>
</style>
`情况二 

监视ref定义的 [对象类型] 数据: 直接写数据名, 监视的是对象的 [地址值] ,若想监视对象内部的数据, 要手动开启深度监视.

注意:

`若修改的是ref 定义的对象中的属性, newValue 和 oldValue 都是新的值,因为它们是同一个对象.

`若修改整个ref定义的对象, newValue 是新值, oldValue 是旧值, 因为不是同一个对象了.

<script setup lang="ts" name="Person">
import {ref, watch} from "vue";

//数据
let person = ref({
  name: '张三',
  age: 18
})

//函数
function changeName() {
  person.value.name += '~'
}

function changeAge() {
  person.value.age++
}

function changePersion() {
  person.value = {name: '李四', age: 20}
}

//监视,情况一:监视 [ref] 定义的 [对象类型] 数据,监视的是对象的地址值
// watch(person, (newValue, oldValue) => {
//   console.log('persion变化了', newValue, oldValue)
// }


//监视,情况二:监视 [ref] 定义的 [对象类型] 数据监视的是对象的地址值,若想监视对象的属性变化则需要手动开启深度监视: {deep:true}
/**
 * watch的第一个参数: 被监视的数据
 * watch的第二个参数: 监视的回调
 * watch的第三个参数: 配置对象(deep immediate等等......)
 */
watch(person, (newValue, oldValue) => {
  console.log('persion变化了', newValue, oldValue)
}, {deep: true})
/**
 * deep: 深度监视
 * immediate: 立即监视
 */
</script>


<template>
  <h2>情况二: 监视 [ref] 定义的 [对象类型] 数据</h2>
  <h2>姓名:{{ person.name }}</h2>
  <h2>年龄:{{ person.age }}</h2>
  <button @click="changeName">修改姓名</button>
  <button @click="changeAge">修改年龄</button>
  <button @click="changePersion">修改人物</button>
</template>


<style scoped>

</style>
`情况三

监视reactive定义的 [对象类型] 数据, 且默认开启了深度监视

<script setup lang="ts" name="Person">
import {reactive,  watch} from "vue";

//数据
let person = reactive({
  name: '张三',
  age: 18
})

//函数
function changeName() {
  person.name += '~'
}

function changeAge() {
  person.age++
}

function changePersion() {
  Object.assign(person,{name:'李',age:14})
}

//监视 情况三: 监视 [reactive] 定义的 [响应式对象]数据 且默认是开启深度监视的
watch(person,(newValue,oldValue)=>{
  console.log('persion变化了',newValue,oldValue)
})
</script>


<template>
  <h2>情况三: 监视 [reactive] 定义的 [响应式对象]</h2>
  <h2>姓名:{{ person.name }}</h2>
  <h2>年龄:{{ person.age }}</h2>
  <button @click="changeName">修改姓名</button>
  <button @click="changeAge">修改年龄</button>
  <button @click="changePersion">修改人物</button>
</template>


<style scoped>

</style>
`情况四

监视ref或reactive 定义的 [对象类型] 数据中的某个属性,注意点如下:

1.若该属性不是 [对象类型],需要写成函数形式

2.若该属性值依然是 [对象类型],可直接编写,也可以写成函数,不过建议携程函数   

结论:监视的是对象内的属性,那么最好写函数式,注意:若是对象监视的是地址值 ,需要关注对象内部,需要手动开启深度监视

<script setup lang="ts" name="Person">
import {reactive, watch} from "vue";

//数据
let person = reactive({
  name: '张三',
  age: 18,
  car: {
    c1: '奔驰',
    c2: '宝马'
  }
})

//方法
function changeName() {
  person.name += '~'
}

function changeAge() {
  person.age++
}

function changeC1() {
  person.car.c1 = '奥迪'
}

function changeC2() {
  person.car.c2 = '劳斯莱斯'
}

function changeCar() {
  person.car = {c1: '雅迪', c2: '小刀'}
}


//监视 情况四: 情况一:监视响应式对象数据中的某个属性,且该属性是基本类型的 要写成函数

//若该属性不是 [对象类型],需要写成函数形式 监视 recative定义的数据内的某一项内容
// watch(()=>{return person.name}, (newValue, oldValue) => {
//   console.log('persion.name 变化了', newValue, oldValue)
// })

//监视 情况四: 情况二:监视响应式对象数据中的某个属性,且该属性是基本类型的 可以直接写,也能写成函数,但是更推荐写函数

// watch(person.car, (newValue, oldValue) => {
//   console.log('persion.car  变化了', newValue, oldValue)
// //此时当c1 和c2 发生变化时可以监视的到但是当整个car发生改变时无法监视,
// //因为 changeCar()改变了整个car 而此处的监视监视的是之前的car
// })

// watch(()=>person.car, (newValue, oldValue) => {
//   console.log('persion.car  变化了', newValue, oldValue)
// //此时当c1 和c2 发生变化时监视不到但是当整个car发生改变时可以监视的到,
// //因为 此时外面包了函数所以此时检测的是地址值
// })

watch(()=>person.car, (newValue, oldValue) => {
  console.log('persion.car  变化了', newValue, oldValue)
//此时当c1 ,c2 ,car 发生变化时都可以监视的到,
//因为 此时外面包了函数所以此时检测的是地址值,且对此地址进行了深度监视
},{deep:true})
</script>


<template>
  <h2>情况四: 监视[ref] 或 [reactive] 定义的 [对象类型] 数据中的某个属性</h2>
  <h2>姓名:{{ person.name }}</h2>
  <h2>年龄:{{ person.age }}</h2>
  <h2>汽车:{{ person.car.c1 }}</h2>
  <h2>汽车:{{ person.car.c2 }}</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>
</template>


<style scoped>

</style>
`情况五           

监视上述的多个数据时                                                                                                                       

<script setup lang="ts" name="Person">
import {reactive, watch} from "vue";

//数据
let person = reactive({
  name: '张三',
  age: 18,
  car: {
    c1: '奔驰',
    c2: '宝马'
  }
})

//方法
function changeName() {
  person.name += '~'
}

function changeAge() {
  person.age++
}

function changeC1() {
  person.car.c1 = '奥迪'
}

function changeC2() {
  person.car.c2 = '劳斯莱斯'
}

function changeCar() {
  person.car = {c1: '雅迪', c2: '小刀'}
}


//监视 情况五: 监视上述多个数据

watch([()=>person.name,()=>person.age,()=>person.car,()=>person.car.c1,()=>person.car.c2], (newValue, oldValue) => {
  console.log('persion.car  变化了', newValue, oldValue)
},{deep:true})
</script>


<template>
  <h2>情况五: 监视上述多个数据</h2>
  <h2>姓名:{{ person.name }}</h2>
  <h2>年龄:{{ person.age }}</h2>
  <h2>汽车:{{ person.car.c1 }}</h2>
  <h2>汽车:{{ person.car.c2 }}</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>
</template>


<style scoped>

</style>

4.2watchEffect

立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数

<script setup lang="ts" name="Person">
import {ref, watch, watchEffect} from "vue";

//数据
let temp = ref(10)
let height = ref(50)

//方法
function changerTemp() {
  temp.value += 10
}

function changerHeight() {
  height.value += 10
}

//监视

//不用watchEffect的写法
// watch([temp, height], (newValue, oldValue) => {
//   //[()=>{return  temp.value},()=>{return  height.value}]数组里面放函数也可以
//   // console.log('当前'+newValue,'曾经'+oldValue)
//   let [newTemp, newHeight] = newValue
//   if (newTemp >= 60 || newHeight >= 80) {
//     console.log('当前水温为'+newTemp,'当前水位为'+newHeight+'超过要求')
//   }
// })

//用watchEffect的写法
watchEffect(()=>{
  if (temp.value >= 60 || height.value >= 80) {
    console.log('当前水温为'+temp.value,'当前水位为'+height.value+'超过要求')
  }
})
</script>


<template>
  <h1>watchEffect</h1>
  <h2>需求,当水温达到60,或水位达到80时,给服务器发送请求</h2>
  <h2>当前水温{{ temp }}℃</h2>
  <h2>当前水位{{ height }}m</h2>
  <button @click="changerTemp">点我升高温度+10</button>
  <button @click="changerHeight">点我升高高度+10</button>
</template>


<style scoped>

</style>

4.3watch和watchEffect对比

1.都能监听响应式数据的变化,不同的时监听数据变化的方式不同

2.watch: 要明确的指出监视的数据

3.watchEffect: 不需要明确指出监视的数据(函数中用到哪些属性,就监视那些的属性)

5` 标签的ref属性

常用作 作为标记点 防止页面内容发生冲突

persion.vue

<script setup lang="ts" name="Person">

import {ref,defineExpose} from "vue";

//数据
//存储元素标记内容
let title2 = ref()
let a =ref(0)
let b =ref(0)
let c =ref(0)


//函数
//通过id获取到页面代码 此方法可能会出现id冲突问题 实际项目工作中不建议使用
// function showLog() {
//   console.log(document.getElementById('title2'))
// }

function showLog() {
  console.log(title2.value)
}

//导出 如果此处不导出那么下面的那个vue 是取不到此处 a b c的内容的
defineExpose({a,b,c})
</script>


<template>
  <div class="person">
    <h1>标签的ref属性</h1>
    <h1>中国</h1>
    <h2 ref="title2" >北京</h2>
    <h3>胡同</h3>
    <button @click="showLog">点击我输出h2</button>
  </div>
</template>

<style scoped>
如果输出的时候内容的时候多出了例如 data-v-4cadc14e 等这一类的内容 
是因为在此处使用了局部样式 ,将scoped那块删了就没有了 
但是! 如果删了的话 那么此处的样式就是全局的了 
如果样式的名发生冲突可能会出现显示混乱的效果
.person{
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

lab.vue

<script setup lang="ts">
import router from "@/router";
import Person from "@/components/Person.vue";
import {ref} from "vue";


function logon() {
  router.push({
    path: "/log",
    name:"log"
  })
}

let title2 = ref(0)
let per = ref()

function showLog() {
//此处可以看作是调用persion.vue的内容
  console.log(per.value)
}
</script>

<template>
  <h1 ref="title2">主页</h1>
  <person ref="per"/>
  <button @click="showLog">测试</button>
</template>

<style scoped>

</style>

6`  接口 泛型 自定义类型

person.ts

//1. 定义一个接口 用于限制person对象的具体属性
export interface Person{
    id:string,
    name:string,
    age:number
}

//2. 一个自定义类型
//数组 方法一
// export type Persons = Array<Person>

//数组 方法二
export type Persons = Person[]

Person.vue

<script setup lang="ts" name="Person">
import {type Person,type Persons} from '@/typedefine/person'

//定义单个数据
// let persion:Person = {id:'01',name:'LiM',age:18}

//定义数组数据
// let persion: Array<Person> = [
//     //let定义 persion名称 :Array数组 <person>泛型
//   {id: '01', name: 'LiM', age: 19},
//   {id: '02', name: 'Li', age: 18},
//   {id: '03', name: 'L', age: 19}
// ]

let persion:Persons = [
  //let定义 persion名称 :Array数组 <person>泛型
  {id: '01', name: 'LiM', age: 19},
  {id: '02', name: 'Li', age: 18},
  {id: '03', name: 'L', age: 19}
]
</script>


<template>
  <div class="person">
    ???
  </div>
</template>


<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

7` props的使用

简单示例:

接收父类传过来的数据 defineProps([' '])

父类内容:

<template>
  <person a="哈哈哈哈"/>
</template>

子类引用:

<script setup lang="ts" name="Person">
defineProps(['a'])
</script>

<template>
    {{ a }}
</template>

曾加难度:

接收数组

父类内容:

<script setup lang="ts">
import Person from "@/components/Person.vue";
import {type Persons} from '@/typedefine/person'
import {reactive} from "vue";


//定义数组数据
let persionList= reactive<Persons>([
  //let定义 persion名称 :Array数组 <person>泛型
  {id: '01', name: 'LiM', age: 19},
  {id: '02', name: 'Li', age: 18},
  {id: '03', name: 'L', age: 19}
])

console.log(persionList)
</script>

<template>
  <person a="哈哈哈哈" :list="persionList"/>
</template>

<style scoped>

</style>

子类内容:

<script setup lang="ts" name="Person">
import type {Persons} from "@/typedefine/person";

// //只接收list
// defineProps(['list'])

// //接收list + 限制类型
// defineProps<{ list: Persons }>()

// //接收list + 限制类型 + 限制必要性
// defineProps<{ list?: Persons }>()

//接收list + 限制类型 + 限制必要性 + 指定默认值
withDefaults(defineProps<{ list?: Persons }>(), {
  list: () => [{id: '00', name: '0号', age: 20}]
})

// //接收list 同时将props保存起来
// let x = defineProps(['list'])
</script>


<template>
  <div class="person">
    <ul>
      <li v-for="persionObj in list" :key="persionObj.id">
        {{ persionObj.name }} -- {{ persionObj.age }}
      </li>
    </ul>
  </div>
</template>


<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

8` 生命周期

概念:Vue组件实例在创建时要经历一系列的初始化步骤,在此过程中Vue会在合适的时机, 调用特定的函数, 从而让开发者有机会在特定阶段运行自己的代码, 这些特定的函数统称为: 生命周期钩子

注意:

当引用组件时 子组件会优先于父组件进行挂载

用 v-if 来决定是否显示时 如果条件为false那么内容将会被销毁

用v-show来决定是否显示时 如果条件为false那么内容会被隐藏

8.1 Vue2生命周期

组件的生命周期: 四个周期 八个钩子

[时刻]                [调用特定函数]

-->创建(创建前 beforeCreate,         创建完毕 created)

-->挂载(创建前 beforeMount,          挂载完毕 mounted)

-->更新(创建前 beforeUpdate,        挂载完毕 update)        

-->销毁(创建前 beforeDestroy,       销毁完毕 destroyed)

拓展: 用 v-if 来决定是否显示时 如果条件为false那么内容将会被销毁 但是如果用v-show则会隐藏

8.2 Vue3生命周期

组件的生命周期: 四个周期 八个钩子

[时刻]                [调用特定函数]

-->创建 setup函数(vue3推荐使用setup语法糖 所以setup都不需要在写了)

-->挂载(创建前 onBeforeMount,          挂载完毕 onMounted)

-->更新(创建前 onBeforeUpdate,        挂载完毕 onUpdate)        

-->卸载(创建前 onBeforeUnmount,       销毁完毕 onUnmounted)

拓展: vue3调用函数需要在函数内写上回调函数 ()=>{ 这里面写你要写的东西 }

9`自定义Hooks

hooks 简单来说就是外联ts 把冗杂的ts内容(数据 函数 方法 钩子)单独存放,在需要的时候进行使用

9.1 创建一个文件夹名称为hooks

9.2 创建ts文件

axios 网络请求

安装: npm i axios (控制台)

引入: import axios from 'axios' (组件)

useDog.ts

import axios from "axios";
import {reactive} from "vue";

export default function () {
    //数据
    let dogList = reactive([
        'Https://api.dujin.org/pic'
    ])

    //函数
    async function getDog() {
        try {
            let result = await axios.get('Https://api.dujin.org/pic')
            dogList.push(result.data.message)
        } catch (error) {
            alert(error)
        }
    }
    //向外部提供信息
    return {dogList,getDog}
}

useSum.ts

import {ref} from "vue";

export default function () {
    //数据
    let sum = ref(0)

    //函数
    function changerSum() {
        sum.value++
    }
    return {sum,changerSum}
}

9.3在需要的时候使用

在vue组件内使用

import useSum from "@/hooks/useLoginIn";

const {sum,changerSum} = useSum()

二`路由

路由就是 一组 key 和 value 的对应关系( key + value => route) 多个路由由路由器管理

1.路由的基本切换效果

创建几个vue文件

@/router/index.ts

// 创建一个路由器 ,并暴露出去

//引入
import {createRouter, createWebHistory} from 'vue-router'

// 引入组件
import lable from '@/views/lable.vue'
import logHome from "@/views/log/logHome.vue";
import login from "@/views/log/page/login.vue";
import logon from "@/views/log/page/logon.vue";
import back from "@/views/log/page/back.vue";


// 创建路由
const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: [
        {
            path: '/',
            name: 'lable',
            component: () => lable,
            // 标签页
        }, {
            path: '/log',
            name: 'log',
            component: () => logHome,
            // 登录注册页
            children: [
                {
                    path: '/log/login',
                    name: 'login',
                    component: () => login
                    //   登录页
                }, {
                    path: '/log/logon',
                    name: 'logon',
                    component: () => logon
                    //   注册页
                }, {
                    path: '/log/back',
                    name: 'back',
                    component: () => back
                    //   找回
                },
            ]
        }
    ]
})

// 暴露路由
export default router

main.ts

// 引入createApp用于创建应用
import { createApp } from 'vue'

// 引入App跟组件
import App from './App.vue'

// 引入路由
import router from './router'

// 使用路由
app.use(router)
// 挂载整个容器到app容器中
app.mount('#app')

vue.页面

<script setup lang="ts" name="Person">
//展示区占位RouterView 
import {RouterView,RouterLink} from "vue-router";


</script>


<template>
  <div class="person">
    <h2>VUE路由测试</h2>
<!--    导航区-->
    <div>
      <RouterLink to="/lable" active-class="/logHome">首页</RouterLink>
<!--      active-class="" 激活时展示样式-->
      <RouterLink to="/login">登录</RouterLink>
      <RouterLink to="/logon">注册</RouterLink>
      <RouterLink to="/back">找回</RouterLink>
    </div>
<!--    展示区-->
    <div>
      展示组件
      <RouterView/>
    </div>
  </div>
</template>


<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

2` 路由的两个注意点

1.路由组件通常存放在pages 或者 views文件夹 ,一般组件通常放在 components文件夹

       路由组件: 靠路由的规则渲染出来的(不是通过标签展示出来的 比如: <ABAB/> 这种的)

        一般组件: 是咱们程序员亲手写的那种标签的组件

        

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

3` to的两种写法

基础写法:

1.字符串写法:<routerLink to="/login">登录</routerLink>

2.对象写法:   <routerLink :to="{path:'/login'}">登录</routerLink>

传参写法:

query写法:
<div>
      <!--      路由传参-->
      <ul>
        <!--        方法一-->
        <li v-for=" item in newList " :key="item.id">
          <RouterLink :to="`/?id=${item.id}&title=${item.title}`">{{ item.title }}</RouterLink>
        </li>
        <!--        方法二-->
        <li v-for=" item in newList " :key="item.id">
          <RouterLink
              :to="{
                path:'/',
                query:{
                  id:item.id,
                  title:item.title
                }
              }">
            {{ item.title }}
          </RouterLink>
        </li>
      </ul>
    </div>

参数接收

<script setup lang="ts">
    let route = useRoute()
    let {query} = toRefs(route)
</script>

<template>
    <ul>
        <li>编号:{{router.id}}</li>
        <li>标题:{{router.title}}</li>
    </ul>
</template>
params写法:

备注1:传递params参数时 ,若使用to的对象写法,必须使用name配置项,不能用path

备注2:传递params参数时 ,需要提前在规则中占位

vue组件内:

<script setup lang="ts" name="Person">

</script>

<template>
  <div class="person">
    <h2>VUE路由测试</h2>
    <!--    导航区-->
    <div>
      <!--      路由传参-->
      <ul>
        <!--        方法一-->
        <li v-for=" item in newList " :key="item.id">
          <RouterLink :to="`/${item.id}/${item.title}`">{{ item.title }}</RouterLink>
        </li>
      </ul>
      <ul>
        <!--        方法二-->
        <li v-for=" item in newList " :key="item.id">
          <RouterLink 
            :to="{
                name:"/" 
<!--注意因为之前路径用占位了所以这块用name跳转 且不能传对象和数组-->
                params:{
                    id:item.id,
                    title:item.title
                }
            }"
           >
            
            {{ item.title }}</RouterLink>
        </li>
      </ul>
    </div>
    <!--    展示区-->
    <div>
      展示组件
      <RouterView/>
    </div>
  </div>
</template>


<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

路由文件内:

routes: [
        {
            path: '/:x/:y?',//:x为必须传的 :y?为可传可不传
            name: 'lable',
            component: () => lable,
            // 标签页
        }
        ]

参数接收

<script setup lang="ts">
    let route = useRoute()
    let {query} = toRefs(route)
</script>

<template>
    <ul>
        <li>编号:{{router.params.id}}</li>
        <li>标题:{{router.params.title}}</li>
    </ul>
</template>

4` 路由器的工作模式

history模式

写法:

        vue2: mode:'hsitory

        vue3: history:createWebHistory()

        React: BrowserRouter 标签

优点:URL更加美观,不带有# ,更接近传统网站的URL

缺点:后期项目上线 ,需要服务端配合处理路径问题 ,否则刷新会有404错误

hash模式

写法:

        vue2: mode:'hash

        vue3: history:createWebHashHistory()

        React: HashRouter 标签

优点:兼容性更好 ,因为不需要服务器端处理路径

缺点:URL带有#不太美观 ,且在SEO优化方面相对较差

`5 路由的props配置

作用:让路由组件更方便的收到参数(可以将路由参数作为 props 传给组件)

路由:

routes: [
        {
            path: '/:x/:y?',//:x为必须传的 :y?为可传可不传
            name: 'lable',
            component: () => logHome,//组件地址
            // 第一种写法,将路由收到的所有params参数作为props传给路由组件
            props:true

            // 第二种写法,可以自己决定将什么作为props传给路由组件
            // 对象写法
            // 比如传query的
            // props(route){
            //    return route.query
            // }
        }
        ]

组件:

<script setup lang="ts">
    defineProps(['id','title'])
</script>

<template>
    <ul>
        <li>编号:{{id}}</li>
        <li>标题:{title}}</li>
    </ul>
</template>

6` replace属性

push: 栈模式 可以 前进 后退(默认)

replace: 覆盖 不能 前进 后退 在<RouterLink replace>

7` 编程试导航

脱离RouterLink实现跳转

路由组件的两个重要的属性: $route 和 $router 变成了两个 hooks

<script setup lang="ts" name="Person">

import {onMounted} from "vue";
import {useRouter} from "vue-router";

//路由器
const router = useRouter()

//组件挂在
onMounted(()=>{
  //展示事件
  setTimeout(()=>{
    //让路由实现跳转
    router.push('/')//push跳转 留下历史记录 可以前进 后退
    // router.replace('/')//replace跳转 不留记录
  },3000)//只能看3s
})
</script>

<template>
  <div class="person">

  </div>
</template>


<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

8` 路由的重定向

将本来应该跳转到/的跳转到/home那

routes: [
        {
            path: '/',
            name: 'lable',
            component: () => lable,
            // 标签页
        },{
            path: '/home',
            name: 'home',
            component: () => home,
            // 标签页
        },{
            path: '/',
            redirect: '/home'
        }
        ]

三` pinia 仓库

Pinia: 集中式状态(数据) 管理

1.搭建环境

下载: npm install pinia

main.ts

// 引入createApp用于创建应用
import {createApp} from 'vue'

//引入App跟组件
import App from './App.vue'

//引入pinia
import {createPinia} from 'pinia'

// 应用路由
const app = createApp(App)

// pinia注入
app.use(createPinia())

// 挂载整个容器到app容器中
app.mount('#app')

2.创建目录

创建一个目录名称为store

3.编写格式

count.ts

// 引入仓库
import {defineStore} from "pinia";

// 使用仓库 官方命名规则: use+文件名+Store
// defineStore参数1: id 推荐与文件名一致
// defineStore参数2: options 配置对象
export const useCountStore = defineStore('count',{
    
    // actions里面放置的是一给个的方法,用于相应组件种的"动作"
    actions:{
        increment(value){
            if(this.sum<10){
                console.log('此方法被调用',value)
                //修改数据
                this.sum+=value
            }
        }
    },
    //真正存储数据的地方
    state() {
        return{
            sum:7,
            school1:'辽工大',
            school2:'辽科大'
        }
    },
})

Count.vue

<script setup lang="ts" name="count">
import {ref} from "vue";
import {useCountStore} from "@/stores/count";

//数据
let n = ref(1)

//拿到数据并将其存入countStore
const countStore = useCountStore()

//函数
function add() {
  countStore.sum+=n.value
  console.log(n)
}


</script>

<template>
  <div class="person">
    <h2>当前求和为: {{ countStore.sum }}</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>
  </div>
</template>

<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

4`修改数据的三种方式:

<script setup lang="ts" name="count">
import {ref} from "vue";
import {useCountStore} from "@/stores/count";

//数据
let n = ref(1)

//拿到数据并将其存入countStore
const countStore = useCountStore()

//函数
function add() {
  //第一种修改方式:直接修改
  // countStore.sum+=n.value
  // countStore.sum = '北大'
  // countStore.sum = '清华'

  //第二种修改方式:批量修改
  // countStore.$patch({
  //   sum:888,
  //   school1: '北大',
  //   school2: '郊大'
  // })

  //第三种修改方法:调用在count.ts已经定义好了的方法
  countStore.increment(n.value)
}


</script>

<template>
  <div class="person">
    <h2>当前求和为: {{ countStore.sum }}</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>
  </div>
</template>

<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

5` storeToRefs 解构数据

将store内的数据结构出来(只解构其中的数据,不会对方法进行)

<script setup lang="ts" name="count">
import {ref} from "vue";
import {useCountStore} from "@/stores/count";
import {storeToRefs} from "pinia";

//数据
let n = ref(1)

//拿到数据并将其存入countStore
const countStore = useCountStore()

//解构
const {sum,school1,school2} = storeToRefs(countStore)

//函数
function add() {
  countStore.increment(n.value)
}


</script>

<template>
  <div class="person">
    <h2>当前求和为: {{ sum }}</h2>
    <h2>{{school1}}</h2>
    <h2>{{school2}}</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>
  </div>
</template>

<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

6` getters 加工数据

count.ts

// 引入仓库
import {defineStore} from "pinia";
import {state} from "vue-tsc/out/shared";

// 使用仓库 官方命名规则: use+文件名+Store
// defineStore参数1: id 推荐与文件名一致
// defineStore参数2: options 配置对象
export const useCountStore = defineStore('count',{

    // actions里面放置的是一给个的方法,用于相应组件种的"动作"
    actions:{
        increment(value){
            if(this.sum<10){
                console.log('此方法被调用',value)
                //修改数据
                this.sum+=value
            }
        }
    },
    //真正存储数据的地方
    state() {
        return{
            sum:7,
            school1:'辽工大',
            school2:'辽科大'
        }
    },

    //加工数据
    getters:{
        bigSum:state => state.sum*10,
        upperSchool():string{
            return this.school1+'很好'
        }
    }
})

Count.vue

<script setup lang="ts" name="count">
import {ref} from "vue";
import {useCountStore} from "@/stores/count";
import {storeToRefs} from "pinia";

//数据
let n = ref(1)

//拿到数据并将其存入countStore
const countStore = useCountStore()

//解构
const {sum,school1,school2,bigSum} = storeToRefs(countStore)

//函数
function add() {
  countStore.increment(n.value)
}


</script>

<template>
  <div class="person">
    <h2>当前求和为: {{ sum }} 放大十倍后 {{bigSum}}</h2>
    <h2>{{school1}}</h2>
    <h2>{{school2}}</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>
  </div>
</template>

<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

7` $subscribe

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

<script setup lang="ts" name="count">
import {ref} from "vue";
import {useCountStore} from "@/stores/count";
import {storeToRefs} from "pinia";

//数据
let n = ref(1)

//拿到数据并将其存入countStore
const countStore = useCountStore()

//侦听
countStore.$subscribe((mutation, state)=>{
  console.log('数据发生变化'+'本次修改信息为:'+mutation+'侦听的数据'+state)
})

//解构
const {sum} = storeToRefs(countStore)

//函数
function add() {
  countStore.increment(n.value)
}


</script>

<template>
  <div class="person">
    <h2>当前求和为: {{ sum }}</h2>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
    </select>
    <button @click="add">加</button>
  </div>
</template>

<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

8` store组合式写法

// 引入仓库
import {defineStore} from "pinia";
import {reactive} from "vue";
import axios from "axios";

// 使用仓库 官方命名规则: use+文件名+Store
// defineStore参数1: id 推荐与文件名一致
// defineStore参数2: options 配置对象
export const useCountStore = defineStore('count', () => {
    let countList = reactive(
        JSON.parse(localStorage.getItem('countList') as string) || []
    )

    //相当于getCount函数,相当于action
    async function getCount(value: any) {
        // 发送请求,下面这行的写法是: 连续解构赋值+重命名
        let {data: {content: title}} = await axios.get('https://api.uomg.com/api/...........')
        //把请求回来的字符串,包装成一个对象
        let obj = {id:nanoid(),title}
        //放到数组中
        countList.unshift(obj)
    }
    return {countList,getCount}
})

四` 通讯组件

1` props:

父传子 直接传递非函数 ;子传父 传递一个函数给子子调用父给的函数

2` 自定义事件

传参数的同时保留传递事件对象

@click="text(6,7,$event)"

自定义事件常用于子给父传值

子组件

<script setup lang="ts" name="Person">

// 声明事件
import {onMounted} from "vue";

const emit = defineEmits(['haha'])

onMounted(() => {
  setTimeout(() => {
    //触发haha
    emit('haha')
  }, 3000)
})
</script>

<template>
  <div class="person">
    <button @click="emit('haha')"></button>
  </div>
</template>


<style scoped>
.person {
  background-color: skyblue;
  box-shadow: 0 0 10px;
  border-radius: 10px;
  padding: 20px;
}
</style>

父组件

<script setup lang="ts">
import router from "@/router";
import Person from "@/components/Person.vue";

function qwe() {
  console.log('qwe')
}
</script>

<template>
  <h1 ref="title2">主页</h1>

<!--  给子组件Person绑定事件-->
  <person @haha="qwe"/>
</template>

<style scoped>

</style>

3` mitt

安装:npm i mitt

mitt属于工具 存放于文件夹 utils/tools 中

于文件夹内新建文件emitter.ts

//引入mitt
import mitt from "mitt";

//调用mitt得到emitter emitter能绑定事件触发事件
const emitter = mitt()

/**
 * .all 拿到所有绑定事件
 * .emit 触发某一个事件
 * .off 解绑某一个事件
 * .on 绑定某一个事件
 */
emitter.on('text1',()=>{
    console.log('被调用了')
})
emitter.emit('text1')


//暴露mitt
export default emitter

于main.ts内挂载

//引入mitter
import emitter from "@/utils/emitter";

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值