一、搭建工程(vite)
## 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"><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><button @click="changName">修改名字</button><button @click="changAge">年龄+1</button><button @click="showTel">点我查看联系方式</button></div>
</template><script lang="ts">export default {name:'Person',}
</script><!-- 下面的写法是setup语法糖 -->
<script setup lang="ts">console.log(this) //undefined// 数据(注意:此时的name、age、tel都不是响应式数据)let name = '张三'let age = 18let tel = '13888888888'// 方法function changName(){name = '李四'//注意:此时这么修改name页面是不变化的}function changAge(){console.log(age)age += 1 //注意:此时这么修改age页面是不变化的}function showTel(){alert(tel)}
</script>
扩展:上述代码,还需要编写一个不写setup
的script
标签,去指定组件名字,比较麻烦,我们可以借助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">
三、路由
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"><h2 class="title">Vue路由测试</h2><!-- 导航区 --><div class="navigate"><RouterLink to="/home" active-class="active">首页</RouterLink><RouterLink to="/news" active-class="active">新闻</RouterLink><RouterLink to="/about" active-class="active">关于</RouterLink></div><!-- 展示区 --><div class="main-content"><RouterView></RouterView></div></div>
</template><script lang="ts" setup name="App">import {RouterLink,RouterView} from 'vue-router'
</script>
2、两种路由工作模式
3、编程式导航
路由组件的两个重要的属性:$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
是一个保存:状态、业务逻辑 的实体,每个组件都可以读取、写入它。 -
它有三个概念:
state
、getter
、action
,相当于组件中的:data
、computed
和methods
。 -
具体编码:
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">// 引入对应的useXxxxxStore import {useSumStore} from '@/store/sum'// 调用useXxxxxStore得到对应的storeconst 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中的sumthis.sum += value}},//减decrement(value:number){if(this.sum > 1){this.sum -= value}}},/*************/ })
组件中调用action
// 使用countStore
const countStore = useCountStore()// 调用对应action
countStore.incrementOdd(n.value)
4、storeToRefs
-
借助
storeToRefs
将store
中的数据转为ref
对象,方便在模板中使用。 -
注意:
pinia
提供的storeToRefs
只会将数据做转换,而Vue
的toRefs
会转换store
中数据。
<template><div class="count"><h2>当前求和为:{{sum}}</h2></div>
</template><script setup lang="ts" name="Count">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就是stateconst talkList = reactive(JSON.parse(localStorage.getItem('talkList') as string) || [])// getATalk函数相当于actionasync 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"><h3>父组件</h3><h4>我的车:{{ car }}</h4><h4>儿子给的玩具:{{ toy }}</h4><Child :car="car" :getToy="getToy"/></div>
</template><script setup lang="ts" name="Father">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"><h3>子组件</h3><h4>我的玩具:{{ toy }}</h4><h4>父给我的车:{{ car }}</h4><button @click="getToy(toy)">玩具给父亲</button></div>
</template><script setup lang="ts" name="Child">import { ref } from "vue";const toy = ref('奥特曼')defineProps(['car','getToy'])
</script>
2、自定义事件
自定义事件常用于:子 => 父
事件对象$event
: 是调用emit
时所提供的数据,可以是任意类型!!!
3、mitt
与消息订阅与发布(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"> <!-- v-model的本质是下面这行代码 --> <input type="text" :value="userName" @input="userName =(<HTMLInputElement>$event.target).value" >
-
组件标签上的
v-model
的本质::moldeValue
+update:modelValue
事件。<!-- 组件标签上使用v-model指令 --> <AtguiguInput v-model="userName"/> <!-- 组件标签上v-model的本质 --> <AtguiguInput :modelValue="userName" @update:model-value="userName = $event"/>
AtguiguInput
组件中:<template><div class="box"><!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 --><!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件--><input type="text" :value="modelValue" @input="emit('update:model-value',$event.target.value)"></div> </template> <script setup lang="ts" name="AtguiguInput">// 接收propsdefineProps(['modelValue'])// 声明事件const emit = defineEmits(['update:model-value']) </script>
-
也可以更换
value
,例如改成abc
<!-- 也可以更换value,例如改成abc--> <AtguiguInput v-model:abc="userName"/> <!-- 上面代码的本质如下 --> <AtguiguInput :abc="userName" @update:abc="userName = $event"/>
AtguiguInput
组件中:<template><div class="box"><input type="text" :value="abc" @input="emit('update:abc',$event.target.value)"></div> </template> <script setup lang="ts" name="AtguiguInput">// 接收propsdefineProps(['abc'])// 声明事件const emit = defineEmits(['update:abc']) </script>
-
如果
value
可以更换,那么就可以在组件标签上多次使用v-model
<AtguiguInput v-model:abc="userName" v-model:xyz="password"/>
5、$attrs概述:$attrs
用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙)。
-
具体说明:
$attrs
是一个对象,包含所有父组件传入的标签属性。注意:
$attrs
会自动排除props
中声明的属性(可以认为声明过的props
被子组件自己“消费”了)
父组件:
<template><div class="father"><h3>父组件</h3><Child :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 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"><h3>子组件</h3><GrandChild v-bind="$attrs"/></div>
</template>
<script setup lang="ts" name="Child">import GrandChild from './GrandChild.vue'
</script>
孙组件:
<template><div class="grand-child"><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></div>
</template>
<script setup lang="ts" name="GrandChild">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"><h3>父组件</h3><h4>资产:{{ money }}</h4><h4>汽车:{{ car }}</h4><button @click="money += 1">资产+1</button><button @click="car.price += 1">汽车价格+1</button><Child/></div> </template> <script setup lang="ts" name="Father">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"><h3>我是孙组件</h3><h4>资产:{{ money }}</h4><h4>汽车:{{ car }}</h4><button @click="updateMoney(6)">点我</button></div> </template> <script setup lang="ts" name="GrandChild">import { inject } from 'vue';// 注入数据let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(x:number)=>{}})let car = inject('car') </script>
8、pinia
六、其他知识点
Vue3基础使用-CSDN博客https://blog.csdn.net/m0_71534259/article/details/139900842?spm=1001.2014.3001.5501
Vue2基础-CSDN博客https://blog.csdn.net/m0_71534259/article/details/139250173?spm=1001.2014.3001.5501