这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

最近,我刚刚完成了一个阅读器的txt文件阅读功能,但在处理大文件时,遇到了文本内容过多导致浏览器崩溃的问题。

一般情况下,没有任何样式渲染时不会出现什么问题,15MB的文件大约会有3秒的空白时间。

<div id="content"></div>
fetch('./dp.txt').then(resp => resp.text()).then(text => {
document.getElementById('content').innerText = text
})

尽管目前还没有严重的问题,但随着文件继续增大,肯定会超过浏览器内存限制而导致崩溃。

在开发阅读器的过程中,我添加了下面的样式,结果导致浏览器直接崩溃:

* {
margin: 0;
padding: 0;
} html,
body {
width: 100%;
height: 100%;
overflow: hidden;
} body {
column-fill: auto;
column-width: 375px;
overflow-x: auto;
}

预期结果应该是像下面这样分段显示:

然而,实际出现了下面的问题:

因此,文件内容太多会导致浏览器崩溃。即使进行普通的渲染,我们也要考虑这个问题。

如何解决

解决这个问题的方法有点经验的前端开发工程师应该都知道可以使用虚拟滚动,重点是怎么对文本分段分段,最容易的可能就是按照一定数量字符划分,但是这个导致文本衔接不整齐出现文本跳动。如图,橙色和蓝色表示两端文本的衔接,虚拟滚动必然会提前移除橙色那块内容,那么就会导致蓝色文本位置发生改变。

要解决这个问题,我们需要想办法用某个元素替代原来的位置。当前页橙色部分删除并计算位置,问题会变得复杂并且误差比较大,因此这一部分直接保留,把这部分前面的内容移除,然后用相同长度的元素占据,接下来重点就是怎么获取到橙色部分与前面内容的分界点。

获取分界点可以使用document.createRange()document.createRange()是 JavaScript 中用于创建Range对象的方法。Range对象表示一个包含节点与文本节点之间一定范围的文档片段。这个范围可以横跨单个节点、部分节点或者多个节点。

// 创建 Range 对象
const range = document.createRange(); range.setStart(textNode, 0); // 第一个参数可以是文本节点,第二个参数表示偏移量
range.setEnd(textNode, 1);
const rect = range.getBoundingClientRect(); // 获取第一个字符的位置信息

利用Range对象的特性,我们可以从橙色部分的最后一个字符开始计算,直到找到分界点的位置。

阅读器如果仅仅只是从左往右阅读,按照上面的方法已经可以实现,但是阅读器可能会出现页面直接跳转,跳转之后的文本起点你并不知道,并且页面总页码你也无法知道。因此从一开始就要知道每一页的分界点,也就是需要做预渲染。以下是一个简单的示例:

let text = '...'
const step = 300
let end = Math.min(step, value.length) // 获取结束点 while (text.length > 0) {
node.innerText = value.substring(0, end) // 取部分插入节点
const range = document.createRange()
range.selectNodeContents(node)
const rect = range.getBoundingClientRect() // 获取当前内容的位置信息 if (rect.height > boxHeight) {
// 判断当前内容高度是否会超出显示区域的高度
// 如果超出,从 end 最后一个字符开始计算,直到不超出范围
while (bottom > boxHeight) {
// node.childNodes[0] 表示文本节点
range.setStart(node.childNodes[0], end - 1)
range.setEnd(node.childNodes[0], end)
bottom = range.getBoundingClientRect().bottom
end--
}
} else {
// 如果没有超出,end 继续增加
// ...
}
}

上面只是简单的实现原理,可以达到精确区分每一页的字符,但是计算量有点太大,15MB文本大约500多万字,循环次数估计也在几十万到上百万。在本人的电脑上测试大约需要20秒,每个人设备的性能不同,所需时间也会有所不同。很明显,这种实现方式并不太理想。

后来我对这个方案进行了优化,实际上我们不需要计算每一页的分界点,可以计算出多页的分界点,例如10页、20页、50页等。优化后的代码是将step增大,比如设为10000,然后将不能组成一页的尾部内容去掉。优化后,15MB的文本大约需要4秒左右。需要注意的是,step并不是越大越好,太大会导致渲染页面占用时间过长。

这就是我目前用来解决页面渲染大量文本的方法。如果你有更好的方案,欢迎留言。

本文转载于:

https://juejin.cn/post/7261231729523965989

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--浏览器渲染15M文本导致崩溃怎么办的更多相关文章

  1. 【Web动画】CSS3 3D 行星运转 && 浏览器渲染原理

    承接上一篇:[CSS3进阶]酷炫的3D旋转透视 . 最近入坑 Web 动画,所以把自己的学习过程记录一下分享给大家. CSS3 3D 行星运转 demo 页面请戳:Demo.(建议使用Chrome打开 ...

  2. 160826、浏览器渲染页面过程描述,DOM编程技巧以及重排和重绘

    一.浏览器渲染页过程描述   1.浏览器解析html源码,然后创建一个DOM树. 在DOM树中,每一个HTML标签都有一个对应的节点(元素节点),并且每一个文本也都有一个对应的节点(文本节点). DO ...

  3. 浏览器渲染页面过程描述,DOM编程技巧以及重排和重绘。

    一.浏览器渲染页过程描述 1.浏览器解析html源码,然后创建一个DOM树. 在DOM树中,每一个HTML标签都有一个对应的节点(元素节点),并且每一个文本也都有一个对应的节点(文本节点). DOM树 ...

  4. IE (第一部分) 浏览器 中 关于浏览器模式和文本模式的困惑

    什么是浏览器模式和文本模式? 经常使用IE开发者工具的同学,肯定见过浏览器模式和文本模式,对于这两个名词,综合相关文档解释如下: 浏览器模式(Browser Mode),用于切换IE针对该网页的默认文 ...

  5. 从敲入 URL 到浏览器渲染完成、对HTTP协议的理解

    1. 大致过程 当你这样子回答的时候: 用户输入 url 地址,浏览器查询 DNS 查找对应的请求 IP 地址 建立 TCP 连接 浏览器向服务器发送 http 请求,如果服务器段返回以 301 之类 ...

  6. 浏览器渲染详细过程:重绘、重排和 composite 只是冰山一角

    https://juejin.im/entry/590801780ce46300617c89b8 渲染 这张很经典的图许多人都看过,其中的概念大家应该都很熟悉,也就是这么几个步骤:js修改dom结构或 ...

  7. web性能优化-浏览器渲染原理

    在web性能优化-浏览器工作原理中讲到,浏览器渲染是在renderer process中完成的. 那我们来看下renderer process究竟干了什么? Renderer Process包含的线程 ...

  8. iOS开发笔记12:iOS7上UITextField限制字数输入导致崩溃问题

    在一些场景中,需要限制用户的输入字数,例如在textField里进行控制(textView也类似,崩溃原因也相同),如图所示 系统会监听文本输入,需要注意的第一点是输入法处于联想输入还未确定提交的时候 ...

  9. 读书笔记(一)—— 浅析浏览器渲染过程和html中的文件加载

    在构建页面时,我们会在html中载入一个或多个css和js文件.或许大家都已经习惯了"最佳实践"中,css文件应该放在<head>标签中引入,而js文件则是放在< ...

  10. Web标准的简单理解 不同内核浏览器的差异以及浏览器渲染简介(转)

    Web标准是一系列标准的集合.这些标准大概分三方面:结构.表现和行为.结构化主要有HTML, XHTML和XML,表现主要有CSS,行为标准主要包括对象模型,如 W3C DOM.ECMAScript等 ...

随机推荐

  1. UUID算法:独一无二的标识符解决方案

    引言 在分布式系统和大数据环境下,唯一标识符的生成和管理是一项关键任务.UUID(Universally Unique Identifier)算法应运而生,成为了解决重复数据和标识符冲突的有效工具.本 ...

  2. SATA学习笔记——OOB信号

    一.SATA物理层概述 说OOB之前,首先得了解一下SATA结构以及物理层的含义. SATA主要包括:应用层(Application Layer), 传输层(Transport Layer),链路层( ...

  3. jenkins构建第一个maven项目

    1. 项目介绍 spring boot样例github项目. 大家可以访问:https://github.com/mudfish/springbootdemo 2. jenkins中新建maven任务 ...

  4. Java并发编程实例--12.使用线程工厂创建线程

    工厂模式是面向对象编程世界中最有用的设计模式. 它是一个创新型的模式,目标是开发一个对象,这个对象的任务是去创建其他类对象. 这样一来,如果我们想创建某些类的对象就不需要使用new关键字.好处有以下几 ...

  5. qt基础知识总结

    qt基础知识总结 1.ctrl+r:快速运行 2.两种模式的区别: 一个是提供菜单栏的,一个不提供菜单栏 3.界面讲解 layouts:布局=水平布局+垂直布局+网格布局+表单布局 spacers:垫 ...

  6. pinject依赖注入模块

    pinject 是一个基于 Python 的轻量级依赖注入库,可以方便地实现依赖注入的功能. 下面我们将通过一个简单的示例来演示如何使用 pinject 实现依赖注入. 首先,我们需要安装 pinje ...

  7. django学习第一天---MVC和MTV框架,request对象的属性,url路由系统

    jinja2模板渲染简单使用 下载安装 pip install jinja2 使用示例 html文件中写法 <!DOCTYPE html> <html lang="zh-C ...

  8. Kotlin 协程五 —— 在Android 中使用 Kotlin 协程

    目录 一.Android MVVM 结构 二.添加依赖 三.在后台线程中执行 3.1 协程解决了什么问题 3.2 保证主线程安全 3.3 withContext 的性能 四.结构化并发 4.1 追踪协 ...

  9. Android Compose开发

    目录 好处 入门 Composable 布局 其他组件 列表 verticalScroll 延迟列表 内容内边距 性能 修饰符 偏移量 requiredSize 滚动 添加间距Spacer Butto ...

  10. 网络安全-Linux常用命令

    安装上传下载的软件包 yum install lrzsz -y 下载:linux-->windows sz -y /etc/hosts 上传:windows-->linux rz -y 系 ...