Skip to content
页面概要

状态管理(Pinia)

对于每个大项目来说,使用状态树 (store) 管理状态 (state) 十分有必要。

并且本项目在 vue 原有的基础上扩展了 asyncData 方法seo 方法,状态树 (store)也是嵌入其中的。

Pinia 说明

Pinia 是一个状态管理库,由 Vue 核心团队维护,对 Vue 2 和 Vue 3 都可用。

现有用户可能对 Vuex 更熟悉,它是 Vue 之前的官方状态管理库。由于 Pinia 在生态系统中能够承担相同的职责且能做得更好,因此 Vuex 现在处于维护模式。它仍然可以工作,但不再接受新的功能。对于新的应用程序,建议使用 Pinia。

事实上,Pinia 这款产品最初是为了探索 Vuex 的下一个版本,整合了核心团队关于 Vuex 5 的许多想法。最终,Pinia 已经实现了想要在 Vuex 5 中提供的大部分内容,同时 Evan You 已经于 2021年11月24日 在推特宣布 Pinia 正式成为 vuejs 官方的状态库,意味着 Pinia 就是 Vuex 5 。

相比于 Vuex,Pinia 提供了更简洁直接的 API,并提供了组合式风格的 API,最重要的是,在使用 TypeScript 时它提供了非常好的类型推导。

Pinia 使用

你可以在 /web/store 下创建文件,或者在每个页面目录下创建文件,如下样列:

/web/store/user.ts 中定义

import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => {
    return {
      name: '',
    };
  },
});

1
2
3
4
5
6
7
8
9
10

/web/views/About/store.ts 中定义

/**
 * About store
 * @author LiQingSong
 */
import { defineStore } from 'pinia';
import { ResponseData } from '@/utils/request';
import {
  TableListItem,
  PaginationConfig,
  ResponseDataType,
  TableListQueryParams,
} from './data.d';
import { queryList } from './service';

export interface IAboutState {
  loading: boolean;
  list: TableListItem[];
  pagination: PaginationConfig;
}

export const useAboutStore = defineStore('about', {
  state(): IAboutState {
    return {
      loading: false,
      list: [],
      pagination: {
        total: 0,
        current: 1,
        pageSize: 10,
      },
    };
  },
  actions: {
    async getList(params?: TableListQueryParams) {
      try {
        this.loading = true;
        const response: ResponseData<ResponseDataType> = await queryList(
          params
        );
        const data = response.data;
        if (data) {
          this.list = data.list || [];
          this.pagination = {
            ...this.pagination,
            total: data.total,
            current: params ? params.current || 1 : 1,
          };
        }
        this.loading = false;
      } catch (error: any) {
        console.log('error useAboutStore getList', error);
      }
    },
  },
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

/web/views/About/index.vue 中使用





 


 
 





 
 








 


















































<script lang="ts">
import { defineComponent, computed, watch } from 'vue'
import { useRoute } from "vue-router"
import Pagination from "@/components/Pagination/base.vue";
import { useAboutStore, IAboutState } from "./store";
export default defineComponent({
  async asyncData({store, route}) {
    const aboutStore = useAboutStore(store);
    await aboutStore.getList({current: Number(route.query.page || 1)});
  }
})
</script>
<script lang="ts" setup>
// 读取数据
const aboutStore = useAboutStore();
const tableData = computed<IAboutState>(()=>aboutStore.$state);

const route = useRoute();
const page = computed(()=>route.query.page);

watch(page,()=> {
  if(route.path !== '/about') {
    return;
  }
 aboutStore.getList({current: Number(page.value || 1)});
})
</script>
<template>
  <div class="about">
    <h1>This is an about page</h1>
    <div class="box">
      <ul v-loading="tableData.loading">
        <li v-for="item in tableData.list">

          <div>
            <router-link :to="{ path: '/detail', query: { id: item.id }}">
              {{item.title}}
            </router-link>
            <span>{{item.addtime}}</span>
          </div>
        
        </li>
      </ul>
      <div>
        <pagination :total="tableData.pagination.total" :current-page="tableData.pagination.current" page-url="/about?page={page}"></pagination>
      </div>
    </div>
  </div>
</template>
<style lang="scss" scoped>
.box {
  box-sizing: border-box;
  min-width: 200px;
  max-width: 980px;
  margin: 0 auto;
  padding: 45px;
  text-align: left;
  ul {
    li {
      padding: 5px 0;
     div {
      display: flex;
      justify-content: space-between;
     }
    }
  }
}
@media (max-width: 767px) {
  .box {
    padding: 15px;
  }
}
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

TIP

详细规则请查看 Pinia 官方文档

Released under the MIT License.