[TOC]
目标
- 理解性能监控和优化的一般方法
- 移动端的性能
- 运用工具来监控和优化
性能优化的重要性
1. 性能和PV直接相关
2. 性能与收入有关
3. 前端容易产生性能的瓶颈
4. 无线设备性能问题更明显
反面案例: 百科某次上线,性能下降20% ====> PV流失近15%====> 收入下降10%
移动端更严重 DOM ready时间: wifi 2.3s, 3G/4G 4.2s, 2G 首字节5-6s, 8s+
一、何为前端性能
打开一个网站,需要经过多少步
性能优化的一般思路
- 减少请求次数
- 减少请求体积
- 加快请求速度
- 缩短渲染时间
二、指标的确定、采集和分析
基于用户角度的关键指标选取
- 页面打不开 -- 白屏时间(DNS查询,TCP连接,发送请求,等待响应)
- 页面显示不出来 -- 首屏时间(html传输,静态资源下载)
- 按钮点击不了 -- 用户可操作(解析文档,执行JS/CSS规则,计算布局)
- 图片显示慢 -- 页面总下载(渲染完成)
怎么采集展示
页面打开的时间起点到页面渲染完成的时间各阶段时间分布,总占比
数据波动
波动即意味着性能发生变化,需要注意
占比更重要
总下载的时间,各阶段的用户下载占比(很快0-2,较快2-4,可接受4-8,很慢8+)
数据采集-首屏时间
图片是制约首屏的主要因素
获取首屏图片的加载耗时即可获取大概首屏时间
首屏统计流程
- 首屏大概位置执行统计JS
- 绑定所有图片加载事件
- 页面onload之后找到最慢一张图片加载时间
数据采集- 可操作时间
DomReady或核心JS加载完毕
数据采集-总下载时间
onload or 异步渲染完成
浏览器性能API
三、常用的优化方法
- 雅虎性能优化军规
- 可优化的点
- DNS查询
- DNS缓存(浏览器、操作系统)
- 减少DNS数(一个页面不超过4个, DNS预查询dns-prefetch)
- 建立连接
- 使用CDN 提速10%-20%(缩短距离,降低连接时间)
- 发送请求
- 减少HTTP请求(打包JS,CSS文件,图片合并)
- Keep alive 减少TCP请求连接数
- 内容传输
- 文件压缩(js, css, html),代码混淆 60%+
- 代码精简 减少无用代码,提高质量
- gzip
- 缓存
- Expires、Cache-Control
- Last-Modified
- Etag
- 充分利用缓存--强缓存(文件名md5后缀)
- DNS查询
- 前端工程化与性能优化--静态资源管理 自动将样式表放在头部,脚本放在底部,并按需加载
- 代码的性能--css
- 书写高效的CSS selectors
- 删除没用的CSS代码
- 避免使用CSS expressions(实践中比较难~~~)
- 把CSS放到页面顶部
- 不要缩放图片--很大的图片缩到很小展示,耗性能
- 代码的性能--JavaScript
- 数据结构和算法优化
- 避免with, eval
- 减少跨上下文查找: 全部变量、属性
- 避免arguments、debugger
- 缓存计算结果
- DOM才是性能大头
- 避免DOM重绘(避免访问childNode数组,读写分离),不在for循环重绘DOM
- 收回、重复利用DOM
- 缓存数据而不是DOM
- MVVM 框架的DOM 只在需要时才更新DOM 性能:JS Engine > Render Engine
- React的性能优化
-
shouldComponentUpdate / PureComponent
-
全局数据store管理(redux) + immutable
-
在shouldComponentUpdate里全部return false,在componentWillReceiveProps里判断是否需要更新
export class Line extends React.Component { shouldComponentUpdate(nextProps) { return false; } componentWillReceiveProps(nextProps) { // 只有某些数据发生改变时进行绘图 if (this.props.data !== nextProps.data) { this.refresh(); } } ... } -
Debounce防抖延迟更新 compositionstart, compositionend 解决中文输入时的频繁更新
export default class DebounceText extends Component { componentWillMount() { this.props_onChange = debounce(this.props.onchange, 300); } onChange = val => { this.setState({ value: val }); this.props_onChange(val); } render() { let props = Object.assign({}, this.props, {value: this.state.value}); delete props.onChange; return <Input value={this.state.value} onChange={this.onChange} /> } } -
React 性能优化
- redux --> mobx, mobx-state-tree 对redux性能优化,主动用observer观察数据的变化...
- 避免过度的动画效果
-
四、极致的性能优化
- 懒加载(lazy render), 逐屏加载,优化首屏时间
- bigRender 减少DOM数,提升首屏(DOM树转为注释,页面滚动到指定位置才加载)
- bigpipe 一次请求,分chunk方式获取所有内容,后端并发
Request-header (http 1.1)
- transfer-encoding: chunked
- content-length 先给前端吐一个结构,在HTML结束后,输出一些js代码渲染内容上去 性能提升 贴吧首页白屏提升40% FRS页贴吧列表区展示时间提升20% 弊端 代码复杂度 后端成本
五、移动端的性能
-
显性加载- loading页
-
首屏优先
- 单页应用首屏后端渲染
- 逐屏加载
- 滚动加载(用户无感知,提速30%)
-
减少首屏图片数量,减少请求
- 使用其他方式代替图片: css3,svg,iconfont
- 合适的图片类型: webP > jpg, png8>gif
- 避免使用dataURL
- 响应式图片 分辨率、Retina srcset, picture
-
加载过程的优化
- 预加载: 提前加载下一页
- 避免302重定向
- 异步加载广告等第三方资源
- 减少cookie:静态资源域名不适用cookie
- 长缓存
-
执行过程的优化
- 合理使用CSS 3D加速
- 避免批量绑定数量,使用事件代理
- 严格控制DOM数,缓存数据而不是DOM(不超20层)
- 用touchstart, touchend代替click
-
利用Native的能力
将一些不频繁更新的一些东西本地化
- Hybird的加速方式
- 模板资源本地化,极大提速首屏
- 模板资源的增量下发
- 图片缓存:imageCache
- 利用NA(native)的Request、socket长连接
- 利用NA获取用户信息、位置等
手百使用Hybird的性能收益
- 相比H5方案,首屏时间降低60%+
- 模板增量下发,体积减小90%
- Hybird的加速方式
总结
前端性能十分关键,影响PV 和收入 找到关键指标,用统计到的数据说话 性能优化贯穿页面的整个响应过程 无线端更需要优化 监控-分析-优化,利用现有的工具方案