vue3(三)
1、父子路由在页面局部跳转的应用
2、通过replace实现不可后退跳转
3、通过钩子函数实现路由和页面跳转
4、路由重定向
5、pinia
6、修改通过useStore拿到的数据
7、storeToRefs
8、pinia里的getters的使用
9、store的组合式写法
1、父子路由在页面局部跳转的应用
假设有一页面NewsView.vue,里面的内容要如何实现如下图所示的布局,如何实现点击页面左半部分文字,而只有页面右半部分的内容发生变化呢?
不难想到,导航栏的内容是挂载到App.vue文件上的。稍加思索的是,要利用RouterView技术,将页面右(Detail.vue文件)挂载到页面左(NewsView.vue文件)上面,Detail.vue要获取到NewsView.vue的内容(参数)。顺便说一下,这里传递参数用的是query,还有一种传参是通过params传参,此处不细展开。具体代码实现如下,请耐心看完:
// 路由配置文件
import { createRouter, createWebHistory} from 'vue-router';
import HomeView from '../views/HomeView.vue';
import AboutView from '../views/AboutView.vue';
import NewsView from '../views/NewsView.vue';
import Detail from '../views/Detail.vue';
const router = createRouter({
history: createWebHistory(), // 路由器的工作模式
routes: [ // 路由规则
{
path: '/home',
name: 'home',
component: HomeView,
},
{
path: '/about',
name: 'about',
component: AboutView
},
{
path: '/news',
name: 'news',
component: NewsView,
children: [
{
path: 'detail',
name: 'detail',
component: Detail,
},
]
}
]
})
export default router
// 文件App.vue
<template>
<div class="navigate">
<RouterLink class="rlink" to="/home">点击此处回主页</RouterLink>
<RouterLink class="rlink" to="/about">点击此处显示关于</RouterLink>
<RouterLink class="rlink" to="/news">点击此处显新闻</RouterLink>
<!-- 普通标签写herf,RouterLink标签写to,或者 :to="{name:""}" -->
</div>
<router-view/> <!-- 挂载路由 -->
</template>
<script lang = "ts">
import { RouterView, RouterLink } from 'vue-router'
export default{
}
</script>
<style scoped>
div.navigate{
margin: 90px;
}
.rlink{
margin-right: 40px;
}
</style>
// 文件NewsView.vue
<template>
<div class = "layout1"> <!-- layout1是flex布局 -->
<div class="my">
<div class v-for="i in newslist" :key="i.id">
<!-- <RouterLink :to="`/news/detail?id=${i.id}&title=${i.title}`">{{ i.title }}</RouterLink> -->
<RouterLink
:to="{
path: '/news/detail',
query:{
id: i.id,
title: i.title,
content: i.content
}
}"> {{ i.title }}
</RouterLink>
</div>
</div>
<div class="my">
<RouterView /> <!-- 挂载路由实现本页面的局部跳转 -->
</div>
</div>
</template>
<script lang = 'ts'>
import { reactive } from 'vue'
export default{
name: "NewsView",
setup(){
const newslist = reactive([
{ id: 0, title: "2024年上半年软考报名时间", content: "2024-03-01" },
{ id: 1, title: "2024年上半年软考考试时间", content: "2024-05-25"},
{ id: 2, title: "2024年上半年软考成绩查询时间", content: "2024-08-01" },
]);
return {
newslist,
}
}
}
</script>
<style scoped>
div.my{
margin-right: 20px;
}
section.layout1 {
display: flex;
gap: 22px;
justify-content: center;
align-items: center;
}
</style>
// 文件Detail.vue
<template>
<div>
<li>编号: {{ route.query.id }}</li> <!--通过query传递参数-->
<li>标题: {{ route.query.title }}</li>
<li>内容: {{ route.query.content }}</li>
</div>
</template>
<script lang="ts">
import { useRoute } from 'vue-router';
export default{
name: "Detail",
setup(){
let route = useRoute();
return{
route
}
},
components:{
}
}
</script>
2、通过replace实现不可后退跳转
只需在RouterLink上加上replace关键字即可
<RouterLink replace class="rlink" to="/home">点击此处回主页</RouterLink>
<RouterLink replace class="rlink" to="/about">点击此处显示关于</RouterLink>
<RouterLink replace class="rlink" to="/news">点击此处显新闻</RouterLink>
3、通过钩子函数实现路由和页面跳转
在上述News.vue文件中改写成以下代码:
<script lang = 'ts'>
import { reactive } from 'vue';
import { useRouter } from 'vue-router';
import { onMounted } from 'vue';
export default{
name: "NewsView",
setup(){
const newslist = reactive([
{ id: 0, title: "2024年上半年软考报名时间", content: "2024-03-01" },
{ id: 1, title: "2024年上半年软考考试时间", content: "2024-05-25"},
{ id: 2, title: "2024年上半年软考成绩查询时间", content: "2024-08-01" },
]);
const router = useRouter();
onMounted(()=>{
setTimeout(()=>{
router.push(//可以写纯路径,也可写对象
{
name: "detail",
query: {
id: "xx",
title: "xx",
content: "xx"
}
}
);
},3000)
})
return {
newslist,
}
}
}
</script>
4、路由重定向
{
path: '/',
redirect: '/home'
},
5、pinia
集中式状态(数据管理):redux vuex pinia
简单理解:几十甚至上百组件的管理,包括数据传递等
介绍要用到的几个接口:
npm i nanoid
:作用返回一个id,这个id唯一
npm i pinia
引入接口后,main.ts需手动修改:
import { createPinia } from 'pinia'
const pinia = createPinia();
app.use(pinia);
接下来需要在src下创建store文件夹,
store中写文件,文件中存储你要用到的所有数据,以count为例子:
//文件 count.ts
import { defineStore } from 'pinia'; // 首先要导入pinia
export const useCountStore = defineStore('count', { // 这是官方推荐的命名规范!
state(){ // 存储数据的地方
return {
sum:6
}
actions: { // 里面放置的是一个一个的动作方法,用于响应组件的动作
},
getters:{
}
}
});
接下来,要在addNumber.vue中用到数据sum
<template>
<div class="count">
<div>当前求和为 {{ sum }}</div>
<select v-model="n">
<option :value="1">1</option>
<option :value="2">2</option>
<option :value="3">3</option>
</select>
<button @click="addSum">按下加</button>
<button @click="subSum">按下减</button>
</div>
</template>
<script lang="ts">
import { ref } from 'vue'
import { useCountStore } from '../store/count'; // 第一步导入对应的store
export default{
name: "addNumber",
setup(){
const countStore = useCountStore(); // 第二步,使用你导入的这个store
let sum = ref(countStore.sum); // 第三步,取出数据,接下来照常使用
let n = ref(1)
function addSum(){
sum.value = sum.value + n.value;
}
function subSum(){
sum.value = sum.value- n.value;
}
return {
sum,
n,
addSum,
subSum
}
},
components:{
}
}
</script>
<style>
div.count{
margin: 20px;
padding: 10px;
background-color: skyblue;
border-radius: 10px;
box-shadow: 0 0 5px;
}
select,button{
margin: 0 5px;
height: 25;
}
</style>
再举一个例子:
// 文件talk.ts
import { defineStore } from 'pinia'
import { reactive } from 'vue';
export const useTalkStore = defineStore('talk', {
state() {
return {
talklist: reactive([
{id: 'ftrfasdf01', title: '这是第一句话'},
{id: 'ftrfasdf02', title: '这是第二句话'},
{id: 'ftrfasdf03', title: '这是第三句话'},
]),
}
}
});
// LoveTalk.vue
<template>
<div class = count>
<button @click="getLoveTalk">点击此处获取一句话</button>
<ul v-for="i in talklist" :key="i.id">
<li>{{ i.title }}</li>
</ul>
</div>
</template>
<script lang="ts">
import { reactive } from 'vue';
import { nanoid } from 'nanoid';
import axios from 'axios';
import { useTalkStore } from '@/store/talk';
export default{
name: "LoveTalk",
setup(){
const talkStore = useTalkStore();
let talklist = reactive(talkStore.talklist);
async function getLoveTalk(){
let result = await axios.get("https://api.uomg.com/api/rand.qinghua?format=json");
let obj = { id: nanoid(), title: result.data.content };
talklist.unshift(obj); // 后出现的在上面
}
return {
talklist,
getLoveTalk
}
},
components:{
}
}
</script>
<style>
</style>
6、修改通过useStore拿到的数据
第一种修改方式,拿到countStore后直接修改
const countStore = useCountStore();
let n = ref(1) //用户选择的数字
function addSum(){
countStore.sum = countStore.sum + n.value;
}
function subSum(){
countStore.sum = countStore.sum - n.value;
}
第二种修改方式,通过$patch
countStore.$patch({sum: 10});
第三种修改方式,通过action
// 文件count.ts
import { defineStore } from 'pinia';
export const useCountStore = defineStore('count', {
state(){
return {
sum:0
}
},
actions: { //里面放置的是一个一个的动作方法,用于响应组件的动作
add(value:number){ // 变量value是number类型
this.sum = this.sum + number;
},
sub(value:number){
this.sum = this.sum - number;
}
}
});
// 文件addCount.vue,使用该数据的局部代码
function subSum(){
countStore.sub(n.value);
}
7、storeToRefs
storeToRefs只会关注store里的数据,不会关注里面的方法进行ref包裹
const personStore = usePersonStore()
const { id, name, address } = storeToRefs(personStore);
// 如果这里用的是toRefs,那么会将很多没用的数据解构出来
8、pinia里的getters的使用
Getters相当于Store的计算属性:
可以用defineStore()中的getters属性定义
getters中可以定义接受一个state作为参数的函数
// 文件src/store/count.ts
import { defineStore } from 'pinia';
export const useCountStore = defineStore('count', {
state(){ // 存储数据的地方
return {
sum:0
}
},
actions: { // 里面放置的是一个一个的动作方法,用于响应组件的动作
add(value:number){ // 变量value是number类型
this.sum = this.sum + value;
},
sub(value:number){
this.sum = this.sum - value;
console.log(this)
}
},
getters:{
// bigSum(state){ // 对数据不满意之后,加工的地方
// return state.sum * 100;
// },
// 或者你可以这样写:
bigSum():number{ // 告诉系统你返回值是number类型
return this.sum * 100;
}
// 官方鼓励这样写:bigSum:(state)=>{ state.sum*100 }
}
});
9、store的组合式写法
// 文件src/store/count.ts
import { defineStore } from 'pinia';
import { ref } from 'vue'
export const useCountStore = defineStore('count', ()=>{ // 接下来的内容当setup用
let sum = ref(99)
function add(value:number){
sum.value = sum.value + value;
}
function sub(value:number){
sum.value = sum.value - value;
}
return{
sum,
add,
sub
}
},
)