UniApp生命周期全解析:从白屏问题到性能优化实战 1. 从“页面白屏”说起为什么必须搞懂UniApp生命周期前几天一个刚接手UniApp项目的同事跑来找我一脸困惑。他负责的页面在App端冷启动时偶尔会先白屏一两秒然后才正常显示数据。他检查了网络请求、数据加载逻辑都没问题。我让他把onLoad和onShow里的代码给我看看果然问题就出在这里——他把一个本应在onShow里执行的、依赖页面已渲染完成的DOM操作放在了onLoad里。这个看似微小的顺序错误在复杂的原生渲染环境下被放大导致了短暂的白屏。这个案例让我觉得是时候系统性地聊聊UniApp的生命周期了。这绝不是一个枯燥的API列表背诵题。对于任何使用UniApp进行跨平台开发的开发者而言生命周期是你理解应用运行脉络、掌控页面状态、优化性能表现、乃至解决各种诡异Bug的“地图”。无论是处理App的冷热启动、小程序的页面栈管理还是H5的路由跳转生命周期都是贯穿始终的指挥棒。简单来说UniApp的生命周期是一系列在特定时间点由框架自动触发的函数。你的代码在这些函数里执行就像演员在导演喊“Action”时开始表演。理解每个“Action”喊出的时机和上下文你才能演好这出戏。很多人觉得生命周期就是onLoad,onShow,onHide那几个钩子但真正用起来尤其是在处理平台差异、状态恢复、性能优化时才会发现里面的门道深着呢。接下来我会抛开简单的罗列带你从应用启动到页面销毁完整走一遍UniApp的生命周期旅程。我们会重点关注那些容易混淆的点、平台差异带来的“坑”以及如何利用生命周期写出更健壮、高效的代码。无论你是刚接触UniApp的新手还是已经写过一些页面但总觉得有些地方“不太顺”的开发者相信这篇内容都能给你带来新的启发。2. 应用级生命周期整个App的“呼吸”与“心跳”当我们启动一个UniApp应用时最先登场的不是某个页面而是应用本身的生命周期。它定义了应用从启动、运行到被销毁的完整过程。理解它是处理全局状态、监听应用行为的基础。2.1onLaunch应用的“诞生”时刻onLaunch是整个应用生命周期中第一个、也是最重要的一个钩子。它在应用初始化完成时触发且在整个应用运行期间仅触发一次。你可以把它想象成应用的“构造函数”。核心作用与常见场景获取启动参数这是onLaunch最典型的用途。当用户通过分享链接、扫码、或其他应用跳转打开你的UniApp时携带的参数会在这里获取。// app.vue 或 App.vue (Vue 3 组合式API) export default { onLaunch(options) { console.log(App Launch, 启动参数:, options); // options 可能包含path, query, scene, shareTicket等 // 例如小程序扫码进入options.query 会包含二维码的参数 if (options.query options.query.inviteCode) { // 处理邀请码逻辑 this.globalData.inviteCode options.query.inviteCode; } } }初始化全局服务在这里初始化一些全局只需要一次的服务比如第三方SDK如统计、推送、IM、全局状态管理库如Vuex或Pinia的实例化、以及你自定义的全局工具类。onLaunch() { // 初始化云开发如果使用 wx.cloud.init({...}); // 初始化全局状态存储 this.globalData { userInfo: null, systemInfo: null }; // 获取手机系统信息用于后续的样式适配 uni.getSystemInfo({ success: (res) { this.globalData.systemInfo res; console.log(系统信息:, res); } }); }检查更新与热重启对于App端可以在这里检查是否有新版本并提示用户更新。对于小程序端可以在这里处理强制更新逻辑。注意onLaunch的执行时机非常早此时首页的onLoad可能还没有执行。因此不要在这里执行耗时操作否则会拖慢应用的首次渲染速度。复杂的初始化如大型数据预加载应考虑异步进行或放到首页的onLoad中。2.2onShow与onHide应用的“前台”与“后台”这两个钩子标志着应用是否对用户可见它们可以触发多次。onShow当应用从后台切换到前台显示时触发。例如用户按了Home键将应用挂起然后又从任务管理器切回来。onHide当应用从前台切换到后台隐藏时触发。例如用户按了Home键或切换到其他应用。它们与页面级onShow/onHide的区别应用级的钩子监听的是整个App的可见性变化而页面级的只关心当前页面的可见性。例如在App内从A页面跳转到B页面A页面的onHide和B页面的onShow会触发但应用级的onShow和onHide不会触发因为整个App依然在前台。典型应用场景数据同步与恢复在onShow中可以检查登录态是否过期并重新拉取用户信息或同步未保存的数据。播放器控制对于音视频播放类应用通常在onHide时暂停播放在onShow时根据业务逻辑决定是否恢复播放。统计与日志记录用户每次打开和离开应用的时间用于分析用户活跃度。onShow() { console.log(App Show); // 应用从后台唤醒检查登录态 this.checkLoginStatus(); // 恢复全局定时器或动画 if (this.globalData.backgroundTimer) { this.resumeBackgroundTask(); } }, onHide() { console.log(App Hide); // 应用进入后台暂停不必要的任务以节省资源 this.pauseBackgroundTask(); // 如果需要可以尝试保存当前的草稿或状态 this.autoSaveDraft(); }2.3onError与onUnhandledRejection应用的“健康监测”这两个是错误监听钩子用于捕获全局的JavaScript异常和未处理的Promise拒绝。onError监听JavaScript执行错误。onUnhandledRejection监听未被.catch()处理的Promise拒绝。为什么它们很重要在跨平台开发中一些平台如小程序的原生环境对错误的容忍度较低一个未捕获的错误可能导致整个页面白屏甚至应用闪退。通过全局监听我们可以将错误信息上报到服务器方便排查线上问题同时可以给用户一个友好的错误提示而不是一个崩溃的界面。onError(err) { console.error(App全局错误捕获:, err); // 将错误信息、发生页面、设备信息等上报到监控平台 this.reportErrorToServer({ type: js_error, message: err.message, stack: err.stack, timestamp: Date.now() }); // 可以在这里展示一个全局的错误提示Toast需谨慎避免循环触发 // uni.showToast({ icon: none, title: 程序开小差了请稍后再试 }); }, onUnhandledRejection(res) { console.error(未处理的Promise拒绝:, res); this.reportErrorToServer({ type: promise_rejection, reason: res.reason, timestamp: Date.now() }); }实操心得在开发阶段务必在开发者工具中打开“异常抛出时中断”选项以便即时定位错误。但在生产环境一定要有全局错误捕获这是线上应用稳定性的最后一道防线。上报时记得做好信息过滤避免上传敏感数据。3. 页面级生命周期单个页面的“人生轨迹”如果说应用生命周期是舞台的灯光和幕布那么页面生命周期就是台上每个演员的独白和走位。它是我们日常开发中接触最频繁的部分直接决定了页面的数据流和交互逻辑。3.1 加载阶段onLoad-onShow-onReady这是页面诞生和初次亮相的标准流程。onLoad(option)页面加载时触发。一个页面只会调用一次。这是生命周期中最早可以获取到传递参数(option)的地方。核心任务接收路由参数并基于此参数初始化页面数据如发起网络请求获取详情。onLoad(options) { console.log(页面加载参数:, options); this.id options.id; // 获取传入的ID if (this.id) { this.fetchDetailData(this.id); // 根据ID请求数据 } // 也可以在这里初始化一些页面内部的状态 this.loading true; }与created的区别在Vue.js中created钩子在实例创建完成后同步调用。在UniApp中onLoad的调用时机与created非常接近但onLoad能明确接收到页面参数。通常我们将依赖页面参数的初始化放在onLoad将不依赖参数的初始化放在created。onShow()页面显示/切入前台时触发。每次打开页面都会调用包括从其他页面返回、或从后台切回该页面。核心任务处理页面显示时需要实时更新的内容。例如从编辑页返回列表页需要在onShow中刷新列表数据。与onLoad的常见混淆点onLoad只执行一次用于“初始化”onShow可能执行多次用于“刷新”。文章开头提到的白屏问题就是把一个需要等待页面基础DOM渲染完成才能执行的操作比如计算某个元素高度放在了onLoad里。此时视图可能还未准备好导致计算错误或渲染异常。这类操作应该放在onReady或onShow中并确保DOM已存在。onReady()页面初次渲染完成时触发。一个页面只会调用一次代表视图层已经准备好可以和逻辑层进行交互了。核心任务执行需要访问或操作DOM/组件的操作。例如获取某个元素的节点信息uni.createSelectorQuery或者初始化需要依赖DOM渲染的第三方库如ECharts图表、地图组件。onReady() { // 此时视图已渲染可以安全地操作DOM this.initChart(); // 初始化ECharts实例 this.getNavBarHeight(); // 获取导航栏高度用于布局计算 }注意在小程序平台onReady的触发可能比Web端稍晚因为它需要等待小程序视图层渲染完毕。3.2 隐藏与卸载阶段onHide-onUnload这是页面离开舞台的过程。onHide()页面隐藏/切入后台时触发。当navigateTo到其他页面、或应用切换到后台时触发。核心任务暂停页面上的耗时任务以节省性能和电量。例如停止轮播图自动播放、暂停视频/音频播放、清除定时器等。onHide() { clearInterval(this.dataPollingTimer); // 清除轮询定时器 if (this.videoContext) { this.videoContext.pause(); // 暂停视频播放 } }onUnload()页面卸载时触发。当页面被redirectTo、navigateBack且目标不是该页面或关闭页面时触发。核心任务进行资源清理工作防止内存泄漏。这是生命周期中最后一个可以执行清理操作的钩子。必须清理的资源包括全局事件监听器如uni.$on 但需注意作用域。在页面中创建的、但生命周期长于页面的对象某些特殊的第三方库实例。手动创建的WebSocket连接或长连接。onUnload() { // 移除全局事件监听如果在当前页面监听的话 uni.$off(someEvent, this.eventHandler); // 关闭页面内创建的WebSocket if (this.socket this.socket.readyState WebSocket.OPEN) { this.socket.close(); } // 清除可能引起内存泄漏的引用 this.largeDataCache null; }常见坑点很多人会忘记在onUnload中清理定时器。实际上如果定时器是在onLoad或onShow中通过setInterval创建的页面卸载后这个定时器可能仍然存在并继续执行导致错误或内存泄漏。更安全的做法是在onHide中就清除并在onShow中重新创建。3.3 平台特有的页面生命周期为了处理更复杂的跨平台场景UniApp还提供了一些平台特有的生命周期钩子。onPullDownRefresh()监听用户下拉刷新事件。需要在pages.json中为当前页面开启enablePullDownRefresh。通常在这里执行重新拉取最新数据的操作并在完成后调用uni.stopPullDownRefresh()停止动画。onReachBottom()监听用户上拉触底事件。用于实现列表的加载更多分页功能。需要合理设置防抖和节流避免频繁触发。onPageScroll(event)监听页面滚动事件。event.scrollTop可以获取页面在垂直方向已滚动的距离单位px。注意此事件频繁触发回调函数中应避免执行复杂或耗时操作否则会影响滚动流畅度。onShareAppMessage()监听用户点击页面内转发按钮button open-typeshare或右上角菜单“转发”按钮的行为。必须返回一个分享配置对象定义转发内容的标题、路径、图像等。onNavigationBarButtonTap(event)监听原生导航栏按钮点击事件。event.index为按钮索引在pages.json中定义。onBackPress(event)监听页面返回按钮包括物理返回键、左上角返回按钮、安卓手势返回事件。返回true可以阻止默认返回行为实现自定义逻辑如提示用户保存未提交的表单。onBackPress(options) { if (this.formHasChanged) { uni.showModal({ title: 提示, content: 内容尚未保存确定要返回吗, success: (res) { if (res.confirm) { // 用户确认返回不拦截 } else if (res.cancel) { // 用户取消拦截返回 return true; } } }); // 显示模态框期间先拦截返回 return true; } // 默认不拦截 return false; }4. 组件生命周期Vue组件的“内在节奏”在UniApp中页面本身也是一个Vue组件但它遵循上述的页面级生命周期。而页面内使用的自定义组件则遵循标准的Vue组件生命周期。理解这两者的关系和执行顺序至关重要。4.1 Vue 2 与 Vue 3 生命周期概览UniApp支持Vue 2和Vue 3组合式API。它们的生命周期函数名称和用法略有不同。Vue 2 选项式API生命周期beforeCreate实例初始化之后数据观测和事件配置之前。created实例创建完成已完成数据观测但尚未挂载DOM。可以访问this可以发起异步请求。beforeMount挂载开始之前被调用。mounted实例被挂载到DOM后调用。可以操作DOM子组件不一定已挂载。beforeUpdate数据更新时调用发生在虚拟DOM打补丁之前。updated数据更改导致的虚拟DOM重新渲染和打补丁之后调用。beforeDestroy实例销毁之前调用。destroyed实例销毁后调用。Vue 3 组合式API生命周期通过onX函数导入onBeforeMount/onMountedonBeforeUpdate/onUpdatedonBeforeUnmount/onUnmountedonErrorCaptured(捕获子孙组件错误)onRenderTracked/onRenderTriggered(用于调试渲染依赖)4.2 页面与组件生命周期的执行顺序这是一个非常关键的实践点。假设一个页面Page中包含一个自定义组件Component它们的生命周期触发顺序是怎样的页面初次加载时Page.beforeCreate - Page.created - Page.onLoad - Component.beforeCreate - Component.created - Component.beforeMount - Page.onShow - Page.onReady - Component.mounted关键观察页面的onLoad在页面的Vue实例创建created之后但在其内部组件的beforeCreate之前触发。这意味着你可以在onLoad中设置一些数据这些数据可以作为props传递给子组件。页面的onShow在组件开始挂载beforeMount之后、但在组件完全挂载mounted之前触发。页面的onReady在所有子组件都挂载完成mounted之后触发。这是为什么onReady是执行DOM操作最安全时机的原因。页面跳转时Page A 跳转到 Page BPageA.onHide - PageB.beforeCreate - PageB.created - PageB.onLoad - (PageB子组件创建) - PageB.onShow - PageB.onReady页面返回时从Page B 返回 Page APageB.onUnload - PageA.onShow注意返回时Page A的onLoad不会再次触发因为页面实例被保留在了小程序或App的页面栈中。理解这个顺序可以帮助你解决诸如“为什么在页面的created里获取不到子组件ref”、“为什么在onShow里操作子组件方法有时会报错”这类问题。通常的准则是涉及子组件交互的操作最好放在页面的onReady之后或者子组件的mounted生命周期中。5. 实战中的“坑”与高级应用场景知道了生命周期函数有哪些和它们的顺序只是第一步。在实际项目中灵活、正确地运用它们才能避免踩坑并实现高级功能。5.1 场景一列表页与详情页的数据同步这是一个经典场景。从列表页进入详情页编辑某项数据后返回列表页列表需要刷新以显示最新数据。错误做法在列表页的onLoad中加载数据。因为返回时onLoad不会执行列表不会刷新。正确做法在列表页的onShow生命周期中加载或刷新数据。// list-page.vue export default { data() { return { list: [] }; }, onShow() { // 每次页面显示时包括从详情页返回都重新加载数据 this.loadListData(); }, methods: { async loadListData() { this.list await api.fetchList(); }, navigateToDetail(id) { uni.navigateTo({ url: /pages/detail/detail?id${id} }); } } };优化如果列表数据量大频繁刷新可能影响体验。可以引入一个“脏标记”needRefresh只在从详情页返回时才刷新。这可以通过在跳转时传递标记或在全局状态中管理来实现。5.2 场景二处理视频/音频的播放与后台暂停在播放页当用户切到后台或跳转到其他页面时应暂停播放以节省资源和提供更好体验。// play-page.vue export default { data() { return { videoContext: null }; }, onLoad() { // 创建视频上下文 this.videoContext uni.createVideoContext(myVideo); }, onShow() { // 页面显示时如果是来自后台切换可以考虑自动播放需结合业务逻辑 // this.videoContext.play(); }, onHide() { // 页面隐藏时暂停播放 if (this.videoContext) { this.videoContext.pause(); } }, onUnload() { // 页面卸载时彻底释放资源 this.videoContext null; } };注意对于音频使用uni.createInnerAudioContext逻辑类似。但要注意部分安卓系统在应用退到后台后可能会强制停止音频播放这是系统行为。5.3 场景三条件编译与生命周期UniApp的条件编译#ifdef可以用于代码块但它无法直接作用于生命周期函数声明。如果你需要在不同平台执行不同的生命周期逻辑可以这样做export default { onLoad(options) { // 所有平台共有的逻辑 console.log(页面加载, options); // 平台特定逻辑 #ifdef APP-PLUS this.handleAppLaunchParams(options); #endif #ifdef MP-WEIXIN this.handleMpLaunchParams(options); #endif }, onShow() { #ifdef H5 // 仅在H5平台执行的逻辑比如处理H5路由的特定参数 this.handleH5RouteQuery(); #endif }, methods: { // 将不同平台的逻辑封装成方法 handleAppLaunchParams(options) { /* ... */ }, handleMpLaunchParams(options) { /* ... */ } } };5.4 场景四onBackPress的异步陷阱onBackPress的返回值应该是同步的boolean类型。但实际业务中我们经常需要弹出模态框让用户确认这是一个异步操作。错误示范onBackPress() { uni.showModal({ title: 提示, content: 确定离开, success: (res) { if (res.confirm) { return false; // 这个return无法被onBackPress捕获 } else { return true; // 同上无效 } } }); // 默认返回false无法达到拦截目的 }showModal的success回调是异步的其返回值是回调函数的返回值并不是onBackPress函数的返回值。onBackPress在调用showModal后会立即执行完毕默认返回false导致拦截失败。正确做法利用showModal的异步特性在回调中不返回值而是通过其他方式控制导航但更常见的做法是让onBackPress同步返回true来先拦截然后在异步回调中手动控制是否执行返回。onBackPress() { if (this.needConfirm) { this.showConfirmModal(); // 弹出模态框 return true; // 先拦截返回行为 } return false; }, methods: { showConfirmModal() { uni.showModal({ title: 提示, content: 内容未保存确定退出, success: (res) { if (res.confirm) { // 用户确认退出使用uni.navigateBack()手动返回 uni.navigateBack(); } // 如果取消什么都不做页面保持拦截状态自然解除但注意下次按返回键会再次触发 } }); } }注意这种方式在部分安卓物理返回键场景下可能需要结合plus.key.addEventListener进行更细致的处理。5.5 场景五Skyline渲染引擎下的生命周期差异当在微信小程序中开启Skyline渲染引擎在app.json中配置renderer: skyline后生命周期和行为会有一些变化需要特别注意初始渲染顺序可能不同Skyline为了更快的首屏渲染可能会优化组件和页面的初始化顺序。这要求你的代码不能对传统的生命周期顺序有强依赖。例如在onReady中操作子组件应该仍然是安全的但理论上子组件的mounted可能更早或更晚。onPageScroll等事件在Skyline下滚动事件的触发频率和细节可能有所不同需要做好兼容性测试。自定义组件通信Skyline使用了不同的底层渲染机制父子组件间通过selectComponent等方式获取实例的行为需要验证。建议如果计划使用Skyline应在开发中期就开启进行兼容性测试而不是等到项目尾声。重点关注与视图渲染、组件交互相关的生命周期函数和API。6. 性能优化与生命周期的最佳实践合理地利用生命周期是进行性能优化的有效手段。6.1 数据请求的时机选择首屏关键数据放在页面的onLoad中。这是最早可以发起请求的时机能最大程度减少用户等待时间。非关键或实时性要求高的数据可以放在onShow中确保每次页面可见时都是最新数据。但要注意可能造成的频繁请求可适当加入防抖或条件判断。避免在created中请求阻塞渲染的数据虽然created里可以发起请求但如果请求数据是渲染所必须的放在onLoad语义更清晰。如果请求不阻塞渲染放在created或onLoad区别不大。利用onReady进行非阻塞的后续加载对于首屏不需要立即展示但很快会用到的数据如第二屏的内容、推荐列表等可以在onReady中发起请求这样不会影响首屏渲染速度。6.2 事件监听与内存管理这是一个极易导致内存泄漏的领域。原则在哪里绑定就在哪里解绑。全局事件总线如果在页面中使用uni.$on监听全局事件必须在onUnload中使用uni.$off进行解绑。否则即使页面销毁回调函数仍然被引用无法被垃圾回收。onLoad() { uni.$on(userInfoUpdated, this.handleUserInfoUpdate); }, onUnload() { uni.$off(userInfoUpdated, this.handleUserInfoUpdate); }定时器在onHide或onUnload中必须清除。推荐在onHide中清除在onShow中重新创建以提供更好的后台体验。第三方库实例某些图表库、地图库会创建全局的实例或监听器。查阅其文档确保在onUnload中有正确的销毁方法如dispose。6.3 页面栈管理与状态保持UniApp的页面栈管理因平台而异小程序有明确的5层或10层限制H5和App则宽松。生命周期与此紧密相关。navigateTo保留当前页面跳转新页面。当前页面触发onHide新页面触发onLoad,onShow等。redirectTo关闭当前页面跳转新页面。当前页面触发onUnload新页面触发onLoad,onShow。navigateBack关闭当前页面返回上一页或多级。当前页面触发onUnload返回的页面触发onShow注意不是onLoad。状态保持问题当从详情页返回列表页时我们通常希望列表页保持之前的滚动位置和搜索状态。这可以通过以下方式实现不重新加载页面使用navigateTo和navigateBack利用页面栈机制自然保持状态。使用全局状态管理将列表的过滤条件、分页参数、甚至部分数据存储在Vuex或Pinia中。这样即使页面销毁重建也能恢复状态。小程序独有getCurrentPages()在小程序中可以通过这个API获取页面栈实例直接操作上一个页面的数据和方法需谨慎使用耦合性高。6.4 一个综合案例带搜索、分页、过滤的复杂列表页让我们设计一个产品列表页的生命周期策略onLoad(options)接收可能从外部传入的初始搜索关键词options.keyword。初始化页面内部状态keyword搜索词filter筛选条件page当前页码list数据列表loading加载状态。根据options设置初始的keyword和filter。onShow()检查全局状态中是否有标记需要刷新例如从筛选页返回并应用了新的筛选条件。如果有则重置page1并调用loadData()。如果没有任何需要刷新的标记且列表已有数据list.length 0则可以选择不自动刷新以保持用户体验流畅。onReady()可以在这里执行一些与DOM相关的初始化比如绑定滚动监听用于上拉加载更多但注意onPageScroll本身是生命周期无需手动绑定。onHide()清除当前可能正在进行的网络请求如果使用了可取消的请求库如axios。停止任何页面内的动画或轮播。onUnload()移除可能绑定的全局事件监听器如一个全局的“清空搜索”事件。清理onPageScroll监听器中可能存在的闭包引用。onReachBottom()触发加载更多。先判断是否正在加载或已无更多数据然后page并调用loadData(true)传入一个isLoadMore标志。onPullDownRefresh()触发刷新。重置page1调用loadData()并在请求完成后调用uni.stopPullDownRefresh()。loadData(isLoadMore false)方法组合keyword,filter,page参数发起请求。如果是isLoadMore则将新数据追加到list否则替换list。处理加载状态和错误状态。通过这样精细化的生命周期管理这个列表页就能在各种交互场景下首次进入、搜索、筛选、下拉刷新、上拉加载、从子页面返回都表现出正确的行为同时保持良好的性能和用户体验。理解并熟练运用UniApp的生命周期就像是拿到了应用程序的时序电路图。它不能直接解决所有业务问题但能让你清晰地知道代码在何时、以何种顺序执行从而避免状态错乱、资源泄漏和性能瓶颈。下次当你遇到页面渲染问题、数据不同步或者奇怪的Bug时不妨先静下心来画一画生命周期的执行顺序图很可能答案就隐藏在其中。

相关新闻

最新新闻

Taro 4 微信小程序:RootPortal CSS 变量继承问题与自建 PagePortal 解决方案

Taro 4 微信小程序:RootPortal CSS 变量继承问题与自建 PagePortal 解决方案

Taro 4 微信小程序&#xff1a;RootPortal CSS 变量继承问题与自建 PagePortal 解决方案 背景 在基于 Taro 4 React 开发的微信小程序中&#xff0c;我们有一个下拉筛选组件&#xff0c;展开时需弹出全屏透明遮罩 选项抽屉面板。 由于下拉内容被包裹在 <ScrollView>…

2026/8/2 2:11:05
Linux渗透测试提权思路与常用方法

Linux渗透测试提权思路与常用方法

Linux渗透提权思路与常用方法 ⚠️仅用于授权安全测试&#xff0c;禁止对未授权机器操作&#xff0c;违法将承担法律责任 提权核心&#xff1a;拿到普通Shell后&#xff0c;寻找漏洞、错误配置、权限泄漏、可执行SUID、定时任务、内核漏洞&#xff0c;从普通用户提升到root权限…

2026/8/2 2:11:05
RocketMQ 延迟消息实战:延迟双删策略解决 Redis 缓存一致性

RocketMQ 延迟消息实战:延迟双删策略解决 Redis 缓存一致性

大家好&#xff0c;我是晚安code。 更新完数据库&#xff0c;缓存里却还躺着旧数据——这种脏数据我太熟了。上周排查一个订单价格对不上的线上问题&#xff0c;最后定位到就是「先删缓存再写库」在并发下翻了车。 这篇我把 RocketMQ 延迟消息和延迟双删&#xff08;Delay Doub…

2026/8/2 2:11:05
嵌入式高性能显示方案:7英寸DSI LCD接口原理、驱动实战与性能优化

嵌入式高性能显示方案:7英寸DSI LCD接口原理、驱动实战与性能优化

1. 项目概述&#xff1a;一块7英寸DSI LCD屏能玩出什么花样&#xff1f;如果你手头有一块树莓派&#xff0c;或者正在玩STM32这类MCU&#xff0c;想找个屏幕来显示点东西&#xff0c;那么“7inch DSI LCD”这个组合词对你来说可能既熟悉又陌生。熟悉的是&#xff0c;7英寸是个非…

2026/8/2 2:11:05
USB转RS232串口线硬件原理、驱动安装与通信调试全攻略

USB转RS232串口线硬件原理、驱动安装与通信调试全攻略

1. 从USB到RS232&#xff1a;一根“古老”数据线的现代生存指南如果你手头有一台老旧的工控设备、一台经典的数控机床、或者一块需要调试的开发板&#xff0c;那么你大概率会遇到一个在今天看来有点“复古”的接口&#xff1a;RS232串口。而你的电脑&#xff0c;无论是笔记本还…

2026/8/2 2:11:05
小菜学Gradio:从“这东西能干啥”到“我给王大姐做了个图片处理器”

小菜学Gradio:从“这东西能干啥”到“我给王大姐做了个图片处理器”

——小菜&#xff1a;“大胖老师&#xff0c;咱们文渊慧典的Web界面是用什么做的&#xff1f;王大姐用的那个拖拽上传、一键识别、进度条走完下载&#xff0c;看着挺高级的。是不是得请个前端工程师写好久&#xff1f;”——大胖老师端起保温杯&#xff0c;吹了吹热气&#xff…

2026/8/2 2:06:05