跨平台应用开发进阶(五十六):应用渲染异常问题分析及解决

本文主要是介绍跨平台应用开发进阶(五十六):应用渲染异常问题分析及解决,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 一、前言
    • 二、问题分析
    • 三、延伸阅读
      • 3.1 页面跳转
      • 3.2 $nextTick原理深度解析
        • 3.2.1 什么时候使用$nextTick()
        • 3.2.2 $nextTick() 执行原理
      • 3.3 JS 运行机制
        • 3.3.1 微任务
        • 3.3.2 宏任务
    • 四、解决措施
    • 五、拓展阅读

一、前言

继前期iOS由于移动设备内存不足导致页面白屏问题之后,(详参博文《跨平台应用开发进阶(五十)uni-app ios web-view嵌套H5项目白屏问题分析及解决》)又发现APP在iOS系统运行过程中,会高频出现页面黑屏、黑色区块,白屏问题。

在这里插入图片描述
在这里插入图片描述

二、问题分析

出现以上问题是由于页面渲染问题导致的,引发的可能原因是页面栈溢出、应用内存溢出或泄漏。有关内存管理,详参博文《安全生产:内存溢出和内存泄漏》。

经过getCurrentPages()输出页面栈信息,发现切换底部导航时,页面栈信息已经清空,故不存在页面栈溢出问题。

let pages = getCurrentPages(); //当前页面栈
pages.forEach((page, index) => {console.log('---------CurrentPage----------' + index)Object.keys(page).forEach((key, index) => {key === 'route' && console.log('---------' + key + '----------' + page[key])})
})

继续尝试APP缓存清理机制,制定清理策略如下:

点击当前tabitem,滚动或刷新当前页面调用原生方法plus.cache.clear清理应用缓存。如果是点击不同的tabitem,一定会触发页面切换。

// 点击 tab 时触发
onTabItemTap (e) {// #ifdef APP-PLUSplus.cache.clear( function () {console.log( "Clear application cache successful!" );});// #endif
},

经过清理缓存策略的实施,问题得到解决。

由此,衍生出调用uni.setStorage(OBJECT)(宏任务)、uni.setStorageSync(KEY,DATA)(微任务)等API及uni.downloadFileuni.saveFile是存储至设备内存中吗?

由于调用uni.setStorage(OBJECT)(宏任务)、uni.setStorageSync(KEY,DATA)(微任务)等API会因为关闭APP重启移动设备后消失,所以是存放在内存而不是硬盘。

同样,由于调用uni.downloadFileuni.saveFile等API不会因为关闭APP重启移动设备后消失,所以是存放在硬盘而不是内存。

三、延伸阅读

3.1 页面跳转

  • uni.navigateTo(OBJECT)
    会保留页面栈,能通过调用navigateBack返回。但是有页面栈溢出的问题,不能当作通用跳转方法。

  • uni.redirectTo(OBJECT)
    不会保留页面栈,不能返回上一页,返回默认pages.json第一个页面。

  • uni.reLaunch(OBJECT)
    销毁页面栈并跳转到指定页面。通常用于退出登录返回首页。

  • uni.switchTab(OBJECT)
    切换选项卡用。

  • uni.navigateBack(OBJECT)
    配合navigateTo,返回页面使用,能有效避免页面栈溢出。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层。

通过项目代码分析发现,路由跳转并不存在页面栈溢出异常情况。底部导航栏切换时,页面栈会做清空处理。同一导航栏下的页面跳转才会在页面栈中。

2 排查思路

  1. 根据具体业务场景,逻辑上控制页面跳转和返回,防止页面栈溢出。通过getCurrentPages()判断页面栈有多高;查看webview对象多少,来判断当前跳转方式是否合适。
  2. 排查代码逻辑,是否有长时间后台请求不返回的问题,可以在uni.request添加timeout来规避这种情况。
  3. 排查内存溢出,是否有创建定时器(setInterval),但是没清空的情况(clearInterval)。
  4. uniapp本身问题,因为其跨平台性需要js调用native,性能肯定会有所损失,如果无法接受还是使用原生语言开发。
  5. 删除缓存时,如果是清空数据需要使用uni.clearStorageSync

那就需要查看应用运行过程中占用的内存了,是否存在内存泄漏问题。

3.2 $nextTick原理深度解析

Vue 实现响应式并不是数据发⽣变化之后 DOM ⽴即变化,⽽是按⼀定的策略进⾏ DOM 更新。$nextTick 是在下次 DOM 更新循环结束之后执⾏延迟回调,在修改数据之后使⽤ $nextTick,则可以在回调中获取更新后的 DOM,在下次 DOM 更新循环结束之后执行延迟回调。

简单的理解是:当数据更新了,在dom中渲染后,⾃动执⾏该函数。Vue在更新data之后并不会立即更新DOM上的数据,就是说如果我们修改了data中的数据,再马上获取DOM上的值,我们取得的是旧值,我们把获取DOM上值的操作放进$nextTick里,就可以得到更新后得数据。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

正确的⽤法是:vue改变data中的数据后,使⽤vue.$nextTick()⽅法包裹js对象执行后续代码。

在这里插入图片描述
在这里插入图片描述

3.2.1 什么时候使用$nextTick()

Vue⽣命周期的created()钩⼦函数进⾏的DOM操作⼀定要放在Vue.nextTick()的回调函数中,原因是在created()钩⼦函数执⾏的时候,DOM 其实并未进⾏任何渲染,⽽此时进⾏DOM操作⽆异于徒劳,所以此处⼀定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。

当项⽬中改变data函数的数据,想基于新的dom做点什么,对新DOM⼀系列的js操作都需要放进Vue.nextTick()的回调函数中。

3.2.2 $nextTick() 执行原理

Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个任务队列,并缓冲在同一时间循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。(这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的)

然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行任务队列 (已去重的) 工作。

Vue 在内部对异步队列尝试使用原生的 Promise.then(微任务)、MutationObserversetImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0)(宏任务)代替。

3.3 JS 运行机制

JS 执行是单线程的,它是基于事件循环的。事件循环大致分为以下几个步骤:

  1. 所有同步任务都在主线程上执行,形成一个执行栈
  2. 主线程之外,还存在一个"任务队列"(task queue)。
  3. 只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  4. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。主线程不断重复上面的第三步。

在这里插入图片描述

主线程的执行过程就是一个 tick,而所有的异步结果都是通过 “任务队列” 来调度。"任务队列"中存放的是一个个的任务(task)。规范中规定 task 分为两大类,宏任务微任务

在这里插入图片描述

3.3.1 微任务

也称job,通常用于在当前正在执行的脚本之后直接发生的事情,比如对一系列的行为做出反应,或者做出一些异步的任务,而不需要新建一个全新的task。只要执行栈没有其他javascript在执行,在每个task结束时,微任务队列就会在回调后处理。在微任务期间排队的其他微任务将被添加到这个队列的末尾。

常见的微任务 有 MutationObseverPromise.then$nextTiock

3.3.2 宏任务

宏任务的作用是为了让浏览器能够从内部获取javascript / dom的内容并确保执行栈能够顺序进行。调度是随处可见的,例如解析HTML,获得鼠标点击的事件回调等等。

常见的宏任务有 setTimeoutMessageChannelpostMessagesetImmediate

vue进行DOM更新,内部也是调用nextTick来做异步队列控制。而当我们自己调用nextTick的时候,它就在更新DOM的那个micro task后追加了我们自己的回调函数,从而确保我们的代码在DOM更新后执行。

setTimeout是宏任务:只是延迟执行,在延迟执行的方法里,DOM有可能会更新也有可能没有更新。

四、解决措施

注⚠️:

  • uni-app App 端内置 HTML5+ 引擎,让 js 可以直接调用丰富的原生能力。

    条件编译调用 HTML5+:小程序及 H5 等平台是没有 HTML5+ 扩展规范的,因此在 uni-app 调用 HTML5+ 的扩展规范时,需要注意使用条件编译。否则运行到h5、小程序等平台会出现 plus is not defined错误。

    // #ifdef APP-PLUS
    var appid = plus.runtime.appid;
    console.log('应用的 appid 为:' + appid);
    // #endif
    
  • uni-app 逻辑层是运行在一个独立的jscore里的,它不依赖于本机的webview,所以一方面它没有浏览器兼容问题,可以在Android4.4上跑es6代码,另一方面,它无法运行window、document、navigator、localstorage等浏览器专用的js API。

    jscore就是一个标准js引擎,标准js是可以正常运行的,比如if、for、各种字符串、日期处理等。js和浏览器的区别要注意区分开来。

  • uni-app 的App端没有App那种webkit remote debug,因为uni-app的js不是运行在webview里,而是独立的jscore里。

  • 由于浏览器 GUI 渲染线程JS 引擎线程是互斥的关系,JS引擎执行时,会将GUI线程挂起,直到JS引擎执行结束。当页面中有很多长任务时,就会造成页面 UI 阻塞,出现界面卡顿、掉帧等情况。

GUI渲染线程:负责页面的绘制和渲染,我们所熟知的html、css资源的解析、dom树的生成、页面绘制都是该线程负责的。
JS引擎线程:负责js的解析以及所有异步同步任务的执行,维护一个执行栈,先逐个处理同步代码,当遇到异步任务时,就会借助事件触发线程。

浏览器的进程、线程信息如下:
在这里插入图片描述
其中,前端主要关注的是浏览器内核的渲染进程,其主要负责html、css、js资源解析渲染,还负责事件循环、异步请求等。

数据存储:uni-app中的缓存、H5缓存是存储在内存吗?

  • 原生plus Storage模块管理应用本地数据存储区,用于应用数据的保存和读取。应用本地数据与localStoragesessionStorage的区别在于数据有效域不同,前者可在应用内跨域操作,数据存储期是持久化的,并且没有容量限制。通过plus.storage可获取应用本地数据管理对象。

  • uni-app应用uni.setStorage(OBJECT)(宏任务)、uni.setStorageSync(KEY,DATA)(微任务)进行数据存储时,其本质是使用原生plus.storage,无大小限制,不是缓存,是将数据存储至sqlite数据库文件进行持久化。

  • App端还支持SQLite、IO文件等本地存储方案。

  • H5端应用localStoragesessionStorage进行数据存储,浏览器限制5M大小,是缓存概念,可能会被清理。

  • 应用Vuex实现状态管理。

  • 原生plus.cache管理应用缓存数据仅包括程序中使用webview产生的数据,不包括业务逻辑中使用扩展api(例如uni.setStorage(OBJECT)(宏任务)、uni.setStorageSync(KEY,DATA)(微任务)等API)保存的数据。

  • 各个小程序端为其自带的storage api,数据存储生命周期跟小程序本身一致,即除用户主动删除或超过一定时间被自动清理,否则数据都一直可用。

  • uni-app框架组件本身无法解决的问题,可抛弃框架组件,使用原生进行优化。

最终是选择在切换底部页签时做缓存清理动作,注意此处的缓存清理使用plus.cache.clear实现清除应用的缓存数据。清理应用缓存数据仅包括程序中使用webview产生的数据,不包括业务逻辑中使用扩展api保存的数据。代码如下:

// 点击 tab 时触发
onTabItemTap (e) {console.log('-----------点击 tab 时触发-----------', e);// #ifdef APP-PLUSplus.cache.clear( function () {console.log( "component Clear application cache successful!" );});// #endif
},

五、拓展阅读

  • uni-app 路由导航
  • uni-app getCurrentPages()
  • HTML5+ API
  • uni-app 使用HTML5+的注意事项
  • Android平台 storage性能优化说明
  • onTabItemTap
  • 《跨平台应用开发进阶(五十)uni-app ios web-view嵌套H5项目白屏问题分析及解决》
  • 《网站开发进阶(三十)HTML5–本地存储Web Storage》
  • 《Vue进阶(幺捌玖):localStorage 与 sessionStorage 应用总结》
  • 《安全生产:内存溢出和内存泄漏》

这篇关于跨平台应用开发进阶(五十六):应用渲染异常问题分析及解决的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/288193

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置