title: vue(TS)+路由器 date: 2025-01-28 12:00:00 tags:- 前端 categories:- 前端
Vue3-第二部分
這里是代碼中出現TS的,后面是路由器
現在先上代碼,步步分析。
eg1-props的使用
步步分析代碼(先理解,再實踐)
框架
先分析main.ts
常規出現,就是創建與引入
// 引入createApp用于創建應用 import {createApp} from 'vue' // 引入App根組件 import App from './App.vue' ? createApp(App).mount('#app')
分析組件內的person.vue
模版部分
<template><div class="person"><ul><li v-for="p in list" :key="p.id">{ {p.name} } -- { {p.age} }</li></ul></div> </template>
功能說明:
1. 渲染數據列表:
? list 是通過 props 傳遞給 Person 組件的。
? 使用 v-for 循環遍歷 list 數組,動態生成 <li> 列表項。
? 每個 li 顯示每個對象的 name 和 age。
2. 綁定唯一的 key:
? 使用 :key="p.id" 為每個列表項綁定唯一的 key,提高渲染效率。
腳本部分
<script lang="ts" setup name="Person">import { withDefaults } from 'vue'import { type Persons } from '@/types' ?// 接收list + 限制類型 + 限制必要性 + 指定默認值withDefaults(defineProps<{list?: Persons}>(), {list: () => [{ id: 'ausydgyu01', name: '康師傅·王麻子·特侖蘇', age: 19 }]}) </script>
上面的import是什么?
一般是導入工具與類型
? withDefaults:
? 用于為 defineProps 定義的 props 設置默認值。它接收兩個參數:
? defineProps 的返回值(包含 props 的類型約束)。
? 一個對象,用來指定每個 prop 的默認值。
? Persons:
? 從 @/types 導入的類型別名,表示一個由多個 person 對象組成的數組,符合以下結構:
也就是如果我要用到 prop的時候用
知識點解析:
1. defineProps:
? Vue 3 提供的 API,用于定義組件接收的 props。
? defineProps<{list?: Persons}>():
? 定義了一個可選的 list 屬性,類型為 Persons(數組,每個元素是一個符合 PersonInter 的對象)。
2. withDefaults:
? 用來為可選 props(如 list?)設置默認值。
? 默認值為:
[{ id: 'ausydgyu01', name: '康師傅·王麻子·特侖蘇', age: 19 }]
再分析index.ts
// 定義一個接口,用于限制person對象的具體屬性 export interface PersonInter {id: string,name: string,age: number, } ? // 一個自定義類型 export type Persons = PersonInter[]
1. 接口 PersonInter:
? 定義了 person 對象的結構,強制要求每個對象包含以下屬性:
? id:字符串,唯一標識。
? name:字符串,人員姓名。
? age:數字,人員年齡。
2. 類型別名 Persons:
? 定義了一個數組類型,數組的每個元素都必須符合 PersonInter 接口。
App.vue解析
<template><!-- 務必看懂下面這一行代碼 --><!-- <h2 a="1+1" :b="1+1" c="x" :d="x" ref="qwe">測試</h2> --><Person a="哈哈" /> </template>
靜態屬性:a = "1 + 1"是一個普通的字符串,直接作為屬性值
動態屬性:b = "1 + 1"是一個動態表達式,結果會被計算后作為屬性值
ref="qwe",綁定DOM引用,可以在JavaScript中通過ref操作這個DOM元素
2. 子組件 <Person /> 的使用:
? <Person /> 是導入的子組件,代表 person.vue 文件。
? a="哈哈" 是傳遞給 <Person /> 的一個普通屬性。
腳本部分
<script lang="ts" setup name="App">import Person from './components/Person.vue'import { reactive } from 'vue'import { type Persons } from '@/types' ?let x = 9 ?let personList = reactive<Persons>([{ id: 'asudfysafd01', name: '張三', age: 18 },{ id: 'asudfysafd02', name: '李四', age: 20 },{ id: 'asudfysaf)d03', name: '王五', age: 22 }]) </script>
1. 引入 Person 組件:
? import Person from './components/Person.vue' 引入 person.vue,使得 <Person /能夠在模板中使用。
-
定yi響應式數據:
-
reactive 的作用:
? 使得 personList 成為響應式數據。當 personList 或其內部的對象屬性發生變化時,Vue 會自動更新視圖。
整體邏輯總結
Index.ts 定義了personInter接口和Person類型,用來約束person數據結構
Person.vue 接收list作為props,通過withDefaults為list設置默認值
渲染list數據。動態生成列表
App.vue
定義一個響應式數據personalist,并可以通過props傳遞給pweson.vue
vue2生命周期
<template><div class="person"><h2>當前求和為:{ { sum } }</h2><button @click="add">點我sum+1</button></div> </template>
功能說明:
1. 數據綁定:
? { { sum } } 用來動態展示變量 sum 的值。
? 每次點擊按鈕,sum 的值增加 1。
2. 事件綁定:
? @click="add":綁定按鈕點擊事件,觸發 add 方法,更新 sum 的值。
<script lang="ts" setup name="Person">import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'// 數據let sum = ref(0)// 方法function add() {sum.value += 1}// 創建console.log('創建')// 掛載前onBeforeMount(() => {// console.log('掛載前')})// 掛載完畢onMounted(() => {console.log('子---掛載完畢')})// 更新前onBeforeUpdate(() => {// console.log('更新前')})// 更新完畢onUpdated(() => {// console.log('更新完畢')})// 卸載前onBeforeUnmount(() => {// console.log('卸載前')})// 卸載完畢onUnmounted(() => {// console.log('卸載完畢')}) </script>
app.vue
<template><Person v-if="isShow" /> </template>
1. <Person v-if="isShow" /:
? 條件渲染子組件 Person。
? 當 isShow 為 true 時,<Person / 會被掛載。
? 當 isShow 為 false 時,<Person /會被卸載。
腳本部分
<script lang="ts" setup name="App">import Person from './components/Person.vue'import { ref, onMounted } from 'vue'let isShow = ref(true)// 掛載完畢onMounted(() => {console.log('父---掛載完畢')}) </script>
功能解析:
1. 引入子組件:
? Person 是一個子組件,來自 ./components/Person.vue。
2. 響應式數據:
? let isShow = ref(true):定義了一個響應式布爾值 isShow,控制 <Person /> 的顯示和隱藏。
3. 生命周期鉤子:
? onMounted:在 App 組件掛載到 DOM 后執行。這里輸出 父---掛載完畢,用于標記父組件掛載完成。
運行流程
-
父組件掛載(App)
? isShow 默認為 true。
? <Person / 被掛載到 DOM 中。
? 控制臺輸出:
父---掛載完畢 創建 子---掛載完畢
. 子組件更新(Person)
? 點擊按鈕時,sum.value 增加 1,觸發子組件更新。
? 在更新階段,執行以下生命周期鉤子:
? onBeforeUpdate
? onUpdated
3. 子組件卸載(Person)
? 如果將 isShow 設置為 false(例如通過交互),<Person /> 會被卸載。
? 卸載階段執行:
? onBeforeUnmount
? onUnmounted
5. 總結
person.vue 的功能
? 通過按鈕點擊實現 sum 的動態更新。
? 使用 Vue 3 的生命周期鉤子監控組件的各個階段,包括掛載、更新、卸載。
app.vue 的功能
? 使用 v-if 控制子組件 Person 的掛載和卸載。
? 父組件負責管理子組件的存在與否,同時通過生命周期鉤子記錄父組件的掛載階段。
生命周期運行示意圖
1. 父組件掛載:
? onMounted -> 輸出:父---掛載完畢
2. 子組件掛載:
? 創建
? onBeforeMount(未輸出)
? onMounted -> 輸出:子---掛載完畢
3. 子組件更新:
? onBeforeUpdate
? onUpdated
4. 子組件卸載:
? onBeforeUnmount
? onUnmounted
對應頁面
Eg2-hook自定義
<template><div class="person"><h2>當前求和為:{ { sum } },放大10倍后:{ { bigSum } }</h2><button @click="add">點我sum+1</button><hr><img v-for="(dog,index) in dogList" :src="dog" :key="index"><br><button @click="getDog">再來一只小狗</button></div> </template><script lang="ts" setup name="Person">import useSum from '@/hooks/useSum'import useDog from '@/hooks/useDog'const {sum,add,bigSum} = useSum()const {dogList,getDog} = useDog() </script><style scoped>.person {background-color: skyblue;box-shadow: 0 0 10px;border-radius: 10px;padding: 20px;}button {margin: 0 5px;}li {font-size: 20px;}img {height: 100px;margin-right: 10px;} </style>
這個是person.vue
在模版部分,
? 圖片渲染:
? 使用 v-for 循環 dogList,通過動態綁定 src 和 key 屬性渲染小狗圖片。
在腳本部分
? 引入邏輯模塊:
? 從 useSum.ts 中引入了 sum、add 和 bigSum,負責數值的處理。
? 從 useDog.ts 中引入了 dogList 和 getDog,負責圖片列表的管理和 API 請求。
? 使用組合式 API:
? 通過解構的方式,將邏輯解耦到獨立的模塊中,提高代碼的可復用性。
在useDog.ts模塊
import { reactive, onMounted } from 'vue' import axios from 'axios'export default function () {// 數據let dogList = reactive(['https://images.dog.ceo/breeds/pembroke/n02113023_4373.jpg'])// 方法async function getDog() {try {let result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')dogList.push(result.data.message)} catch (error) {alert(error)}}// 鉤子onMounted(() => {getDog()})// 向外部提供東西return { dogList, getDog } }
功能分析:
1. 響應式數據:
? 使用 reactive 定義了圖片列表 dogList,默認包含一張圖片。
2. API 請求方法:
? getDog 方法使用 Axios 請求 https://dog.ceo 提供的小狗圖片 API。
? 將獲取的圖片 URL 推入 dogList 中。
? 通過 try-catch 捕獲請求錯誤。
3. 生命周期鉤子:
? 在組件掛載時 (onMounted) 自動調用 getDog,預加載一張小狗圖片。
關鍵點:
? 合理使用 reactive 管理數組的響應式更新。
? 在組件加載時預先獲取數據,優化用戶體驗。
在這里面有一個地方發送異步請求
zlet result = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
axios.get():
? 使用 axios 發起一個 HTTP GET 請求。
? 請求地址是 https://dog.ceo/api/breed/pembroke/images/random,這是一個提供隨機小狗圖片的 API。
? 返回的數據是一個包含 message 屬性的 JSON 對象
await:
? await 暫停代碼的執行,直到請求完成并返回結果。
? 返回值存儲在 result 變量中,具體數據保存在 result.data 中。
在useSum.ts模塊
import { ref, onMounted, computed } from 'vue'export default function () {// 數據let sum = ref(0)let bigSum = computed(() => {return sum.value * 10})// 方法function add() {sum.value += 1}// 鉤子onMounted(() => {add()})// 給外部提供東西return { sum, add, bigSum } }
功能分析:
1. 響應式數據:
? 使用 ref 定義了單一響應式數據 sum。
? 使用 computed 定義了計算屬性 bigSum,動態計算 sum 的 10 倍值。
2. 方法:
? add 方法使 sum 增加 1。
3. 生命周期鉤子:
? 在 onMounted 中調用 add,在組件加載時使 sum 初始值為 1。
路由
在 Vue 3 中,路由管理通常通過 Vue Router 實現。路由的主要功能是實現頁面的導航和組件的動態渲染。以下是關于路由的核心概念和代碼實現的詳細說明。
1. 什么是路由?
路由是一種通過 URL 映射組件或視圖的方式。它允許用戶在單頁應用(SPA)中導航,而無需重新加載整個頁面。
例如:
? /home 映射到 Home 組件。
? /about 映射到 About 組件。
頁面組件
<template><div class="home"><img src="http://www.atguigu.com/images/index_new/logo.png" alt=""></div> </template>
? 功能:展示一個居中的圖片。
? 樣式:通過 flex 布局將內容水平和垂直居中。
? 用途:作為首頁內容。
<script setup lang="ts" name="Home"> </script>
? 作用:
? 表示該組件使用 Vue 3 的 <script setup> 語法。
? lang="ts" 表示代碼使用 TypeScript,增加類型安全。
? name="Home" 給組件命名為 Home,方便調試和遞歸調用。
樣式部分
<style scoped>.home {display: flex;justify-content: center;align-items: center;height: 100%;} </style>
? 作用:
? 定義組件的樣式。
? scoped 表示樣式只作用于當前組件,不影響其他組件。
? 細節解析:
? display: flex;:
? 使用 Flex 布局,使子元素容易居中對齊。
? justify-content: center;:
? 子元素水平居中。
? align-items: center;:
? 子元素垂直居中。
? height: 100%;:
? 根容器的高度設置為父級容器的 100%。
About.vue
<template><div class="about"><h2>大家好,歡迎來到尚硅谷直播間</h2></div> </template><script setup lang="ts" name="About"></script><style scoped> .about {display: flex;justify-content: center;align-items: center;height: 100%;color: rgb(85, 84, 84);font-size: 18px; } </style>
News.vue
<template><div class="news"><ul><li><a href="#">新聞001</a></li><li><a href="#">新聞002</a></li><li><a href="#">新聞003</a></li><li><a href="#">新聞004</a></li></ul></div> </template><script setup lang="ts" name="News"></script><style scoped> /* 新聞 */ .news {padding: 0 20px;display: flex;justify-content: space-between;height: 100%; } .news ul {margin-top: 30px;list-style: none;padding-left: 10px; } .news li>a {font-size: 18px;line-height: 40px;text-decoration: none;color: #64967E;text-shadow: 0 0 1px rgb(0, 84, 0); } </style>
路由配置
// 創建一個路由器,并暴露出去// 第一步:引入createRouter import {createRouter,createWebHistory} from 'vue-router' // 引入一個一個可能要呈現組件 import Home from '@/components/Home.vue' import News from '@/components/News.vue' import About from '@/components/About.vue'// 第二步:創建路由器 const router = createRouter({history:createWebHistory(), //路由器的工作模式(稍后講解) routes:[ //一個一個的路由規則{path:'/home',component:Home},{path:'/news',component:News},{path:'/about',component:About},] })// 暴露出去router export default router
? createRouter:
? 創建一個路由實例。
? createWebHistory:
? 使用 HTML5 的歷史記錄模式。
? routes:
? 定義路由規則,每條規則對應一個路徑和組件。
應用入口
// 引入createApp用于創建應用 import {createApp} from 'vue' // 引入App根組件 import App from './App.vue' // 引入路由器 import router from './router'// 創建一個應用 const app = createApp(App) // 使用路由器 app.use(router) // 掛載整個應用到app容器中 app.mount('#app')
App.vue
模版部分
<template><div class="app"><h2 class="title">Vue路由測試</h2><!-- 導航區 --><div class="navigate"><RouterLink to="/home" active-class="xiaozhupeiqi">首頁</RouterLink><RouterLink to="/news" active-class="xiaozhupeiqi">新聞</RouterLink><RouterLink to="/about" active-class="xiaozhupeiqi">關于</RouterLink></div><!-- 展示區 --><div class="main-content"><RouterView></RouterView></div></div> </template>
<RouterLink:
? Vue Router 提供的導航組件,類似于 HTML 的 <a> 標簽。
? to="/home":指定點擊該鏈接時跳轉的路由路徑。
? active-class="xiaozhupeiqi":定義激活時的樣式類名,當鏈接的路由匹配當前路徑時會自動應用。
? 導航鏈接功能:
? 首頁:跳轉到 /home 路由。
? 新聞:跳轉到 /news 路由。
? 關于:跳轉到 /about 路由。
在展示區
<div class="main-content"><RouterView></RouterView> </div>
? <RouterView :
? Vue Router 提供的內置組件,用于渲染當前路由匹配的組件。
? 根據用戶點擊的導航鏈接,<RouterView會動態切換為對應的組件內容,例如 Home.vue、News.vue 或 About.vue。
腳本部分
<script lang="ts" setup name="App">import { RouterView, RouterLink } from 'vue-router' </script>
逐行解析
(1) lang="ts"
? 表示當前腳本部分使用 TypeScript,增強類型安全。
? 允許對變量、函數等進行類型聲明。
(2) setup
? 使用 Vue 3 的組合式 API 的語法糖。
? 在 <script setup中定義的變量和方法,可以直接在模板中使用,無需顯式返回。
(3) name="App"
? 為當前組件指定名稱為 App。
? 在開發者工具(如 Vue DevTools)中調試時,可以看到組件名稱為 App,便于區分。
(4) 引入 Vue Router 的組件
import { RouterView, RouterLink } from 'vue-router'
? RouterLink:用于定義路由導航鏈接。
? RouterView:用于動態渲染路由匹配的組件。
樣式部分
.title {text-align: center;word-spacing: 5px;margin: 30px 0;height: 70px;line-height: 70px;background-image: linear-gradient(45deg, gray, white);border-radius: 10px;box-shadow: 0 0 2px;font-size: 30px; } .navigate {display: flex;justify-content: space-around;margin: 0 100px; } .navigate a {display: block;text-align: center;width: 90px;height: 40px;line-height: 40px;border-radius: 10px;background-color: gray;text-decoration: none;color: white;font-size: 18px;letter-spacing: 5px; }
xiaozhupeiqi 激活后呈現的
這個就是超鏈接<a的時候,未點擊前呈現的
視頻中沒有講這一部分,少了一個知識點的講解就是routeLink->轉換為<a標簽
Query 參數-路由
Header.ts
<template><h2 class="title">Vue路由測試</h2> </template><script setup lang="ts" name="Header"></script><style scoped>.title {text-align: center;word-spacing: 5px;margin: 30px 0;height: 70px;line-height: 70px;background-image: linear-gradient(45deg, gray, white);border-radius: 10px;box-shadow: 0 0 2px;font-size: 30px;} </style>
About.ts
<template><div class="about"><h2>大家好,歡迎來到尚硅谷直播間</h2></div> </template><script setup lang="ts" name="About">import {onMounted,onUnmounted} from 'vue'onMounted(()=>{console.log('About組件掛載了')})onUnmounted(()=>{console.log('About組件卸載了')}) </script><style scoped> .about {display: flex;justify-content: center;align-items: center;height: 100%;color: rgb(85, 84, 84);font-size: 18px; } </style>
Detail.ts
<template><ul class="news-list"><li>編號:{ { query.id } }</li><li>標題:{ { query.title } }</li><li>內容:{ { query.content } }</li></ul> </template><script setup lang="ts" name="About">import {toRefs} from 'vue'import {useRoute} from 'vue-router'let route = useRoute()let {query} = toRefs(route)</script><style scoped>.news-list {list-style: none;padding-left: 20px;}.news-list>li {line-height: 30px;} </style>
腳本部分
解析
(1) useRoute
? 定義:
? useRoute 是 Vue Router 提供的組合式 API,用于獲取當前路由對象。
? 作用:
? 返回當前激活的路由信息,包括路徑、參數、查詢字符串等。
? 返回值:
? route 是一個響應式對象,包含當前路由的所有信息,例如:
{path: "/news",query: {id: "123",title: "Vue Router",content: "這是一個簡單的示例"},params: { ... },... }
(2) toRefs
? 定義:
? toRefs 是 Vue 的組合式 API,用于將響應式對象的屬性轉換為獨立的響應式引用。
? 作用:
? 將 route.query 轉換為響應式引用,使得在模板中訪問 query 的屬性時,能夠保持響應式更新。
? 代碼作用:
let { query } = toRefs(route)
(3) 數據流程
? useRoute() 獲取當前路由信息。
? 通過 toRefs(route) 解構出 query,用于動態綁定數據。
Params
<template><div class="news"><!-- 導航區 --><ul><li v-for="news in newsList" :key="news.id"><!-- 第一種寫法 --><!-- <RouterLink :to="`/news/detail/${news.id}/${news.title}/${news.content}`">{ {news.title} }</RouterLink> --><!-- 第二種寫法 --><RouterLink :to="{name:'xiang',params:{id:news.id,title:news.title,content:news.content}}">{ {news.title} }</RouterLink></li></ul><!-- 展示區 --><div class="news-content"><RouterView></RouterView></div></div> </template><script setup lang="ts" name="News">import {reactive} from 'vue'import {RouterView,RouterLink} from 'vue-router'const newsList = reactive([{id:'asfdtrfay01',title:'很好的抗癌食物',content:'西藍花'},{id:'asfdtrfay02',title:'如何一夜暴富',content:'學IT'},{id:'asfdtrfay03',title:'震驚,萬萬沒想到',content:'明天是周一'},{id:'asfdtrfay04',title:'好消息!好消息!',content:'快過年了'}])</script><style scoped> /* 新聞 */ .news {padding: 0 20px;display: flex;justify-content: space-between;height: 100%; } .news ul {margin-top: 30px;/* list-style: none; */padding-left: 10px; } .news li::marker {color: #64967E; } .news li>a {font-size: 18px;line-height: 40px;text-decoration: none;color: #64967E;text-shadow: 0 0 1px rgb(0, 84, 0); } .news-content {width: 70%;height: 90%;border: 1px solid;margin-top: 20px;border-radius: 10px; } </style>
這個是new.vue
Pinia
Count.vue
模版部分
<template><div class="count"><h2>當前求和為:{ { 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><button @click="minus">減</button></div> </template>
? 使用 { { sum } } 動態綁定 sum 的值,顯示當前的求和結果。
2. 選擇操作數:
? 使用 <select 元素,讓用戶選擇一個數字(1、2 或 3)。
? 通過 v-model.number="n" 雙向綁定選中的值到變量 n,并將其轉換為數值。
-
加減操作:
? 點擊“加”按鈕時調用 add 方法。
? 點擊“減”按鈕時調用 minus 方法。
腳本部分
<script setup lang="ts" name="Count">import { ref } from "vue";// 數據let sum = ref(1) // 當前求和let n = ref(1) // 用戶選擇的數字// 方法function add(){sum.value += n.value}function minus(){sum.value -= n.value} </script>
1. ref 定義響應式數據:
? sum:當前求和,初始值為 1。
? n:用戶選擇的數字,初始值為 1。
? 響應式數據會自動更新綁定到模板的內容。
2. 方法功能:
? add:將選中的數字 n.value 加到 sum.value 上。
? minus:從 sum.value 中減去 n.value。
<script setup lang="ts" name="LoveTalk">import { reactive } from 'vue'import axios from "axios";import { nanoid } from 'nanoid'// 數據let talkList = reactive([{id:'ftrfasdf01',title:'今天你有點怪,哪里怪?怪好看的!'},{id:'ftrfasdf02',title:'草莓、藍莓、蔓越莓,今天想我了沒?'},{id:'ftrfasdf03',title:'心里給你留了一塊地,我的死心塌地'}])// 方法async function getLoveTalk(){// 發請求,下面這行的寫法是:連續解構賦值+重命名let {data:{content:title} } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')// 把請求回來的字符串,包裝成一個對象let obj = {id:nanoid(),title}// 放到數組中talkList.unshift(obj)} </script>
在app.vue里面寫
<template><Count/><br><LoveTalk/> </template><script setup lang="ts" name="App">import Count from './components/Count.vue'import LoveTalk from './components/LoveTalk.vue' </script>
npm i pinia
import {createApp} from 'vue' import App from './App.vue' // 第一步:引入pinia import {createPinia} from 'pinia'const app = createApp(App) // 第二步:創建pinia const pinia = createPinia() // 第三步:安裝pinia app.use(pinia) app.mount('#app')
小菠蘿出來啦!!!
存儲+讀取數據
創建一個store文件夾
Count.ts
import {defineStore} from 'pinia'export const useCountStore = defineStore('count',{// 真正存儲數據的地方state(){return {sum:6}} })
Lovetalk.ts
import {defineStore} from 'pinia'export const useTalkStore = defineStore('talk',{// 真正存儲數據的地方state(){return {talkList:[{id:'ftrfasdf01',title:'今天你有點怪,哪里怪?怪好看的!'},{id:'ftrfasdf02',title:'草莓、藍莓、蔓越莓,今天想我了沒?'},{id:'ftrfasdf03',title:'心里給你留了一塊地,我的死心塌地'}]}} })
修改數據
import {defineStore} from 'pinia'export const useCountStore = defineStore('count',{// actions里面放置的是一個一個的方法,用于響應組件中的“動作”actions:{increment(value){console.log('increment被調用了',value)if( this.sum < 10){// 修改數據(this是當前的store)this.sum += value}}},// 真正存儲數據的地方state(){return {sum:6,school:'atguigu',address:'宏福科技園'}} })
import {defineStore} from 'pinia' import axios from 'axios' import {nanoid} from 'nanoid'export const useTalkStore = defineStore('talk',{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)}},// 真正存儲數據的地方state(){return {talkList:[{id:'ftrfasdf01',title:'今天你有點怪,哪里怪?怪好看的!'},{id:'ftrfasdf02',title:'草莓、藍莓、蔓越莓,今天想我了沒?'},{id:'ftrfasdf03',title:'心里給你留了一塊地,我的死心塌地'}]}} })
現在展開解釋
整體功能
這段代碼通過 Pinia 定義了一個 Store,用于管理一個情話列表 talkList,并提供了一個方法 getATalk 來向 API 請求新的情話并添加到 talkList 中。
1. 引入的依賴
import { defineStore } from 'pinia' import axios from 'axios' import { nanoid } from 'nanoid'
解析
1. defineStore:
? 從 pinia 中引入,用于定義一個新的 Store。
? Store 是狀態管理的核心,用于存儲和管理全局共享的狀態。
2. axios:
? 用于發送 HTTP 請求。
? 這里通過 axios.get() 從 https://api.uomg.com/api/rand.qinghua 獲取隨機土味情話。
3. nanoid:
? 一個小型的 ID 生成工具。
? 用于為每條情話生成唯一的 ID,確保 talkList 中的每條情話都有一個獨特的標識。
2. 定義 Store
export const useTalkStore = defineStore('talk', { ... })
解析
1. export const useTalkStore:
? 定義了一個 Store,命名為 useTalkStore。
? 這個名字的命名規則通常是 use 開頭,以表明它是一個 Store。
2. defineStore('talk', {...}):
? 'talk' 是這個 Store 的唯一標識符,用于區分其他 Store。
? 第二個參數是 Store 的配置對象,包含 state 和 actions 等。
3. state:存儲數據
state() {return {talkList: [{ id: 'ftrfasdf01', title: '今天你有點怪,哪里怪?怪好看的!' },{ id: 'ftrfasdf02', title: '草莓、藍莓、蔓越莓,今天想我了沒?' },{ id: 'ftrfasdf03', title: '心里給你留了一塊地,我的死心塌地' }]} }
解析
1. state:
? 一個函數,返回一個對象,這個對象定義了 Store 中的數據。
? 在這里,state 定義了一個情話列表 talkList。
2. talkList:
? 是一個數組,存儲了情話的初始數據。
? 每條情話是一個對象,包含以下字段:
? id:情話的唯一標識符。
? title:情話的具體內容。
3. 響應式特性:
? Pinia 的 state 是響應式的。
? 當 talkList 數據發生變化時,綁定到 talkList 的視圖會自動更新。
4. actions:定義方法
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)} }
解析
1. actions:
? 定義了 Store 中的方法,通常用于處理復雜邏輯或修改狀態。
? getATalk 是一個異步方法,用于從 API 獲取新的情話并更新 talkList。
2. getATalk 的工作流程:
? 發送請求:
let { data: { content: title } } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
? 使用 axios.get() 發送請求,從 API 獲取情話。
? 解構賦值提取 content 字段,并將其重命名為 title。
? 創建新對象:
let obj = { id: nanoid(), title }
? 使用 nanoid() 生成一個唯一的 ID。
? 創建一個包含 id 和 title 的新情話對象。
? 更新 talkList:
this.talkList.unshift(obj)
? 使用 unshift 方法,將新情話添加到 talkList 的開頭。
? 由于 talkList 是響應式的,更新數據后,綁定到 talkList 的 UI 會自動更新。
1. storeToRefs 的使用
storeToRefs 是 Pinia 提供的一個工具函數,主要用于從 Store 中提取狀態(state)和 Getter 的響應式引用,確保解構后不會丟失響應性。
import { storeToRefs } from 'pinia'const store = useSomeStore() const { stateProp, getterProp } = storeToRefs(store)
作用
? 將 state 和 getter 轉換為響應式 ref。
? 解構 Store 中的屬性時,防止響應性丟失。
2. getters 的使用
Pinia 的 getters 是類似于 Vuex 中的計算屬性,用于對 state 的值進行派生計算。
state: () => ({talkList: [{ id: '1', title: '情話一' },{ id: '2', title: '情話二' }]}),getters: {talkCount: (state) => state.talkList.length // 返回情話總數} })
import { useTalkStore } from '@/stores/talkStore'const talkStore = useTalkStore()// 直接訪問 getter console.log(talkStore.talkCount) // 輸出情話總數
3. $subscribe 的使用
$subscribe 是 Pinia 提供的一個方法,用于監聽 Store 中 state 和 action 的變化。
語法
store.$subscribe((mutation, state) => {console.log(mutation) // 包含 type 和 payloadconsole.log(state) // 當前狀態 })
4. store 組合式寫法
Pinia 支持組合式 API(Composition API)風格的 Store 定義。
export const useTalkStore = defineStore('talk', () => {const talkList = ref([{ id: '1', title: '情話一' },{ id: '2', title: '情話二' }])const talkCount = computed(() => talkList.value.length)const addTalk = (id, title) => {talkList.value.push({ id, title })}return { talkList, talkCount, addTalk } }) import { useTalkStore } from '@/stores/talkStore'const talkStore = useTalkStore()// 調用方法和訪問屬性 talkStore.addTalk('3', '情話三') console.log(talkStore.talkCount) // 輸出 3