github-myBlob
css
script
标签的话,会判断是否存在async
或者defer
,前者会并行进行下载并执行 JS,后者会先下载文件,而后等待 HTML 解析完成后顺序执行,若是以上都没有,就会阻塞住渲染流程直到 JS 执行完毕。遇到文件下载的会去下载文件,这里若是使用 HTTP 2.0 协议的话会极大的提升多图的下载效率。DOMContentLoaded
事件根据上面的过程能够看到,页面的加载过程主要分为下载、解析、渲染三个步骤,总体能够从两个角度来考虑:html
咱们能够打开 Chrome 的调试工具来分析此阶段的性能指标
在创建 TCP 链接的阶段(HTTP 协议是创建在 TCP 协议之上的)前端
在请求响应的阶段vue
依据上面的指标给出如下几点优化方案(仅供参考)react
条件:拥有多个域名
Chrome 浏览器只容许每一个源拥有 6 个 TCP 链接,所以能够经过划分子域的方式,将多个资源分布在不一样子域上用来减小请求队列的等待时间。然而,划分子域并非一劳永逸的方式,多个子域意味着更多的 DNS 查询时间。一般划分为 3 到 5 个比较合适。
对如何拆分资源有以下建议:webpack
DNS 解析也是须要时间的,能够经过预解析的方式来预先得到域名所对应的 IP,方法是在 head 标签里写上几个 link 标签ios
<link rel="dns-prefetch" href="https://www.google.com"> <link rel="dns-prefetch" href="https://www.google-analytics.com">
对以上几个网站提早解析,这个过程是并行的,不会阻塞页面渲染。git
在开发中,可能会遇到这样的状况。有些资源不须要立刻用到,可是但愿尽早获取,这时候就可使用预加载。
预加载实际上是声明式的 fetch,强制浏览器请求资源,而且不会阻塞 onload 事件,可使用如下代码开启预加载:github
<link rel="preload" href="http://example.com">
预加载能够必定程度上下降首屏的加载时间,由于能够将一些不影响首屏但重要的文件延后加载,惟一缺点就是兼容性很差。web
HTTP 是一个无状态的面向链接的协议,即每一个 HTTP 请求都是独立的。然而无状态并不表明 HTTP 不能保持 TCP 链接,Keep-Alive 正是 HTTP 协议中保持 TCP 链接很是重要的一个属性。 HTTP1.1 协议中,Keep-Alive 默认打开,使得通讯双方在完成一次通讯后仍然保持必定时长的链接,所以浏览器能够在一个单独的链接上进行多个请求,有效地下降创建 TCP 请求所消耗的时间。
使用 CND 加速能够减小客户端到服务器的网络距离。
还有一种比较流行的作法是让一些项目依赖走 CDN,好比 vuex、vue-router 这些插件经过外链的形式来引入,由于它们都有本身免费的 CDN,这样能够减小打包后的文件体积。
缓存对于前端性能优化来讲是个很重要的点,良好的缓存策略能够下降资源的重复加载提升网页的总体加载速度。
一般浏览器缓存策略分为两种:强缓存 和 协商缓存。
Expires
和Cache-Control
强缓存表示在缓存期间不须要向服务器发送请求Last-Modified/If-Modified-Since
和ETag/If-None-Match
实现HTTP 头中与缓存相关的属性,主要有如下几个:
(1) Expires: 指定缓存过时的时间,是一个绝对时间,但受客户端和服务端时钟和时区差别的影响,是 HTTP/1.0 的产物
形如Expires: Wed, 22 Oct 2018 08:41:00 GMT
(2) Cache-Control:比 Expires 策略更详细,max-age 优先级比 Expires 高,其值能够是如下五种状况
(3) Last-Modified / If-Modified-Since: Last-Modified
表示本地文件最后修改日期,If-Modified-Since
会将上次从服务器获取的Last-Modified
的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。
可是若是(服务器)在本地打开缓存文件(或者删了个字符 a 后又填上去),就会形成Last-Modified
被修改,因此在 HTTP / 1.1 出现了ETag
。
(4) Etag / If-None-Match: ETag
相似于文件指纹,If-None-Match
会将当前ETag
发送给服务器,询问该资源ETag
是否变更,有变更的话就将新的资源发送回来。而且ETag
优先级比Last-Modified
高。
因为 etag 要使用少数的字符表示一个不定大小的文件(如 etag: "58c4e2a1-f7"),因此 etag 是有重合的风险的,若是网站的信息特别重要,连很小的几率如百万分之一都不容许,那么就不要使用 etag 了。使用 etag 的代价是增长了服务器的计算负担,特别是当文件比较大时。
选择合适的缓存策略
对于大部分的场景均可以使用强缓存配合协商缓存解决,可是在一些特殊的地方可能须要选择特殊的缓存策略
Cache-control: no-store
,表示该资源不须要缓存Cache-Control: no-cache
并配合ETag
使用,表示该资源已被缓存,可是每次都会发送请求询问资源是否更新。Cache-Control: max-age=31536000
并配合策略缓存使用,而后对文件进行指纹处理,一旦文件名变更就会马上下载新的文件。由于浏览器会有并发请求限制,在 HTTP / 1.1 时代,每一个请求都须要创建和断开,消耗了好几个 RTT 时间,而且因为 TCP 慢启动的缘由,加载体积大的文件会须要更多的时间。
在 HTTP / 2.0 中引入了多路复用,可以让多个请求使用同一个 TCP 连接,极大的加快了网页的加载速度。而且还支持 Header 压缩,进一步的减小了请求的数据大小。
这又涉及到不少知识点了,简单来讲,咱们要尽量地在保证咱们的 App 能正常运行、图片尽量保证高质量的前提下去压缩全部用到的文件的体积。好比图片格式的选择、去掉咱们代码中的注释、空行、无关代码等。
图片相关优化
构建工具的使用
压缩 HTML 文件
能够把 HTML 的注释去掉,把行前缩进删掉,这样处理的文件能够明显减小 HTML 的体积;这样作几乎是没有风险的,除了 pre 标签不可以去掉行首缩进以外,其余的都正常。
<script>
标签位置渲染线程和 JS 引擎线程是互斥的,若是你想首屏渲染的越快,就越不该该在首屏就加载 JS 文件,所以建议将 script 标签放在 body 标签底部的缘由。或者使用给 script 标签添加 defer 或者 async 属性 。
执行 JS 代码过长会卡住渲染,对于须要不少时间计算的代码能够考虑使用 Webworker。Webworker 可让咱们另开一个线程执行脚本(这并无改变 JS 单线程的本质,由于新开的线程受控于主线程且不得操做 DOM)而不影响渲染。
懒加载就是将不关键的资源延后加载。
懒加载的原理就是只加载自定义区域(一般是可视区域,但也能够是即将进入可视区域)内须要加载的东西。对于图片来讲,先设置图片标签的src
属性为一张占位图,将真实的图片资源放入一个自定义属性中,当进入自定义区域时,就将自定义属性替换为src
属性,这样图片就会去下载资源,实现了图片懒加载。
懒加载不只能够用于图片,也可使用在别的资源上。好比进入可视区域才开始播放视频等等。
使用场景好比抽奖动画展现过程当中预先加载其余内容,或者电子书阅读章节的预加载可使切换下一章节时更为流畅。
执行 JavaScript 的解析和 UI 渲染的两个浏览器线程是互斥的,UI 渲染时 JS 代码解析终止,反之亦然。
当 页面布局和几何属性 改变时,就会触发 回流。
当 须要更新的只是元素的某些外观 时,就会触发 重绘。
使用 CSS 预处理器时注意不要有过多的嵌套,嵌套层次过深会影响浏览器查找选择器的速度,且必定程度上会产生出不少冗余的字节。
减小 DOM 元素数量,合理利用 :after、:before 等伪类,避免页面过深的层级嵌套;
优化 JavaScript 性能,减小 DOM 操做次数(或集中操做),能有效规避页面重绘/重排;
只能说尽量去作优化,如数据分页、首屏直出、按需加载等
为触发频率较高的函数使用函数节流
SPA:单页面富应用
动态地重写页面的部分与用户交互而不是加载新的页面。
优势:① 先后端分离 ② 页面之间切换快 ③ 后端只需提供 API
缺点:① 首屏速度慢,由于用户首次加载 SPA 框架及应用程序的代码而后才渲染页面 ② 不利于 SEO
SEO(Search Engine Optimization):搜索引擎优化
经常使用技术:利用 <title> 标签和 <meta> 标签的 description
<html> <head> <title>标题内容</title> <meta name="description" content="描述内容"> <meta name="keyword" content="关键字1,关键字2,—"> </head> </html>
SPA 应用中,一般经过 AJAX 获取数据,而这里就难以保证咱们的页面能被搜索引擎正常收录到。而且有一些搜索引擎不支持执行 JS 和经过 AJAX 获取数据,那就更不用提 SEO 了。
对于有些网站而言,SEO 显得相当重要,例如主要之内容输出为主的 Quora、stackoverflow、知乎和豆瓣等等,那如何才能正常使用 SPA 而又不影响 SEO 呢 ?因此有了 SSR
SSR(Server-Side Rendering):服务端渲染
如下内容部分参考《深刻浅出 React 与 Redux》- 程墨
为了量化网页性能,咱们定义两个指标:
在一个 彻底靠浏览器端渲染 的应用中,当用户在浏览器中打开一个页面的时候,最坏状况下没有任何缓存,须要等待三个 HTTP 请求才能到达 TTFP 的时间点:
而对于服务器端渲染,由于获取 HTTP 请求以后就会返回全部有内容的 HTML,因此在一个 HTTP 的周期以后就会提供给浏览器有意义的内容,因此首次渲染时间 TTFP 会优于彻底依赖于浏览器端渲染的页面。
除了更短的 TTFP,服务器端渲染还有一个好处就是利于搜索引擎优化,虽然某些搜索引擎已经可以索引浏览器端渲染的网页,可是毕竟不是全部搜索引擎都能作到这一点,让搜索引擎可以索引到应用页面的最直接方法就是提供完整 HTML
上面的性能对比只是理论上的分析,实际上,采用服务器端渲染是否能得到更好的 TTFP 有多方面因素。
一、服务器端产生的 HTML 过大是否会影响性能?
由于服务器端渲染返回的是完整的 HTML,那么下载这个 HTML 的时间也会增加。
二、服务器端渲染的运算消耗是不是服务器可以承担得起的?
浏览器端渲染的方案下,服务器只提供静态资源,压力被分摊到了访问用户的浏览器中;若是使用服务器端渲染,每一个页面请求都要产生 HTML 页面,这样服务器的压力就会很大。
React 并非给服务器端渲染设计的,若是应用对 TTFP 要求不高,也不但愿对 React 页面进行搜索引擎优化,那么没有必要使用“同构”来增长开发难度;若是但愿应用的性能能更进一步,并且服务器运算资源充足,那么能够尝试。对 Vue 而言应该也是一样的道理。
最后咱们来总结下服务端渲染理论上的优缺点:
优势:
缺点:
一、第三方库走 cdn
例如:
<script src="//cdn.bootcss.com/vue/2.2.5/vue.min.js"></script> <script src="//cdn.bootcss.com/vue-router/2.3.0/vue-router.min.js"></script> <script src="//cdn.bootcss.com/vuex/2.2.1/vuex.min.js"></script> <script src="//cdn.bootcss.com/axios/0.15.3/axios.min.js"></script>
在 webpack 里有个 externals 选项,能够忽略不须要打包的库
https://webpack.js.org/configuration/externals/#root
const path = require('path') module.exports = { mode: 'production', entry: './src/index.js', externals: { 'vue': 'Vue', 'vue-router': 'VueRouter', 'vuex': 'Vuex', 'axios': 'axios' }, output: { ... } }
二、路由懒加载
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export default new Router({ routes: [ { path: '/ebook', component: () => import('./views/ebook/index.vue'), // 路由懒加载,这里用的是ES6的语法 import()函数是动态加载 import 是静态加载 children: [ // 动态路由, 能够传递路径参数 { path: ':fileName', component: () => import('./components/ebook/EbookReader.vue') } ] }, { path: '/store', component: () => import('./views/store/index.vue'), redirect: '/store/shelf', // #/store -> #/store/home ... } ] })
三、使用懒加载插件 Vue-Loader
具体的使用能够参考 这篇文章 或者去看官方文档
step1:cnpm install vue-lazyload --save
step2:main.js导入
import VueLazyLoad from 'vue-lazyload' Vue.use(VueLazyload)
step3:<img class="item-pic" v-lazy="newItem.picUrl"/>
vue 文件中将须要懒加载的图片绑定v-bind:src
修改成v-lazy
这只是图片懒加载,还有不少其余可选配置
四、v-if
与v-show
的选择
通常来讲,v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。所以,若是须要很是频繁地切换,则使用v-show
较好;若是在运行时条件不多改变,则使用v-if
较好。
一、单个组件的优化:更改 shouldComponentUpdate 函数的默认实现,根据每一个 React 组件的内在逻辑定制其行为,减小没必要要的从新渲染
shouldComponentUpdate(nextProps, nextState) { // 假设影响渲染内容的 prop 只有 completed 和 text,只须要确保 // 这两个 prop 没有变化,函数就能够返回 false return (nextProps.completed !== this.props.completed) || (nextProps.text !== this.props.text) }
二、使用 immutable.js 解决复杂数据 diff、clone 等问题。
immutable.js 实现原理:持久化数据结构,也就是使用旧数据建立新数据时,要保证旧数据同时可用且不变。同时为了不 deepCopy 把全部节点都复制一遍带来的性能损耗,Immutable 使用告终构共享,即若是对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
三、在 constructor() 里作 this 绑定
当在 render() 里使用事件处理方法时,提早在构造函数里把 this 绑定上去(若是须要的话),由于在每次 render 过程当中, 再调用 bind 都会新建一个新的函数,浪费资源.
// bad class App extends React.Component { onClickDiv() { // do stuff } render() { return <div onClick={this.onClickDiv.bind(this)} />; } } // good class App extends React.Component { constructor(props) { super(props); this.onClickDiv = this.onClickDiv.bind(this); } onClickDiv() { // do stuff } render() { return <div onClick={this.onClickDiv} />; } }
四、基于路由的代码分割
使用React.lazy
和React Router
来配置基于路由的代码分割
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import React, { Suspense, lazy } from 'react'; const Home = lazy(() => import('./routes/Home')); const About = lazy(() => import('./routes/About')); const App = () => ( <Router> <Suspense fallback={<div>Loading...</div>}> <Switch> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> </Switch> </Suspense> </Router> );
http://www.javashuo.com/article/p-ueqxqjhb-dz.html
http://www.javashuo.com/article/p-hiiezftc-do.html
http://www.javashuo.com/article/p-maytbjvw-kv.html
https://www.jianshu.com/p/333f390f2e84
https://yuchengkai.cn/docs/frontend/