手拉手带你用 Vue3 + VantUI 写一个移动端脚手架 系列三 (状态缓存管理与列表组件)

简介: 状态缓存管理与列表组件

ef6742cdabe8fc0ad8f04fdf4a5b4f3.png

项目地址(持续迭代中):github.com/jyliyue/vit…

系列文章:

前言

上篇我们已经对移动端的布局策略做了介绍,本篇主要对该项目的 状态缓存管理体系 pinia 和 vue3 中 keep-alive 的应用做介绍,并带大家一起封装一个移动端常用的列表组件(包含 下拉刷新,上拉加载,加载完毕等等),将琐碎的列表状态属性内化,不需要在使用的地方定义一堆UI层相关的参数(例如: loading,finished... 等等),支持接口配置化,开发人员只需关注业务数据的获取和样式的编写,废话不多,马上开始!

状态缓存管理体系 Pinia

022754043b7053dff2f8a81d904e455.png

状态管理

本项目使用新版的状态管理 Pinia 代替 Vuex 做状态管理,比较直观的好处就是不用在区分 同步调用异步调用 了,store 的修改动作 action 作为常规函数调用,而不是使用 dispatch 方法或者是 commit 去调用,当然最重要的还是对 TS 支持比较友好

本地缓存管理

关于本地缓存,大家最先想到的就是 localstorage ,项目使用 pinia-plugin-persistedstate 插件实现 store 中数据的持久化,统一管理本地缓存,这里建议大家不要在代码中直接使用 localstorage.setItem 设置缓存数据,看过太多项目设置缓存东一块西一块的,更新维护及其头疼

代码实现

  • 安装插件
npm i pinia pinia-plugin-persistedstate
复制代码
  • 新建 store 目录模块
├── store
│   ├── index.js
│   └── modules
│       └── user.js
复制代码
  • 入口文件 index.js
// store 持久化
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
export default pinia
复制代码
  • 模块文件 user.js
/**
 * @description: 用户信息
 */
export const useUserStore = defineStore('user', {
    // 开启数据持久化
    persist: true,
    state: () => ({
        token: 'token',
        count: 1
    }),
    getters: {
        double() {
            return this.count * 2
        }
    },
    actions: {
        setToken(data) {
            this.token = data
        }
    }
})
复制代码

页面使用

import { useUserStore } from '@/store/modules/user'
const user = useUserStore()
user.setToken('my token')
console.log(user.token)   // my token
console.log(user.count)   // 1
console.log(user.double)  // 2
复制代码

注意如果使用解构的写法,会失去响应式,需要使用 storeToRefs 进行解构

const user = useUserStore()
const { token, count } = storeToRefs(user)
复制代码

KeepAlive 组件缓存

是一个 Vue 的内置组件,它的功能是在多个组件间动态切换时 缓存 被移除的组件实例

我们可以使用 keepAlive 来实现组件的缓存,保持组件的状态,借助这一特性,我们封装一个 组件,通过路由配置统一管理我们的页面组件缓存,并且能够自主控制缓存组件的销毁

50175f66bfe3c92c25ac19af328a860.png

关于  组件的封装,大家可以看我另一篇文章,有详细讲解,这里就不赘述了

传送门:5 分钟带你实现一个可控制缓存销毁的 keepAlive 组件

列表组件

dc53c6089ae1cb4bf537f69fe488a5a.png

思路

基于 Vue3 的特性,在封装组件时我们的思路都是把 UI逻辑业务逻辑 做区分,UI层只包含和交互响应相关的变量,业务层只和包含我们的展示数据,然后我们定义一个类 ListModelui 属性和 data 属性承接我们的UI逻辑和业务逻辑,并把相关的动作方法都在这个类里定义好

基于上述思路,我们来编写这个类和与它相关联的组件

ListModel.js

import { reactive } from 'vue'
class ListModel {
    constructor(options) {
        this.data = reactive({
            list: []
        })
        this.ui = reactive({
            loading: false,
            refreshing: false,
            finished: false,
            finishedText: '没有更多了',
            successText: '刷新成功'
        })
        this.options = options
    }
    onLoad = () => {
        this.options.getData().then((res) => {
            this.data.list = this.data.list.concat(res.data)
            this.ui.refreshing = false
            this.ui.loading = false
            if (this.data.list.length >= res.total) {
                this.ui.finished = true
            }
        })
    }
    onRefresh = () => {
        this.ui.finished = false
        this.ui.loading = true
        this.data.list = []
        this.onLoad()
    }
}
export default ListModel
复制代码

需要注意的点是父组件需要把获取数据的方法作为 options 参数传进来,这里定义为 getData

<app-list>

<script setup>
// This starter template is using Vue 3 <script setup> SFCs
import ListModel from '~/class/ListModle'
const props = defineProps({
    options: {
        type: Object,
        default: () => {}
    }
})
// 初始化列表模型
const listModel = new ListModel(props.options)
// 解构出UI层和业务层数据
const { ui, data } = listModel
// UI层解构出插件动作需要的参数
const { refreshing, successText, loading, finished, finishedText } = toRefs(ui)
// 业务层解构出列表数据
const { list } = toRefs(data)
// 模型层解构出方法
const { onRefresh, onLoad } = listModel
</script>
<template>
    <div class="app-list">
        <van-pull-refresh
            v-model="refreshing"
            :success-text="successText"
            @refresh="onRefresh"
        >
            <van-list
                v-model:loading="loading"
                @load="onLoad"
                :finished="finished"
                :finished-text="finishedText"
                :offset="100"
            >
                <slot name="content" :list="list"> </slot>
            </van-list>
        </van-pull-refresh>
    </div>
</template>
<style lang="scss" scoped>
.app-list {
    height: 100%;
    overflow-y: auto;
}
</style>
复制代码

这里有两个问题需要注意,可能其它同学也遇到过类似的

    <van-list> 进入时数据初始化loading两遍的问题

这里主要的原因是默认的触底判定距离太小导致的,导致多loading了一次,只要把 <van-list>的 offset 属性调大就好了

  • 我们需要在父组件去做列表样式的定制开发,那我们就要拿到组件内部的列表数据 list,这种情况我们可以使用作用域插槽 ,就是在 定义一个 list 属性把数据暴露出去

在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽。

我们也确实有办法这么做!可以像对组件传递 props 那样,向一个插槽的出口上传递 attributes

947f06c0dbcbb3f5dafe4840928467d.png

结合具名作用域插槽,我们的代码可以这样实现

子组件

<slot name="content" :list="list"> </slot>
复制代码

父组件

<app-list :options="options">
    <template #content="{ list }">
        <van-cell
            v-for="(item, index) in list"
            :key="index"
            :title="index"
        />
    </template>
</app-list>
复制代码

这样我们的组件就已经封装好了,接下来看看如何使用

组件的使用

组件使用方面我们只需要写好获取数据的方法以及写好列表样式就好了,这里我们简单点,直接使用  做展示,用 promise 模拟了下数据请求,关于字段命名各位可以根据自己项目的需要修改

<script setup>
// This starter template is using Vue 3 <script setup> SFCs
const options = {
    getData: () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve({
                    data: new Array(20),
                    total: 30
                })
            }, 1000)
        })
    }
}
</script>
<template>
    <app-page>
        <app-list :options="options">
            <template #content="{ list }">
                <van-cell
                    v-for="(item, index) in list"
                    :key="index"
                    :title="index"
                />
            </template>
        </app-list>
    </app-page>
</template>
<style lang="scss" scoped></style>
复制代码

这样我们的基础版  就已经开发完成了,大家可以看看效果

c070c6f550b63f6553d4d0ee6a62b7c.png

好了,本篇对移动端项目的 状态缓存管理体系列表组件 的封装讲解告一段落,下一篇我们将继续对列表组件进行完善,实现进入详情返回列表时记录位置,并做一个支持多 Tab 列表切换,每个列表都能保持位置的案例,敬请大家期待!

项目地址(持续迭代中):github.com/jyliyue/vit…

系列文章:


相关文章
|
15天前
|
JavaScript
在 Vue 中处理组件选项与 Mixin 选项冲突的详细解决方案
【10月更文挑战第18天】通过以上的分析和探讨,相信你对在 Vue 中使用 Mixin 时遇到组件选项与 Mixin 选项冲突的解决方法有了更深入的理解。在实际开发中,要根据具体情况灵活选择合适的解决方案,以确保代码的质量和可维护性。
67 7
|
14天前
|
缓存 JavaScript UED
Vue 的动态组件与 keep-alive
【10月更文挑战第19天】总的来说,动态组件和 `keep-alive` 是 Vue.js 中非常实用的特性,它们为我们提供了更灵活和高效的组件管理方式,使我们能够更好地构建复杂的应用界面。深入理解和掌握它们,以便在实际开发中能够充分发挥它们的优势,提升我们的开发效率和应用性能。
38 18
|
9天前
|
缓存 JavaScript UED
Vue 中实现组件的懒加载
【10月更文挑战第23天】组件的懒加载是 Vue 应用中提高性能的重要手段之一。通过合理运用动态导入、路由配置等方式,可以实现组件的按需加载,减少资源浪费,提高应用的响应速度和用户体验。在实际应用中,需要根据具体情况选择合适的懒加载方式,并结合性能优化的其他措施,以打造更高效、更优质的 Vue 应用。
|
9天前
|
存储 缓存 JavaScript
1.Vue的缓存组件 | 详解KeepAlive
1.Vue的缓存组件 | 详解KeepAlive
29 2
|
14天前
|
前端开发 UED
vue3知识点:Suspense组件
vue3知识点:Suspense组件
27 4
|
13天前
|
JavaScript 前端开发 测试技术
组件化开发:创建可重用的Vue组件
【10月更文挑战第21天】组件化开发:创建可重用的Vue组件
22 1
|
13天前
|
JavaScript 前端开发 Java
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
《vue3第五章》新的组件,包含:Fragment、Teleport、Suspense
25 2
|
14天前
|
Java
vue3知识点:Teleport组件
vue3知识点:Teleport组件
24 1
|
16天前
|
存储 JavaScript
Vue 组件间通信的方式有哪些?
Vue组件间通信主要通过Props、Events、Provide/Inject、Vuex(状态管理)、Ref、Event Bus等实现,支持父子组件及跨级组件间的高效数据传递与状态共享。
|
14天前
|
JavaScript 前端开发
vue全局公共组件自动引入并注册,开发效率直接起飞!
【10月更文挑战第14天】vue全局公共组件自动引入并注册,开发效率直接起飞!
38 1