uni-app项目后台页面数据更新方案

前情

uni-app是我比较喜欢的跨平台框架,它能开发小程序/H5/APP(安卓/iOS),重要的是对前端开发友好,自带的IDE可视化的运行和打包也让开发体验也非常棒,公司项目就是主推uni-app,为了解决页面数据更新操作,我实现了一套页面数据更新方案

为什么我要专门针对数据更新做方案

目前我遇到一种场景,我当前在订单详情页,详情页会展示当前订单的一些数据,特别是订单状态,同时会显示一个申请售后的按钮,此时我点击申请售后跳转售后提交页并成功发起了售后,我再执行back操作,订单详情页状态还是停留在前一个状态,同时申请售后按钮也还在,用户再点击又会跳售后申请页,再发起就会报错,因为服务端做了验证

其实像类似这样的场景有很多,如订单列表页有一个待支付订单,我点击订单跳转到订单详情完成支付再回来发现订单还在待支付状态,面对这种种给人不太好的体验,我在开发联调的时候忍了很久,一直在想怎么解决这种尴尬现象

常见方案

  • 通过uni-app的事件通知机制

在前台页面做了一些操作,如果想更新后台的页面数据,后台页面做事件监听,前台页面发起事件通知,通知后台页面做数据更新

/* by 01130.hk - online tools website : 01130.hk/zh/worldtime.html */
// 后台页面
// 在页面销毁的时候记得清除事件监听,此处使用的事件名是update,真正项目最好每一个页面都唯一
uni.$on('update', function(data) {
    console.log('监听到事件来自 update,携带参数 msg 为:' + data.msg);
    // 这里发起请求
});

// 前台页面
uni.$emit('update', { msg: '页面更新' });
  • 全局状态管理

就是把数据放全局状态管理,如vuex/pinia,如果是vue3项目就直接用pinia,前台页面直接调用全局状态管理的方法以达到更新后台页面的的数据

  • onShow生命钩子里暴力拉取更新

uni-app为页面引入了onShow生命钩子,每次页面从后台切到前台都会触发,最暴力的做法就是每次onShow触发的时候做接口请求,这样确实是可以解决数据更新延后的问题,但是问题就是会发生很多无用的接口请求

  • 给页面引入下拉刷新

再有一种方法就是给当前页实现下拉刷新,个人觉得也是不错的方案,是现在很多主流项目在用的,像京东、拼多多、淘宝等,但这主要用在像商品列表,订单列表等,当然如果你项目中有实现下拉刷新,那对于我这个数据更新方案就可有可无了,加上也是不错的,我感觉二者可以并存

我使用的方案

上面这些方法都是可取的,主要看个人选择吧,我的方案也是依赖生命周期钩子, 但是做了一点点优化,我一直都在想有什么方式我可以通知在后台的页面做更新了,我首先想到的就是通过全局状态管理或者事件通知,我此时在想真的需要用到全局状态管理和事件通知么,有没有一种更简单的方式了,于是几番思索后想到了如下方案:

我的方案很简单,就是全局管理一个对象,对象记录需要做页面数据更新的key,同时暴露一些方法可以对key进行增删重置操作,同时封装一个hooks,在页面的onShow生命钩子里检查当前页是否在需要做数据更新的key里,如果是那就做数据更新,同时我还做了一个小尝试,如果onHide和onShow中间间隔达到一定时间,这里我定义为30s,也做下数据更新,这只是我自己加的,也难说是好是坏,先这样用着,如果后续发现不是特别理想再修改也不迟

全局对象

下面是我定义的全局对象,pagesKey记录需要做数据更新的 key,还有一个离开需要数据更新的最小时间,再暴露一些必要方法

/* by 01130.hk - online tools website : 01130.hk/zh/worldtime.html */
// 全局页面key管理
export const pageToBeUpdated = {
  pagesKey: [],

  // 离开30s后,再回来触发更新
  delayTime: 30000,

  add(pageKey) {
    if (!this.pagesKey.includes(pageKey)) {
      this.pagesKey.push(pageKey)
    }
  },

  remove(pageKey) {
    const index = this.pagesKey.indexOf(pageKey)
    if (index !== -1) {
      this.pagesKey.splice(index, 1)
    }
  },

  reset() {
    this.pagesKey = []
  },

  isExist(pageKey) {
    return this.pagesKey.includes(pageKey)
  },
}

封装的hook

// @ts-expect-error
import { onShow, onLoad, onUnload, onHide } from '@dcloudio/uni-app'
import { pageToBeUpdated } from '@/config/global'

/**
 * 页面更新
 * @param pageKey 页面key
 * @param callback 回调函数,一般是页面更新key
 */
export const usePageUpdate = (pageKey: string, callback?: () => void) => {
  let leaveTime = Date.now()
  // 页面显示时,判断当前页面是否在需要更新的页面key中,如果在,证明当前页面是从后台返回的,应该触发回调更新函数
  onShow(() => {
    if (pageToBeUpdated.isExist(pageKey)) {
      callback && callback()
      return
    }
    // 如果离开超过pageToBeUpdated.delayTime时间,再回来就触发更新
    if (Date.now() - leaveTime > pageToBeUpdated.delayTime) {
      callback && callback()
    }
  })

  // 页面隐藏时,证明当前页面也不在前台了,也应该移除排当前页面的key,避免重复触发
  onHide(() => {
    leaveTime = Date.now()
    pageToBeUpdated.remove(pageKey)
  })

  // 如果页面触发了onLoad,证明是初次进入,应该去排除页面key,避免重复触发
  onLoad(() => {
    pageToBeUpdated.remove(pageKey)
  })

  // 页面卸载时,证明当前页面也不在后台了,也应该移除排当前页面的key,避免重复触发,其实可有可无,因为onLoad的时候就已经移除了
  onUnload(() => {
    pageToBeUpdated.remove(pageKey)
  })
}

其中做了一点优化,如果当前页面onLoad钩子触发了,那就不是从后台切回了,此时就要移除当前页面的key,避免页面多次触发数据请求,因为我当前项目基本所有页面的接口请求都放在onLoad钩子里了

使用方式

在需要做数据更新的页面使用hook,关键示例代码如下:

import { usePageUpdate } from '@/hooks/pageUpdate'

usePageUpdate('rentalAfterSalesDetail', getOrderDetailRequest)

注:usePageUpdate第一个参数是当前页面的 key,要做到唯一,第二方法是当前页面拉取服务端数据并更新状态的方法

收集页面key,在需要的地方做页面key收集,等待页面检查,关键示例代码如下:

import { pageToBeUpdated } from '@/config/global'

pageToBeUpdated.add('rentalAfterSalesDetail')

注:在收集页如果你做了一些可能修改了运行在后台的页面状态的操作的时候,就可以做页面key收集

小结

此方案需要你全权把握数据更新,你要知道哪些操作可能会导致运行的后台的页面数据状态有改变,同时又有一个保底更新逻辑,离开页面多久触发更新,这个暂时我定为30s,像抖音/支付宝如果你小程序切到后台超过5分钟就会被杀掉了,而微信是30分钟,所以我选30s,应该是所有平台都是可兼容的了,此举我也难说是好是坏,暂时我项目就是这样用,但是至少可以解决一种问题,就是用户较长时间切到后台再回来保证数据是比较新的

这是我的 uni-app项目的后台数据页面更新方案,已使用在生产中,目前还没有发现什么问题,如果后续发现什么问题再做更新吧,解决方案千千万,世上没有最好,只有更好,如果你有更好的解决方案,可以分享出来,或者你发现此方案有什么问题,也可以提出来一起探讨

好好学习!天天向上!
<think> 我们正在讨论的是uni-app更新页面数据的方法。根据用户之前的问题,他通过按钮调用云函数修改数据数据后,希望更新页面并停留在当前页面。结合uni-app的特性,我们可以采用以下方法: 核心思路:通过修改绑定在视图层的数据,触发视图层的自动更新。 步骤: 1. 在页面的data中定义需要响应的数据。 2. 通过按钮触发事件,在事件处理函数中调用云函数(uniCloud.callFunction)。 3. 在云函数执行成功的回调中,更新页面数据(使用this.setData或直接修改数据并在Vue2中通过$forceUpdate,在Vue3中则通过reactive响应式自动更新)。 4. 视图层会自动重新渲染。 具体实现: 一、Vue2(Options API)写法: 在methods中定义方法,通过this.setData(小程序语法兼容)或直接修改data属性(需要this.$forceUpdate())来更新。 注意:uni-app中,我们通常使用this.key = value的方式修改data,然后调用this.$forceUpdate(),或者使用Vue.set。但是,在uni-app中,我们可以直接使用赋值方式,因为uni-app已经做了处理,在微信小程序平台会自动转换为setData,在H5平台则使用Vue的响应式。然而,对于数组或对象中某一项的修改,我们需要使用Vue.set或数组的变异方法(如splice)来确保响应式。 二、Vue3(Composition API)写法: 使用ref或reactive定义响应式数据,修改数据后,视图会自动更新。 三、使用uni-app页面通讯方法(如uni.$emituni.$on)来实现跨页面更新。例如,在修改数据页面触发事件,在需要更新页面监听事件并更新数据。 根据引用[4],uni-app也支持通过uni.navigateTo的events参数实现页面间事件通信(2.8.9+)。 但是,用户要求停留在当前页面,所以我们主要关注当前页面数据更新。 示例代码(Vue2 Options API): 1. 页面数据定义: ```javascript data() { return { workOrders: [] // 工单列表数据 } } ``` 2. 按钮事件处理: ```javascript methods: { updateOrderStatus(orderId) { // 调用云函数 uniCloud.callFunction({ name: 'update_work_order', data: { id: orderId, status: 'completed' }, success: (res) => { if (res.result.code === 0) { // 找到该工单在数组中的索引 const index = this.workOrders.findIndex(item => item.id === orderId); if (index !== -1) { // 使用Vue.set确保响应式 this.$set(this.workOrders, index, { ...this.workOrders[index], status: 'completed' }); // 或者使用数组的splice方法(也是响应式的) // this.workOrders.splice(index, 1, { ...this.workOrders[index], status: 'completed' }); uni.showToast({ title: '更新成功' }); } } }, fail: (err) => { uni.showToast({ title: '更新失败', icon: 'none' }); } }); } } ``` 3. 视图绑定: ```html <view v-for="(order, index) in workOrders" :key="order.id"> {{ order.title }} - 状态:{{ order.status }} <button @click="updateOrderStatus(order.id)">标记完成</button> </view> ``` 四、全量刷新: 如果局部更新复杂,也可以重新获取整个数据列表(但性能较低): ```javascript success: (res) => { if (res.result.code === 0) { this.loadWorkOrders(); // 重新加载列表数据 } } // 加载列表数据方法 loadWorkOrders() { uniCloud.callFunction({ name: 'get_work_orders', success: (res) => { this.workOrders = res.result.data; } }); } ``` 五、使用定时刷新(结合引用[3]考虑性能): 在页面加载时设置定时器,在页面卸载时清除: ```javascript onLoad() { this.timer = setInterval(() => { this.loadWorkOrders(); }, 60000); // 每分钟刷新一次 }, onUnload() { clearInterval(this.timer); } ``` 注意:引用[3]提到,页面静态DOM过多会影响加载速度,所以在使用定时刷新时,要避免频繁更新大量数据。 六、跨页面更新: 如果需要在其他页面修改数据后,返回到本页面也能看到更新,可以在onShow生命周期中刷新数据: ```javascript onShow() { this.loadWorkOrders(); } ``` 或者使用全局事件: 在修改数据页面(可能是当前页面,也可能是其他页面): ```javascript uni.$emit('updateWorkOrders'); ``` 在当前页面监听: ```javascript onLoad() { uni.$on('updateWorkOrders', this.loadWorkOrders); }, onUnload() { uni.$off('updateWorkOrders', this.loadWorkOrders); } ``` 总结:uni-app更新页面数据的主要方法是直接修改响应式数据(Vue2使用Vue.set或数组变异方法,Vue3使用ref/reactive),并通过调用云函数在数据库操作成功后执行数据更新。同时,可以根据需要选择局部更新、全量刷新、定时刷新或全局事件通知等方式。 引用说明: - 引用[3]提醒我们注意页面静态DOM过多会影响性能,所以更新数据时应尽量局部更新,避免全量刷新导致整个列表重新渲染。 - 引用[4]提供了页面间通信的方法,我们可以使用全局事件总线(uni.$emit/uni.$on)来实现跨页面更新。 相关问题: 1. uni-app中如何实现局部数据更新? 2. 如何避免频繁刷新列表数据导致的性能问题? 3. 在uni-app中,如何实现页面数据同步更新? 4. 在Vue3的Composition API中,更新响应式数据有什么不同? 5. 如何使用uni-app页面事件通信通道? </think> ### uni-app 更新页面数据的方法详解 在 uni-app更新页面数据主要依靠 Vue 的响应式系统,以下是核心方法和具体实现: #### 一、基础数据更新方法 1. **直接赋值更新** 修改 `data` 中的值触发视图自动更新: ```javascript export default { data() { return { count: 0, user: { name: '张三', age: 25 } } }, methods: { updateData() { // 基本数据类型更新 this.count += 1 // 对象属性更新(需保留响应性) this.user = { ...this.user, age: 26 } // 推荐方式 // 或 this.$set(this.user, 'age', 26) } } } ``` 2. **数组更新方法** 使用 Vue 的响应式数组方法: ```javascript updateList() { // 正确方式 this.list.splice(0, 1, { id: 100, name: '新项目' }) // 替换第一项 this.list.push({ id: Date.now(), name: '新增项' }) // 错误方式(不会触发更新) // this.list[0] = { ... } } ``` #### 二、云函数更新后刷新页面 结合云开发实现数据修改后的页面刷新: ```javascript async updateOrder(orderId) { try { // 1. 调用云函数修改数据 const res = await uniCloud.callFunction({ name: 'updateOrder', data: { id: orderId, status: '完成' } }) if (res.result.code === 0) { // 2. 局部更新当前页面数据 const index = this.orders.findIndex(o => o.id === orderId) if (index !== -1) { this.orders.splice(index, 1, res.result.data) // 替换整条数据 uni.showToast({ title: '更新成功' }) } } } catch (e) { uni.showToast({ title: '更新失败', icon: 'none' }) } } ``` #### 三、跨页面数据更新方案 1. **全局状态管理(简易版)** ```javascript // 在 app.vue 中定义 export default { globalData: { refreshOrders: false } } // 修改数据页面 getApp().globalData.refreshOrders = true // 订单列表页面(onShow中) onShow() { if (getApp().globalData.refreshOrders) { this.loadData() getApp().globalData.refreshOrders = false } } ``` 2. **事件总线通信** ```javascript // 注册全局事件(main.js) Vue.prototype.$eventBus = new Vue() // 数据更新后触发 this.$eventBus.$emit('order-updated') // 列表页面监听 mounted() { this.$eventBus.$on('order-updated', this.loadData) } ``` 3. **页面通信通道(2.8.9+)** ```javascript // 跳转时建立通道 uni.navigateTo({ url: '/pages/edit?id=123', events: { refreshData: () => this.loadData() } }) // 编辑页面修改数据后 const eventChannel = this.getOpenerEventChannel() eventChannel.emit('refreshData') ``` #### 四、性能优化建议 1. **避免大数据更新** ```javascript // 坏实践(全量更新数据) this.bigList = newArray // 可能卡顿 // 好实践(增量更新) newItems.forEach(item => { this.$set(this.bigList, index, item) }) ``` 2. **减少静态 DOM 数量** 根据引用[3],静态 DOM 过多影响加载性能,建议: - 分页加载数据 - 使用 `v-if` 惰性渲染 - 复杂列表使用 `scroll-view` 3. **定时刷新控制** ```javascript onLoad() { this.refreshTimer = setInterval(this.loadData, 30000) }, onHide() { clearInterval(this.refreshTimer) // 页面隐藏时停止刷新 }, onShow() { this.refreshTimer = setInterval(this.loadData, 30000) } ``` > **操作流程总结** > 1. 使用响应式 API 修改数据(`$set`/`splice`) > 2. 云函数修改后返回完整数据直接替换 > 3. 大数据集使用增量更新 > 4. 跨页面更新用全局状态或事件总线 > 5. 页面隐藏时暂停后台刷新[^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值