欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前端性能优化

程序员文章站 2022-07-12 13:35:36
...

浏览器渲染机制

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赋值。

前端性能优化