官网性能优化总结

摘要

使用lighthouse进行性能检测,并对lighthouse提出的建议进行优化。

lighthouse

chrome的插件用不了,下了一个本地的。

## 下载
npm install -g lighthouse

## 运行
lighthouse 127.0.0.1:8080 --view --emulated-form-factor desktop -throttling-method=provided

主要优化

静态化

服务端渲染,“直出”页面,具有较好的SEO和首屏加载速度。主要还有以下的优点:

  • 使用jsp模板语法(百度后发现是用Velocity模板语法)渲染页面,减少了js文件体积
  • 减少了请求数量
  • 因为不用等待大量接口返回,加快了首屏时间

可以尝试Vue的服务端渲染。首页目前有部分是用接口读取数据,然后用jq进行渲染,性能上应该不如Virtual DOM,不过内容不多。

图片懒加载

这是一个很重要的优化项。因为官网上有很多图片,而且编辑们上传文章图片的时候一般没有压缩,但是很多图片的体积都很大。还有一个轮播图,20张图标,最小的几十K,最大的两百多K。对于图片来源不可控的页面,懒加载是个很实用的操作,直接将首屏加载的资源大小加少了十几M。

图片压缩

对于来源可控,小图标等图片可以用雪碧图,base64等方法进行优化。目前只是用工具压缩了图片大小,后续可以考虑在webpack打包的时候生成雪碧图。

异步加载js

通过<script>标签引入的js文件,可以设置deferasync属性让其异步加载,而不会阻塞渲染。deferasync的区别在于async加载完就立即执行,没有考虑依赖,标签顺序等。而defer加载完后会等它前面引入的文件执行完再执行。一般defer用的比较多,async只能用在那些跟别的文件没有联系的孤儿脚本上。
因为项目还是用webpack打包的,打包结果会将js文件以<script>标签的形式注入到html模板中,那要如何给这个标签加上defer属性呢?通过搜索,发现了webpack有一个html-webpack-plugin插件的扩展插件script-ext-html-webpack-plugin,可以通过这个插件给注入到html中的script标签加上defer属性。

const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');

plugins.push(new ScriptExtHtmlWebpackPlugin({
  defaultAttribute: 'defer'
}))

异步加载css

没想到css也能异步加载,但这是lighthouse给出的建议。找了一下发现有以下两种方法:
一是通过js脚本在文档中插入<link>标签
二是通过<link>media属性
media属性是媒体查询用的,用于在不同情况下加载不同的css。这里是将其设置为一个不适配当前浏览器环境的值,甚至是不能识别的值,浏览器会认为这个样式文件优先级低,会在不阻塞的情况加载。加载完成后再将media设置为正常值,让浏览器解析css。

<link rel="stylesheet" href="//example.css" media="none" onload="this.media='all'">

这里用的是第二种方法。但是webpack注入到html中的外链css还没找到异步加载的方法。

preconnent

lighthouse建议对于接下来会访问的地址可以提前建立连接。一般有一下几种方式。

dns-prefetch
域名预解析

<link rel="dns-prefetch" href="//example.com">

preconnet
预连接

<link rel="preconnect" href="//example.com">
<link rel="preconnect" href="//cdn.example.com" crossorigin>

prefetch
预加载

<link rel="prefetch" href="//example.com/next-page.html" as="html" crossorigin="use-credentials">
<link rel="prefetch" href="library.js" as="script">

prerender
预渲染

<link rel="prerender" href="//example.com/next-page.html">

这四种层层递进,但是不要连接不需要的资源,反而损耗性能。我在页面上对某些资源用了preconnect,但并没有明显的效果。应该对于在线小说,在线漫画这种场景预加载会更适用。

代码优化

lighthouse上显示主线程耗时最多的是样式和布局,所以对这部分进行优化。主要有一下几点:

  • 去掉页面上用于布局的table,table本身性能较低,且维护性差,是一种过时的布局方案。
  • 在去掉table布局的同时减少一些无意义的DOM元素,减少DOM元素的数量和嵌套。
  • 减少css选择器的嵌套。用sass,less这种css预处理器很容易造成多层嵌套。优化前代码里最多的有七八层嵌套,对性能有一定影响。重构后不超过三层。

通过上面的重构后,样式布局和渲染时间从lighthouse上看大概减少了300ms。但样式和布局的时间还是最长的,感觉还有优化空间。

接下来是js代码的优化和重构。因为移除Vue框架,以及用服务端端直出,现在js代码已经减少了大部分。主要有以下几部分:

  • 拆分函数,将功能复杂的函数拆分成小函数,让每个函数只做一件事。
  • 优化分支结构,用对象Object,代替if...elseswitch...case

如下面这段代码,优化后变得更加简洁,也便于维护。

// 优化前
var getState = function (state) {
  switch (state) {
    case 1:
      return 'up';
    case 0:
      return 'stay';
    case 2:
      return 'down';
  }
}

// 优化后
var getState = function(state) {
  var stateMap = {
    1: 'up', 0: 'stay', 2: 'down'
  }
  return stateMap[state]
}
  • 优化DOM操作

DOM操作如改变样式,改变内容可能会引起页面的重绘重排,是比较消耗性能的。网上也有很多优化jq操作的方法。
如将查询到的DOM使用变量存起来,避免重复查询。以及将多次DOM操作变成一次等。这里重点讲一下第二种。
常见的需求是渲染一个列表,如果直接在for循环里面append到父元素中,性能是非常差的。幸好原来的操作是将所有DOM用字符串拼接起来,再用html()方法一次性添加到页面中。
还有另一种方法是使用文档碎片(fragment)。通过document.createDocumentFragment()可以新建一个fragment。向fragmentappendChild元素的时候是不会阻塞渲染进程的。最后将fragment替换掉页面上的元素。将fragment元素用appendChild的方法添加到页面上时,实际上添加上去的是它内部的元素,也就是它的子元素。

var fragment = document.createDocumentFragment()
for (var i = 0; i < data.length; i++) {
  var str = '<div>' + i + '</div>'
  fragment.appendChild($(str)[0])
}
$('.container').append(fragment)

经过测试,在当前的场景下,使用fragment的速度和html()是差不多的,都是10ms左右。区别在于最后将fragment添加到页面上$('.container').append(fragment)这行代码仅仅花费1ms。也就是说,将fragment插入页面时不会引起页面重绘重排,不会引起阻塞。

其他还没做的优化

lighthouse的提议还有以下几点:

  • 使用新的图片格式,如webp。以或得更高的压缩率和更小图片体积。但是新的图片格式兼容性不是很好,故抛弃。
  • 使用文本压缩,如gzip。js,css和html文件都可以使用gzip压缩,webpack也有对应的配置项。有时候费尽脑汁去考虑怎么减小代码体积都不如gzip压缩来的有效果。但这个需要在服务器配置。

总结

个人认为,对首屏加载速度影响最大的还是资源大小,请求数量,请求速度等。代码方面,前端一般很难写出严重影响速度的代码。减小资源大小,可以用各种压缩,懒加载,预加载,异步加载等方法。减少请求数量可以使用雪碧图,搭建node中台将多个请求合并成一个等。对于官网这种项目,最好使用服务端渲染,首屏快之外,也有利于SEO。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 资源的合并与压缩 核心:减少http请求数量(合并)减少资源请求大小(压缩) html压缩:压缩这些在文本文件中有...
    _小海绵阅读 401评论 0 0
  • 本文原创:gaoruixia 一. 开篇 提起前端性能优化,大家会想到哪些内容呢? 相信不同的人给出的答案不同,关...
    jad_design阅读 1,238评论 0 0
  • 先从一道题目说起 从输入 URL 到页面加载完成,发生了什么? 站在性能优化的角度;我们可以分为5个过程; DNS...
    QRFF阅读 2,562评论 0 3
  • 减少请求数量 【合并】如果不进行文件合并,HTTP请求次数增多,受丢包影响更严重,但是,文件合并后也会带来首屏渲染...
    小小的开发人员阅读 628评论 0 0
  • 围绕前端的性能多如牛毛,涉及到方方面面,以我我们将围绕PC浏览器和移动端浏览器的优化策略进行罗列注意,是罗列不是展...
    流动码文阅读 672评论 0 0