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