手拉手带你用 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…

系列文章:


相关文章
|
29天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
129 64
|
29天前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
34 8
|
29天前
|
JavaScript
在 Vue 3 中,如何使用 v-model 来处理自定义组件的双向数据绑定?
需要注意的是,在实际开发中,根据具体的业务需求和组件设计,可能需要对上述步骤进行适当的调整和优化,以确保双向数据绑定的正确性和稳定性。同时,深入理解 Vue 3 的响应式机制和组件通信原理,将有助于更好地运用 `v-model` 实现自定义组件的双向数据绑定。
|
1月前
|
存储 JavaScript 开发者
Vue 组件间通信的最佳实践
本文总结了 Vue.js 中组件间通信的多种方法,包括 props、事件、Vuex 状态管理等,帮助开发者选择最适合项目需求的通信方式,提高开发效率和代码可维护性。
|
1月前
|
存储 JavaScript
Vue 组件间如何通信
Vue组件间通信是指在Vue应用中,不同组件之间传递数据和事件的方法。常用的方式有:props、自定义事件、$emit、$attrs、$refs、provide/inject、Vuex等。掌握这些方法可以实现父子组件、兄弟组件及跨级组件间的高效通信。
|
1月前
|
缓存 JavaScript UED
Vue 中实现组件的懒加载
【10月更文挑战第23天】组件的懒加载是 Vue 应用中提高性能的重要手段之一。通过合理运用动态导入、路由配置等方式,可以实现组件的按需加载,减少资源浪费,提高应用的响应速度和用户体验。在实际应用中,需要根据具体情况选择合适的懒加载方式,并结合性能优化的其他措施,以打造更高效、更优质的 Vue 应用。
|
2月前
|
JavaScript 前端开发 测试技术
组件化开发:创建可重用的Vue组件
【10月更文挑战第21天】组件化开发:创建可重用的Vue组件
30 1
|
8天前
|
存储 缓存 NoSQL
解决Redis缓存数据类型丢失问题
解决Redis缓存数据类型丢失问题
132 85
|
2月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
84 6
|
5天前
|
缓存 监控 NoSQL
Redis经典问题:缓存穿透
本文详细探讨了分布式系统和缓存应用中的经典问题——缓存穿透。缓存穿透是指用户请求的数据在缓存和数据库中都不存在,导致大量请求直接落到数据库上,可能引发数据库崩溃或性能下降。文章介绍了几种有效的解决方案,包括接口层增加校验、缓存空值、使用布隆过滤器、优化数据库查询以及加强监控报警机制。通过这些方法,可以有效缓解缓存穿透对系统的影响,提升系统的稳定性和性能。