前端项目的通用优化策略

一、虚拟滚动

当我们开发的时候,遇到大数据加载,页面卡顿的问题应该如何处理?大多数情况下,我们都是尽量通过分页的方式处理这类问题,但是总有一些特殊的情况我们必须把数据全部加载到前端进行处理。我曾经遇到过一个坑爹的组件,必须一次性加载几万条数据到页面上,导致一打开这个组件,就会让页面疯狂卡顿,当时便用了虚拟滚动解决了这个棘手的问题。

所以当我们遇到这种情况:渲染长列表的场景,当渲染条数过多时,所需要的渲染时间会很长,滚动时还会造成页面卡顿,整体体验非常不好。完全可以考虑使用虚拟滚动这个解决方案。

虚拟滚动——指的是只渲染可视区域的列表项,非可见区域的不渲染,在滚动时动态更新可视区域,该方案在优化大量数据渲染时效果是很明显的。以下这个图较为清晰地解释了虚拟滚动的工作原理。
在这里插入图片描述
虚拟滚动基本原理:

计算出 totalHeight 列表总高度,并在触发时滚动事件时根据 scrollTop 值不断更新 startIndex 以及 endIndex ,以此从列表数据 listData 中截取对应元素。

虚拟滚动插件

虚拟滚动的使用场景不少,网上有很多已经造好的轮子,我们也可以适当地选择更快更便捷的方式解决我们的页面问题。

虚拟滚动的插件有很多,比如 vue-virtual-scroller、vue-virtual-scroll-list、react-tiny-virtual-list、react-virtualized 等

这里简单介绍 vue-virtual-scroller 的使用

// 安装插件
npm install vue-virtual-scroller

// main.js
import VueVirtualScroller from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'

Vue.use(VueVirtualScroller)

// 使用
<template> 
  <RecycleScroller 
    class="scroller" 
    :items="list" 
    :item-size="32" 
    key-field="id" 
    v-slot="{ item }"> 
      <div class="user"> {{ item.name }} </div>
  </RecycleScroller> 
</template>

该插件主要有 RecycleScroller.vue、DynamicScroller.vue 这两个组件,其中 RecycleScroller 需要 item 的高度为静态的,也就是列表每个 item 的高度都是一致的,而 DynamicScroller 可以兼容 item 的高度为动态的情况

二、Web Worker 优化长任务

浏览器对页面的渲染时单线程的,导致我们的页面无法同时渲染CSS和JS代码。所以由于浏览器 GUI 渲染线程与 JS 引擎线程是互斥的关系,当页面中有很多长任务时,会造成页面 UI 阻塞,出现界面卡顿、掉帧等情况。

如果直接把下面这段代码直接丢到主线程中,计算过程中页面一直处于卡死状态,无法操作。

let sum = 0;
for (let i = 0; i < 200000; i++) {
    for (let i = 0; i < 10000; i++) {
      sum += Math.random()
    }
  }

使用 Web Worker 执行上述代码时,计算过程中页面正常可操作、无卡顿

// worker.js
onmessage = function (e) {
  // onmessage获取传入的初始值
  let sum = e.data;
  for (let i = 0; i < 200000; i++) {
    for (let i = 0; i < 10000; i++) {
      sum += Math.random()
    }
  }
  // 将计算的结果传递出去
  postMessage(sum);
}

Web Worker 的通信时长

当然Web Worker我们也不能滥用,毕竟加载worker也是需要消耗一定的服务器资源。并不是执行时间超过 50ms 的任务,就可以使用 Web Worker,还要先考虑通信时长的问题假如一个运算执行时长为 100ms,但是通信时长为 300ms, 用了 Web Worker可能会更慢
比如新建一个 web worker, 浏览器会加载对应的 worker.js 资源,下图中的 Time 是这个资源的通信时长(也叫加载时长)。

当任务的运算时长 - 通信时长 > 50ms,推荐使用Web Worker

三、requestAnimationFrame 制作动画

页面如果想要更加吸人眼球,动画必不可少。但是动画有时候会让页面看起来卡顿,而且也比较消耗浏览器的资源。那么为了解决这样的一些问题,我们可以使用requestAnimationFrame这个API来处理我们的动画效果。

requestAnimationFrame 是浏览器专门为动画提供的 API,它的刷新频率与显示器的频率保持一致,使用该 api 可以解决用 setTimeout/setInterval 制作动画卡顿的情况。

①引擎层面
setTimeout/setInterval 属于 JS引擎,requestAnimationFrame 属于 GUI引擎JS引擎与GUI引擎是互斥的,也就是说 GUI 引擎在渲染时会阻塞 JS 引擎的计算。

②时间是否准确
requestAnimationFrame 刷新频率是固定且准确的,但 setTimeout/setInterval 是宏任务,根据事件轮询机制,其他任务会阻塞或延迟js任务的执行,会出现定时器不准的情况。

③性能层面
当页面被隐藏或最小化时,setTimeout/setInterval 定时器仍会在后台执行动画任务,而使用 requestAnimationFrame 当页面处于未激活的状态下,屏幕刷新任务会被系统暂停。

四、JS的加载方式

关于我们页面的加载,相信很多人都知道在单线程的加载方式下,很多时候错误的编码会导致页面的阻塞,从而影响用户的体验,那么关于这点,我们应该更详细地去了解JS的加载方式。

①正常模式

<script src="index.js"></script>

这种情况下 JS 会阻塞 dom 渲染,浏览器必须等待 index.js 加载和执行完成后才能去做其它事情。也就是说如果整个页面跑下来,遇到这行代码将会直接停止页面dom的渲染,等到js文件加载完毕后,才能够再去绘制页面。

②async 模式

<script async src="index.js"></script>

async 模式下,它的加载是异步的,JS 不会阻塞 DOM 的渲染,async 加载是无顺序的,当它加载结束,JS 会立即执行。

使用场景:若该 JS 资源与 DOM 元素没有依赖关系,也不会产生其他资源所需要的数据时,可以使用async 模式,比如埋点统计。

③defer 模式

<script defer src="index.js"></script>

defer 模式下,JS 的加载也是异步的,defer 资源会在 DOMContentLoaded 执行之前,并且 defer 是有顺序的加载。

如果有多个设置了 defer 的 script 标签存在,则会按照引入的前后顺序执行,即便是后面的 script 资源先返回。所以 defer 可以用来控制 JS 文件的执行顺序,比如 element-ui.js 和 vue.js,因为 element-ui.js 依赖于 vue,所以必须先引入 vue.js,再引入 element-ui.js。

defer 使用场景:一般情况下都可以使用 defer,特别是需要控制资源加载顺序时

④module 模式

<script type="module">import { a } from './a.js'</script>

在主流的现代浏览器中,script 标签的属性可以加上 type=“module”,浏览器会对其内部的 import 引用发起 HTTP 请求,获取模块内容。这时 script 的行为会像是 defer 一样,在后台下载,并且等待 DOM 解析。

Vite 就是利用浏览器支持原生的 es module 模块,开发时跳过打包的过程,提升编译效率。

⑤preload 模式

<link rel="preload" as="script" href="index.js">

link 标签的 preload 属性:用于提前加载一些需要的依赖,这些资源会优先加载。

1)preload 加载的资源是在浏览器渲染机制之前进行处理的,并且不会阻塞 onload 事件;

2)preload 加载的 JS 脚本其加载和执行的过程是分离的,即 preload 会预加载相应的脚本代码,待到需要时自行调用;

⑥prefetch 模式

<link rel="prefetch" as="script" href="index.js">

prefetch 是利用浏览器的空闲时间,加载页面将来可能用到的资源的一种机制;通常可以用于加载其他页面(非首页)所需要的资源,以便加快后续页面的打开速度。

prefetch 特点:

1)pretch 加载的资源可以获取非当前页面所需要的资源,并且将其放入缓存至少5分钟(无论资源是否可以缓存)

2)当页面跳转时,未完成的 prefetch 请求不会被中断

加载方式的总结

async、defer 是 script 标签的专属属性,对于网页中的其他资源,可以通过 link 的 preload、prefetch 属性来预加载。

如今现代框架已经将 preload、prefetch 添加到打包流程中了,通过灵活的配置,去使用这些预加载功能,同时我们也可以审时度势地向 script 标签添加 async、defer 属性去处理资源,这样可以显著提升性能。 所以通常情况下,当我们使用了打包工具时,也不需要我们去管理太多复杂的加载形式,只需要通过简单的配置即可达到我们想要的效果。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Web应用前端技术的探索与实践 1 1 前端开发那些事 1 2 怎样提高前端的质量和工作效率? 1 附件(关于2011年工作的一点浅见) 3 3 探讨一些技术问题 4 3.1 Frameset和iframe 4 3.2 Frameset和Div两种方式的对比 5 3.2.1 frameset布局的优点 5 3.2.2 frameset布局的缺点 5 3.2.3 DIV传统布局与frameset相比的优点 5 3.2.4 DIV传统布局与frameset相比的缺点与解决办法 6 3.2.5 大量使用ajax的DIV局部页面,会遇到的一些难点 6 3.2.6 解决方法 6 3.2.7 结论 6 3.3 基于DIV的网页布局-模版的核心 7 3.3.1 概述 7 3.3.2 简单、直观的DIV布局 7 3.3.3 DIV布局问题的终极解决方案 9 3.3.4 解决DIV被撑爆的问题 12 3.3.4.1 DIV撑爆的问题 12 3.3.4.2 图片撑爆问题的解决办法 13 3.3.4.3 文字撑爆问题的解决办法 13 3.4 Java Web应用的DIV布局 14 4 主流邮件系统前端开发的研究 19 4.1 21cn企业邮 19 4.2 Sohu邮箱 21 4.3 网易邮箱 30 4.4 腾讯网络邮箱(foxmail) 40 4.5 新浪邮箱 47 4.6 关于邮箱业务的题外话 49 4.7 小结 51 5 一些应用的问题 51 5.1 21cn.com 51 5.2 综合管理平台 51 5.2.1 文件组织 51 5.2.2 UI 53 5.2.3 Jsp文件胡乱包含 53 6 前端组件及其研发的探索和实践 54 6.1 用户、应用 55 6.2 网站和web应用公共组件的层次 55 6.3 Web应用前端组件的研发原则 56 6.4 研发流程 56 6.5 常用前端组件的分析和研究 57 6.5.1 概述 57 6.5.2 通用组件 58 6.5.2.1 Accordion 58 6.5.2.1.1 效果 59 6.5.2.1.2 参数说明 60 6.5.2.2 Tab 61 6.5.2.2.1 效果 61 6.5.2.2.2 参数说明 64 6.5.2.2.3 事件说明 64 6.5.2.2.4 方法说明 65 6.5.2.2.5 属性说明 65 6.5.2.3 封装Accordion和Tab为Switchable 65 6.5.2.3.1 使用JQuery Tools构建 67 6.5.2.3.2 JQuery Tools的Switchable对Ajax的支持 68 6.5.2.3.3 使用JQuery Switchable 70 6.5.2.3.4 使用kissy Switchable 78 6.5.2.4 统计分析图表组件 94 6.5.2.4.1 应用实例1 95 6.5.2.4.2 应用实例2 97 6.5.2.5 DataGrid1-浏览表格数据 102 6.5.2.5.1 效果 103 6.5.2.5.2 参数说明 107 6.5.2.6 DataGrid2-可编辑cell 110 6.5.2.6.1 效果 110 6.5.2.7 DataGrid3-控制列是否显示 113 6.5.2.7.1 效果 113 6.5.2.8 Tree 116 6.5.2.8.1 效果1 116 6.5.2.8.2 效果2 117 6.5.2.9 树表组件 121 6.5.2.9.1 TABLETREE4J 121 6.5.2.9.2 JQuery TreeGrid 124 6.5.2.10 Combo扩展1-去除IE6下穿透层的bug 129 6.5.2.10.1 效果 130 6.5.2.11 Combo扩展2-新样式、可多选 131 6.5.2.11.1 效果 131 6.5.2.12 Combo扩展3-ComboGrid 134 6.5.2.12.1 效果 134 6.5.2.13 Combo扩展4-ComboTree 137 6.5.2.13.1 效果 137 6.5.2.14 日期-时间选择 139 6.5.2.14.1 基于JQuery EasyUI的日期选择组件 139 6.5.2.14.2 My97DatePicker 142 6.5.2.15 TimeSpinner 146 6.5.2.15.1 效果 146 6.5.2.15.2 应用 147 6.5.2.16 NumberSpinner 148 6.5.2.16.1 效果 148 6.5.2.17 MenuButton 149 6.5.2.17.1 效果 149 6.5.2.18 分页组件Pagination 151 6.5.2.18.1 效果 151 6.5.2.18.2 参数说明 152 6.5.2.19 文件上传组件 153 6.5.2.19.1 uploadify 153 6.5.2.19.2 swfUpload 159 6.5.2.20 消息提示、弹出广告组件 162 6.5.2.20.1 效果 162 6.5.2.20.2 方法说明 165 6.5.2.20.3 扩展说明 166 6.5.2.21 Panel组件 166 6.5.2.21.1 效果 166 6.5.2.21.2 参数说明 168 6.5.2.21.3 事件说明 169 6.5.2.21.4 方法说明 170 6.5.2.22 Window组件 170 6.5.2.22.1 效果 170 6.5.2.22.2 使用说明 172 6.5.2.23 Form数据验证 173 6.5.2.23.1 效果 173 6.5.2.24 内容自动完成、Suggest 174 6.5.2.24.1 效果 175 6.5.2.24.2 应用说明 176 6.5.2.25 WYSIWYG在线Html内容编辑器 179 6.5.2.25.1 SinaEditor 179 6.5.2.25.2 CKEditor 181 6.5.2.26 JMesa 184 6.5.2.26.1 效果 185 6.5.2.26.2 一个较为复杂的应用实例 185 6.5.3 综合实例 191 6.5.3.1 Portal 191 6.5.4 展望 195 7 前端页面的优化工作概论 195 7.1 前端页面优化策略 196 7.2 网站加速的主要方法 198 7.2.1 Web层 198 7.2.2 应用层 203 8 总结 205
前端项目打包优化可以从以下几个方面入手: 1. 代码压缩:使用 webpack 的 UglifyJSPlugin 或 TerserPlugin 进行代码压缩,减小代码体积; ``` const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); module.exports = { // ... optimization: { minimizer: [ new UglifyJSPlugin({ uglifyOptions: { compress: { warnings: false, drop_console: true,//去除console.log drop_debugger: true,//去除debugger pure_funcs: ['console.log']//指定移除的函数 } } }) ] } }; ``` 2. Tree shaking:使用 webpack 的 optimize.ModuleConcatenationPlugin 进行模块合并,减少代码冗余; ``` const webpack = require('webpack'); module.exports = { // ... plugins: [ new webpack.optimize.ModuleConcatenationPlugin() ] }; ``` 3. 懒加载:使用 webpack 的 import() 方法实现按需加载,减少首屏加载时间; ``` function getComponent() { return import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => { var element = document.createElement('div'); element.innerHTML = _.join(['Hello', 'webpack'], ' '); return element; }).catch(error => 'An error occurred while loading the component'); } getComponent().then(component => { document.body.appendChild(component); }); ``` 4. CDN 加速:使用外部 CDN 加速加载常用库,减少服务器压力和网络请求时间; ``` <script src="https://cdn.bootcdn.net/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script> ``` 5. 缓存优化:使用 webpack 的 hash 或 chunkhash 为打包文件添加哈希值,实现静态资源缓存,提高页面加载速度; ``` module.exports = { // ... output: { filename: '[name].[chunkhash].js', path: path.resolve(__dirname, 'dist') } }; ``` 以上是一些常见的前端项目打包优化方法,具体实现可以根据项目需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值