Vue3基础(二)

一+一加1 2024-10-04 11:03:09 阅读 50

一、搭建工程(vite)

<code>## 1.创建命令

npm create vue@latest

## 2.具体配置

## 配置项目名称

√ Project name: vue3_test

## 是否添加TypeScript支持

√ Add TypeScript? Yes

## 是否添加JSX支持

√ Add JSX Support? No

## 是否添加路由环境

√ Add Vue Router for Single Page Application development? No

## 是否添加pinia环境

√ Add Pinia for state management? No

## 是否添加单元测试

√ Add Vitest for Unit Testing? No

## 是否添加端到端测试方案

√ Add an End-to-End Testing Solution? » No

## 是否添加ESLint语法检查

√ Add ESLint for code quality? Yes

## 是否添加Prettiert代码格式化

√ Add Prettier for code formatting? No

二、setup语法糖

<template>

<div class="person">code>

<h2>姓名:{ {name}}</h2>

<h2>年龄:{ {age}}</h2>

<button @click="changName">修改名字</button>code>

<button @click="changAge">年龄+1</button>code>

<button @click="showTel">点我查看联系方式</button>code>

</div>

</template>

<script lang="ts">code>

export default {

name:'Person',

}

</script>

<!-- 下面的写法是setup语法糖 -->

<script setup lang="ts">code>

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>

扩展:上述代码,还需要编写一个不写setupscript标签,去指定组件名字,比较麻烦,我们可以借助vite中的插件简化

第一步:npm i vite-plugin-vue-setup-extend -D

第二步:vite.config.ts

import { defineConfig } from 'vite'

import VueSetupExtend from 'vite-plugin-vue-setup-extend'

export default defineConfig({

plugins: [ VueSetupExtend() ]

})

第三步:<script setup lang="ts" name="Person">code>

三、路由

1、基本使用

npm i vue-router

路由配置文件

import {createRouter,createWebHistory} from 'vue-router'

import Home from '@/pages/Home.vue'

import News from '@/pages/News.vue'

import About from '@/pages/About.vue'

const router = createRouter({

history:createWebHistory(),

routes:[

{

path:'/home',

component:Home

},

{

path:'/about',

component:About

}

]

})

export default router

main.ts 入口文件

import router from './router/index'

app.use(router)

app.mount('#app')

App.vue

<template>

<div class="app">code>

<h2 class="title">Vue路由测试</h2>code>

<!-- 导航区 -->

<div class="navigate">code>

<RouterLink to="/home" active-class="active">首页</RouterLink>code>

<RouterLink to="/news" active-class="active">新闻</RouterLink>code>

<RouterLink to="/about" active-class="active">关于</RouterLink>code>

</div>

<!-- 展示区 -->

<div class="main-content">code>

<RouterView></RouterView>

</div>

</div>

</template>

<script lang="ts" setup name="App">code>

import {RouterLink,RouterView} from 'vue-router'

</script>

2、两种路由工作模式

3、编程式导航

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

import {useRoute,useRouter} from 'vue-router'

const route = useRoute()

const router = useRouter()

console.log(route.query)

console.log(route.parmas)

console.log(router.push)

console.log(router.replace)

4、重定向

{

path:'/',

redirect:'/about'

}

四、pinia

1、搭建环境

npm install pinia

src\main.ts

import { createApp } from 'vue'

import App from './App.vue'

/* 引入createPinia,用于创建pinia */

import { createPinia } from 'pinia'

/* 创建pinia */

const pinia = createPinia()

const app = createApp(App)

/* 使用插件 */{}

app.use(pinia)

app.mount('#app')

2、存储、读取数据

Store是一个保存:状态业务逻辑 的实体,每个组件都可以读取写入它。

它有三个概念:stategetteraction,相当于组件中的: datacomputedmethods

具体编码:src/store/count.ts

// 引入defineStore用于创建store

import {defineStore} from 'pinia'

// 定义并暴露一个store

export const useCountStore = defineStore('count',{

// 动作

actions:{},

// 状态

state(){

return {

sum:6

}

},

// 计算

getters:{}

})

使用数据

<template>

<h2>当前求和为:{ { sumStore.sum }}</h2>

</template>

<script setup lang="ts" name="Count">code>

// 引入对应的useXxxxxStore

import {useSumStore} from '@/store/sum'

// 调用useXxxxxStore得到对应的store

const sumStore = useSumStore()

</script>

3、修改数据

第一种修改方式,直接修改

countStore.sum = 666 第二种修改方式:批量修改

countStore.$patch({

sum:999,

school:'atguigu'

}) 第三种修改方式:借助action修改(action中可以编写一些业务逻辑)

import { defineStore } from 'pinia'

export const useCountStore = defineStore('count', {

/*************/

actions: {

//加

increment(value:number) {

if (this.sum < 10) {

//操作countStore中的sum

this.sum += value

}

},

//减

decrement(value:number){

if(this.sum > 1){

this.sum -= value

}

}

},

/*************/

})

组件中调用action

// 使用countStore

const countStore = useCountStore()

// 调用对应action

countStore.incrementOdd(n.value)

4、storeToRefs

借助storeToRefsstore中的数据转为ref对象,方便在模板中使用。

注意:pinia提供的storeToRefs只会将数据做转换,而VuetoRefs会转换store中数据。

<template>

<div class="count">code>

<h2>当前求和为:{ {sum}}</h2>

</div>

</template>

<script setup lang="ts" name="Count">code>

import { useCountStore } from '@/store/count'

/* 引入storeToRefs */

import { storeToRefs } from 'pinia'

/* 得到countStore */

const countStore = useCountStore()

/* 使用storeToRefs转换countStore,随后解构 */

const {sum} = storeToRefs(countStore)

</script>

5、getters

概念:当state中的数据,需要经过处理后再使用时,可以使用getters配置。

追加getters配置。

// 引入defineStore用于创建store

import {defineStore} from 'pinia'

// 定义并暴露一个store

export const useCountStore = defineStore('count',{

// 动作

actions:{

/************/

},

// 状态

state(){

return {

sum:1,

school:'atguigu'

}

},

// 计算

getters:{

bigSum:(state):number => state.sum *10,

upperSchool():string{

return this. school.toUpperCase()

}

}

})

组件中读取数据

const {increment,decrement} = countStore

let {sum,school,bigSum,upperSchool} = storeToRefs(countStore)

6、$subscribe

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

talkStore.$subscribe((mutate,state)=>{

console.log('LoveTalk',mutate,state)

localStorage.setItem('talk',JSON.stringify(talkList.value))

})

7、store组合式写法

import {defineStore} from 'pinia'

import axios from 'axios'

import {nanoid} from 'nanoid'

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}

})

五、组件通信

1、props

父传子:属性值是非函数

子传父:属性值是函数

父组件

<template>

<div class="father">code>

<h3>父组件</h3>

<h4>我的车:{ { car }}</h4>

<h4>儿子给的玩具:{ { toy }}</h4>

<Child :car="car" :getToy="getToy"/>code>

</div>

</template>

<script setup lang="ts" name="Father">code>

import Child from './Child.vue'

import { ref } from "vue";

// 数据

const car = ref('奔驰')

const toy = ref()

// 方法

function getToy(value:string){

toy.value = value

}

</script>

子组件

<template>

<div class="child">code>

<h3>子组件</h3>

<h4>我的玩具:{ { toy }}</h4>

<h4>父给我的车:{ { car }}</h4>

<button @click="getToy(toy)">玩具给父亲</button>code>

</div>

</template>

<script setup lang="ts" name="Child">code>

import { ref } from "vue";

const toy = ref('奥特曼')

defineProps(['car','getToy'])

</script>

2、自定义事件

自定义事件常用于:子 => 父

事件对象$event: 是调用emit时所提供的数据,可以是任意类型!!!

3、mitt

与消息订阅与发布(<code>pubsub)功能类似,可以实现任意组件间通信

npm i mitt

src\utils\emitter.ts

// 引入mitt

import mitt from "mitt";

// 创建emitter

const emitter = mitt()

// 创建并暴露mitt

export default emitter

 接收数据的组件中:绑定事件、同时在销毁前解绑事件

import emitter from "@/utils/emitter";

import { onUnmounted } from "vue";

// 绑定事件

emitter.on('send-toy',(value)=>{

console.log('send-toy事件被触发',value)

})

onUnmounted(()=>{

// 解绑事件

emitter.off('send-toy')

})

提供数据的组件,在合适的时候触发事件

import emitter from "@/utils/emitter";

function sendToy(){

// 触发事件

emitter.emit('send-toy',toy.value)

}

4、v-model

概述:实现 父↔子 之间相互通信。

前序知识 —— v-model的本质

<!-- 使用v-model指令 -->

<input type="text" v-model="userName">code>

<!-- v-model的本质是下面这行代码 -->

<input

 type="text" code>

 :value="userName" code>

 @input="userName =(<HTMLInputElement>$event.target).value"code>

>

组件标签上的v-model的本质::moldeValueupdate:modelValue事件。

<!-- 组件标签上使用v-model指令 -->

<AtguiguInput v-model="userName"/>code>

<!-- 组件标签上v-model的本质 -->

<AtguiguInput :modelValue="userName" @update:model-value="userName = $event"/>code>

AtguiguInput组件中:

<template>

 <div class="box">code>

   <!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 -->

<!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件-->

   <input

      type="text" code>

      :value="modelValue" code>

      @input="emit('update:model-value',$event.target.value)"code>

   >

 </div>

</template>

<script setup lang="ts" name="AtguiguInput">code>

 // 接收props

 defineProps(['modelValue'])

 // 声明事件

 const emit = defineEmits(['update:model-value'])

</script>

也可以更换value,例如改成abc

<!-- 也可以更换value,例如改成abc-->

<AtguiguInput v-model:abc="userName"/>code>

<!-- 上面代码的本质如下 -->

<AtguiguInput :abc="userName" @update:abc="userName = $event"/>code>

AtguiguInput组件中:

<template>

 <div class="box">code>

   <input

      type="text" code>

      :value="abc" code>

      @input="emit('update:abc',$event.target.value)"code>

   >

 </div>

</template>

<script setup lang="ts" name="AtguiguInput">code>

 // 接收props

 defineProps(['abc'])

 // 声明事件

 const emit = defineEmits(['update:abc'])

</script>

如果value可以更换,那么就可以在组件标签上多次使用v-model

<AtguiguInput v-model:abc="userName" v-model:xyz="password"/> code>

5、$attrs概述:$attrs用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。

具体说明:$attrs是一个对象,包含所有父组件传入的标签属性。

注意:$attrs会自动排除props中声明的属性(可以认为声明过的 props 被子组件自己“消费”了)

父组件:

<template>

 <div class="father">code>

   <h3>父组件</h3>

<Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/>code>

 </div>

</template>

<script setup lang="ts" name="Father">code>

import Child from './Child.vue'

import { ref } from "vue";

let a = ref(1)

let b = ref(2)

let c = ref(3)

let d = ref(4)

function updateA(value){

a.value = value

}

</script>

子组件:

<template>

<div class="child">code>

<h3>子组件</h3>

<GrandChild v-bind="$attrs"/>code>

</div>

</template>

<script setup lang="ts" name="Child">code>

import GrandChild from './GrandChild.vue'

</script>

孙组件:

<template>

<div class="grand-child">code>

<h3>孙组件</h3>

<h4>a:{ { a }}</h4>

<h4>b:{ { b }}</h4>

<h4>c:{ { c }}</h4>

<h4>d:{ { d }}</h4>

<h4>x:{ { x }}</h4>

<h4>y:{ { y }}</h4>

<button @click="updateA(666)">点我更新A</button>code>

</div>

</template>

<script setup lang="ts" name="GrandChild">code>

defineProps(['a','b','c','d','x','y','updateA'])

</script>

6、$refs、$parent

概述:

$refs用于 :父→子。

$parent用于:子→父。

原理如下:

属性 说明
$refs 值为对象,包含所有被ref属性标识的DOM元素或组件实例。
$parent 值为对象,当前组件的父组件实例对象。

7、provide、inject

概述:实现祖孙组件直接通信

具体使用:

在祖先组件中通过provide配置向后代组件提供数据

在后代组件中通过inject配置来声明接收数据

具体编码:

【第一步】父组件中,使用provide提供数据

<template>

 <div class="father">code>

   <h3>父组件</h3>

   <h4>资产:{ { money }}</h4>

   <h4>汽车:{ { car }}</h4>

   <button @click="money += 1">资产+1</button>code>

   <button @click="car.price += 1">汽车价格+1</button>code>

   <Child/>

 </div>

</template>

<script setup lang="ts" name="Father">code>

 import Child from './Child.vue'

 import { ref,reactive,provide } from "vue";

 // 数据

 let money = ref(100)

 let car = reactive({

   brand:'奔驰',

   price:100

})

 // 用于更新money的方法

 function updateMoney(value:number){

   money.value += value

}

 // 提供数据

 provide('moneyContext',{money,updateMoney})

 provide('car',car)

</script>

注意:子组件中不用编写任何东西,是不受到任何打扰的

【第二步】孙组件中使用inject配置项接受数据。

<template>

 <div class="grand-child">code>

   <h3>我是孙组件</h3>

   <h4>资产:{ { money }}</h4>

   <h4>汽车:{ { car }}</h4>

   <button @click="updateMoney(6)">点我</button>code>

 </div>

</template>

<script setup lang="ts" name="GrandChild">code>

 import { inject } from 'vue';

 // 注入数据

let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(x:number)=>{}})

 let car = inject('car')

</script>

8、pinia

六、其他知识点

Vue3基础使用-CSDN博客

icon-default.png?t=N7T8

https://blog.csdn.net/m0_71534259/article/details/139900842?spm=1001.2014.3001.5501

Vue2基础-CSDN博客

icon-default.png?t=N7T8

https://blog.csdn.net/m0_71534259/article/details/139250173?spm=1001.2014.3001.5501



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。