前端性能优化

浏览器渲染机制

Html解析成DOM树,Css解析成CSS树,将DOM树与CSSDOM规则树合并在一起生成Render树,遍历渲染树开始布局,计算每个节点的位置大小信息,将渲染树每个节点绘制到屏幕

  • 阻塞渲染

当浏览器遇到一个script标记时,DOM构建将暂停,直至脚本完成执行,然后继续构建DOM。每次去执行JavaScript脚本都会严重的阻塞DOM树的构建,如果JavaScript脚本还操作了CSSOM,而正好这个CSSOM还没下载和构建,浏览器甚至会延迟脚本执行和构建DOM,直至完成其CSSOM的下载和构建

1.link,script放置位置

浏览器下载脚本时,会阻塞其他资源并行下载,即使是来自不同域名的资源。因此,最好将脚本放在底部,以提高页面加载速度。link的css样式放在head中,引入的包放在自己的js之前。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="D:/deng/project/myreports-project/first/index.css" />
  </head>
  <body>
    <div id='#app'></div>
    <script src='./index.js'></script>
  </body>
</html>

一、HTTP

从输入URL到浏览器显示页面发生了什么

1.减少域名使用

浏览器获取资源需要做DNS解析,建立了TCP/IP的连接,所以项目中存在很多域名时会直接导致加载缓慢。

2.减少HTTP请求数

  • 将图片的图标合并成一个文件,利用background-position来调整位置
  • 行内图片(Base64编码),使用Data URI scheme将图片嵌入HTML或者CSS中;或者将CSS、JS、图片直接嵌入HTML中,会增加文件大小,也可能产生浏览器兼容及其他性能问
  • 合并JS/CSS文件。服务器端(CDN)自动合并,基于Node.js的文件合并工具,通过把所有脚本放在一个文件中的方式来减少请求数

3.减少DNS查询

浏览器需要查询域名对应的ip的地址,一般需要耗费20-120毫秒时间。DNS查询完成之前,浏览器无法从服务器下载任何数据,但是可以缓存

IE缓存30分钟,可以通过注册表中DnsCacheTimeout项设置;
Firefox缓存1分钟,通过network.dnsCacheExpiration配置;

4.缓存

前端缓存最佳实践

在介绍缓存的时候,我们习惯将缓存分为强缓存和协商缓存两种。两者的主要区别是使用本地缓存的时候,是否需要向服务器验证本地缓存是否依旧有效。顾名思义,协商缓存,就是需要和服务器进行协商,最终确定是否使用本地缓存。

5.延迟加载

页面初始加载时哪些内容是绝对必需的?不是必须展示的资源都可以延迟加载

非首屏使用的数据、样式、脚本、图片等;
用户交互时才会显示的内容。

6.预加载

预先加载利用浏览器空闲时间请求将来要使用的资源,以便用户访问下一页面时更快地响应。根据用户行为预判用户去向,预载相关资源。比如search.yahoo.com开始输入时会有额外的资源加载。Chrome 等浏览器的地址栏也有类似的机制。

7.减少DOM数量和层级

复杂的页面不仅下载的字节更多,JavaScript DOM操作也更慢。例如,同是添加一个事件处理器,500个元素和5000个元素的页面速度上会有很大区别。而且层级很深对SEO也不友好,结构变化时,回流时更需要时间。

8.减少使用iframe

用iframe可以把一个HTML文档插入到父文档里,重要的是明白iframe是如何工作的并高效地使用它。

加载代价昂贵,即使是空的页面;
阻塞页面 load 事件触发;
不利于SEO

9.内容划分到不同域名

浏览器一般会限制每个域的并行线程(一般为6个,甚至更少),使用不同的域名可以最大化下载线程,但注意保持在2-4个域名内,以避免DNS查询损耗。

二、服务器

1.CDN加速

CDN的全称Content Delivery Network,(缩写:CDN)即内容分发网络。

CDN是一个经策略性部署的整体系统,从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均而产生的用户访问网站响应速度慢的根本原因。

CDN目的是通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的网络“边缘”,使用户可以就近取得所需的内容,解决 Internet 网络拥塞状况,提高用户访问网站的响应速度。

一般的公司不可能在每个城市地区都有服务器,那么用户一次完整的请求要经过的历程将路漫漫其修远兮

2.缓存

前端缓存最佳实践

HTML:使用协商缓存。
CSS&JS&图片:使用强缓存,文件命名带上hash值。

3.GZIP压缩

Gzip压缩通常可以减少70%的响应大小,对某些文件更可能高达90%,比Deflate更高效。主流 Web 服务器都有相应模块,而且绝大多数浏览器支持gzip解码。所以,应该对HTML、CSS、JS、XML、JSON等文本类型的内容启用压缩,Nginx能直接读取gzip文件。

注意!!! 图片和 PDF 文件不要使用 gzip。它们本身已经压缩过,再使用 gzip 压缩不仅浪费 CPU 资源,而且还可能增加文件体积。

4.ajax尽量使用GET方法

浏览器执行XMLHttpRequest POST请求时分成两步,先发送Http Header,再发送data。而GET只使用一个TCP数据包(Http Header与data)发送数据,所以首选GET方法。根据HTTP规范,GET用于获取数据,POST则用于向服务器发送数据,所以Ajax请求数据时使用GET更符合规范。

5.负载均衡

Load balancing,即负载均衡,是一种计算机技术,用来在多个计算机(计算机集群)、网络连接、CPU、磁盘驱动器或其他资源中分配负载,以达到最优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。

6.提高带宽

最直接有效的方法就是给服务器升级,提高带宽与内存,既能抵挡cc攻击,又能让并发变高。

三、性能检测工具

1.谷歌插件——Page Speed

我们只需要打开待测试的网页,然后点击Page Speed里的 Start analyzing按钮,它就会自动帮我们测试网络传输性能了

2.谷歌录制

然后结束后能查看当前网页的nodes,listener,deep,可以分析出是否存在内存溢出风险,性能优化需要的修改。

四、Css

1.favicon

请不要忘记为你的网站加上它,它就好像是你的网站的 ID。无论你有没有 favicon.ico ,用户的浏览器依然会请求它。如果你忘记加上这个档案,你的网站就会返回 404 Not Found,这会让浏览器面红。。。所以你要小心一点,避免给予用户负面的第一印象。要解决这个问题,你可以透过?Favicon Generator?生成 favicon 和 manifest 档案。

2.把link放在中

把样式表放在中可以让页面渐进渲染,尽早呈现视觉反馈,给用户加载速度很快的感觉。这对内容比较多的页面尤为重要,用户可以先查看已经下载渲染的内容,而不是盯着白屏等待。

3.避免使用CSS表达式

CSS表达式,如下面这个例子:

background-color:expression((new Date().getHours()%2?"#B8D4FF":"#F08A00"));

这个表示是为了实现背景颜色每2个小时变化一次;这种会导致性能下降。不过应该多数开发人员比较少使用CSS表达式。这里就一笔带过就好了。

4.使用替代@import

对于IE某些版本,@import的行为和放在页面底部一样。所以,不要用它。

五、JavaScript

1.把脚本放在页面底部

浏览器下载脚本时,会阻塞其他资源并行下载,即使是来自不同域名的资源。因此,最好将脚本放在底部,以提高页面加载速度。

2.减少DOM操作

JavaScript 操作 DOM 很慢,尤其是 DOM 节点很多时。当然现在前端框架都是使用虚拟DOM,很少直接操作DOM了。

3.使用高效的事件处理

减少绑定事件监听的节点,如通过事件委托,就是子级交互都是一个,不如父级来添加这个事件;尽早处理事件,在DOMContentLoaded即可进行,不用等到load以后。

六、React

1.shouldComponentUpdate避免重复渲染

react提供这个钩子函数来优化不必要的渲染,可以自定义渲染时机

class CounterButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

  render() {
    return (
      <button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      </button>
    );
  }
}

2.bind函数

  • (1)constructor绑定
constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this); //构造函数中绑定
}
//然后可以
<p onClick={this.handleClick}>
  • (2)使用时绑定
<p onClick={this.handleClick.bind(this)}>
  • (3)使用箭头函数
<p onClick={()=>this.handleClick()}>

使用第一种,第二种每次render都会执行,第三种每次render都会生成箭头函数。

3.不要滥用props

props的更改会导致子组件更新,props尽量只传需要的数据,避免多余的更新,尽量避免使用{…props}

4.Key的绑定

为list添加key,能帮助react在更新时准确找到要更新的部分。

5.组件按需引入

import { Button } from 'antd' 

6.打包压缩代码

使用webpack打包压缩代码。

7.路由拆分

可以将路由打包拆分,将会生成多个js文件,在路由加载到的时候才加载该js文件,像umijs就自带路由系统,能配置路由是否按需加载。

七、Vue

1.路由按需加载

可以将路由打包拆分,将会生成多个js文件,在路由加载到的时候才加载该js文件,具体实现方案的话,如下:

1、基于require.js来实现引入模块的方式(比较老)

let routes = [
  {path: '/home', component: resolve => require(['./components/Home.vue'], resolve), children:[
    {path: '/detail', component: resolve => require(['./components/Detail.vue'], resolve)}
  ]}
]

2、es6模块化的拆分形式

let routes = [
  {path: '/home', component: () => import('./components/Home.vue'), children:[
    {path: '/detail', component: () => import('./components/Detail.vue'), resolve)}
  ]}
]

2.组件按需加载

像这种全局引入会导致最终打包文件过大,首次加载时间非常长

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

3.v-if 和 v-show选择调用

v-show和v-if的区别是:v-if是懒加载,当状态为true时才会加载,并且为false时不会占用布局空间;v-show是无论状态是true或者是false,都会进行渲染,并对布局占据空间对于在项目中,需要频繁调用,不需要权限的显示隐藏,可以选择使用v-show,可以减少系统的切换开销。

4.不要滥用props

props的更改会导致子组件更新,props尽量只传需要的数据,避免多余的更新,尽量避免使用{…props}

5.Key的绑定

为list添加key,能帮助react在更新时准确找到要更新的部分。

6.打包

1、sourcemap 一个可以从中查看源码的文件,但是在线上环境是没必要的,这个就可以关闭

2、别名的使用,使用别名比使用相对路径在服务器上查找文件更快

module.exports = {
  resolve: {
    extensions: ['.js', '.vue', '.json'],     //  使用这些后缀的文件时就可不写扩展名
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  }
}

3、assetsPublicPath设置为/时能被nginx代理识别,但是无法被文件系统识别,但是访问速度比设置成./时快

module.exports = {
  build:{
    assetsPublicPath: '/',
  }
}

4、js文件压缩,webpack使用uglifyjs-webpack-plugin插件压缩js代码

八、图片资源

1.避免图片src为空

虽然src属性为空字符串,但浏览器仍然会向服务器发起一个HTTP请求:

IE 向页面所在的目录发送请求;
Safari、Chrome、Firefox向页面本身发送请求;
Opera不执行任何操作。

空src产生请求的后果不容小觑:

给服务器造成意外的流量负担,尤其时日 PV 较大时;
浪费服务器计算资源;
可能产生报错。

2.雪碧图(Sprite)

将很多图片图标整合到一张图片,通过background-position来控制显示位置。

3.使用字体图标(iconfont)

不论是压缩后的图片,还是雪碧图,终归还是图片,只要是图片,就还是会占用大量网络传输资源。字体图标是通过css实现的,而且可以通过font-size来改变尺寸,通过color来改变颜色,比图片资源更灵活。

4.不要在HTML中缩放图片

不要使用的width、height缩放图片,如果用到小图片,就使用相应大小的图片。如果需要那么图片本身(mycat.jpg)应该是100x100px的,而不是去缩小500x500px的图片。现在很多云存储支持图片裁剪,能自定义很多种的裁剪方式。

5.使用WebP

WebP格式,是谷歌公司开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器带宽资源和数据空间。Facebook、Ebay等知名网站已经开始测试并使用WebP格式。

6.图片懒加载

一张图片就是一个标签,浏览器是否发起请求图片是根据的src属性,所以实现懒加载的关键就是,在图片没有进入可视区域时,先不给的src赋值,这样浏览器就不会发送请求了,等到图片进入可视区域再给src赋值。

热门文章

暂无图片
编程学习 ·

NASM系列啊

文章目录NASM是什么?NASM下载与安装 NASM是什么?The Netwide Assembler, 一款基于80x86和x86-64平台的汇编语言编译程序, 是为了实现编译器程序跨平台和模块化的特性。 NASM支持大量的文件格式,包括Linux,*BSD,a.out,ELF,COFF,Mach−O,Microsoft 16−bit OBJ,Win32…
暂无图片
编程学习 ·

报表热切换是什么意思?如何做到?

热切换(Hot Swap)是指在系统不停机的情况下更换系统部件,在报表业务中则是指在不重启报表及相关应用的情况下完成对报表的维护(新增、修改、删除),冷切换则恰好相反。报表业务很不稳定,业务开展的过程中会刺激出更多查询统计需求,如果每次需求实现后都要等系统空闲(往…
暂无图片
编程学习 ·

Hadoop集群的四个配置文件的常用属性解析

在启动hadoop集群的守护线程时,一定会加载并运行相关的class字节码文件。通过common模块和hdfs模块里的源码可以看到,它们读取了相关的配置文件。hadoop-common-2.7.3-sources.jar下的org.apache.hadoop.conf.Configuration源文件的部分源码:package org.apache.hadoop.conf…
暂无图片
编程学习 ·

练习2-1 Programming in C is fun! (5分)

练习2-1 Programming in C is fun! (5分) 本题要求编写程序,输出一个短句“Programming in C is fun!”。 输入格式: 本题目没有输入。 输出格式: 在一行中输出短句“Programming in C is fun!”。 #include <stdio.h>int main() {printf("Programming in C is fun…
暂无图片
编程学习 ·

easyui datagrid deleteRow(删除行)的BUG

有时候想临时保存一些数据,等确定好后在批量一次提交,但EasyUI datagrid 用的时候添加可以正常,如果从中间删除那行号就全乱了。导致删除的时候有可能删除上一行数据。function addFileRow(){$(#FileTable).datagrid(appendRow,{ File_Name:"aaaa",File_Path:&qu…
暂无图片
编程学习 ·

【论文阅读报告】Visualizing and Understanding Convolutional Networks

背景 众所周知,卷积神经网络在图像处理方面表现突出,但是在很多情况下,我们在调神经网络的参数时只是依靠运气,并不知道自己对参数和网络结构的调整会影响神经网络的哪一部分。因此这篇文献的目的就是让我们通过一种可视化的方法来了解卷积神经网络如何工作,以及每一层的特…
暂无图片
编程学习 ·

必应每日壁纸——7月

只分享,不科普 自行必应科普July1 Wednesday2 Thursday3 Friday4 Saturday5 Sunday6 Monday7 Tuesday8 Wednesday9 Thursday10 Friday11 Saturday12 Sunday July 1 Wednesday 班夫国家公园 莫兰湖德国卡塞尔威廉高地公园中的阿波罗神庙2 Thursday 3 Friday 4 Saturday 5 Sunda…
暂无图片
编程学习 ·

java 并发 join 之 老王泡茶

package juc;import java.util.concurrent.TimeUnit;/*** @author yanjun.liu* @date 2020/7/1--17:00*/ public class Test6 {public static void main(String[] args) throws InterruptedException {Thread lw= new Thread(()->{try {System.out.println("老王开始洗…
暂无图片
编程学习 ·

“/“和 “\“以及“//“和“\\“有什么区别?

大家在学习生活中对于斜杠肯定不会陌生,不管在网址或者地址都会接触到这些东西,这几天刚好抽出一段时间来给大家讲解一下,有什么问题遗漏大家多多指正!!"/"正斜杠表示除法,分割;在windows系统中常用来分割“命令行参数”;正斜杠还表示虚拟路径,比如网址。&q…
暂无图片
编程学习 ·

ESP32使用MicroPython快速开发

Python基本语句一:Print语句:1. 输出字符串和数字>>>print("runoob") # 输出字符串runoob>>> print(100) # 输出数字100>>> str = runoob>>> print(str) # 输出变量runoob>>> L = [1,2,a] …
暂无图片
编程学习 ·

docker常用简单命令

检查内核版本 uname -r 如果内核版本小于3.10执行 yum update 安装docker yum install docker 启动docker systemctl start docker 查看docker版本 docker -v 开机自启动docder systemctl enable docker 停止docker systemctl stop docker ///////////////////////////////////…
暂无图片
编程学习 ·

算法选择判断

目录模拟题判断选择 模拟题利用主定理计算时间复杂度函数的渐进时间复杂度: T(n)=16T(n/4)+n 【T(n)=kT(n / m)+nd k=16,m=4,d=1,有 k>md , T(n)=θ(n2) – T(n)=2T(n/3)+O(n) O(n)的规模是n,在主定理中,k=2,m=3,d=1,因为k<md , T(n)=n 分支限界法在活结点表按照优先…
暂无图片
编程学习 ·

kubernetes-RBAC、dashboard-12

资源请求属性Kubernetes是基于http或https协议工作的(restful风格),因此其对应的操作请求,无非就是增删改查(get、put、delete),因此在每一个Kubernetes的相关请求当中,通常这个请求会包含类似以下的信息; 理论来源 user: 用户名称; group: 用户所属的组; extra: …
暂无图片
编程学习 ·

网络安全技术及应用第3版 主编贾铁军等——教材习题 期末重点 复习题 知识提炼(第4章 黑客攻防与检测防御)

参考教材:网络安全技术及应用 第3版 主编贾铁军等 第4章 黑客攻防与检测防御填空题简答题论述题 填空题 (1)端口扫描的 防范也称为 系统“加固”,主要有 防止IP地址的扫描 和(关闭 闲置及有潜在危险 端口)。 (2)(分布式拒绝服务攻击DDoS)就是利用更多的傀儡机对目标发…
暂无图片
编程学习 ·

Design-适配器模式

适配器模式介绍实现代码实现 介绍 意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对…