《Web前端性能优化》学习总结
第1章 用户体验
1.用户体验(UE/UX)的阶梯图包括产品的可用性、易用性、好用性和品牌价值。
2.页面的一秒钟延迟会造成页面浏览量PV下降。等待8s后,用户将失去耐心。
3.FE是前端的简写。
第2章 前端性能瓶颈
1.传统的DOM操作方式对性能的影响很大,其原因是频繁对DOM结构操作会引起页面的重排(Reflow)和重绘(Repaint),浏览器不得不频繁地计算布局,重新排列与绘制页面元素,导致浏览器产生巨大的性能开销。
2.浏览器的渲染过程为:分别解析HTML文档的HTML、CSS和JS,解析HTML生成DOM,解析CSS生成CSSOM,根据生成的DOM和CSSOM构建Render Tree。根据渲染树,计算布局排列,绘制页面。
3.重排一定会引起重绘。
4.为了解决频繁重排和重绘的问题,MVVM框架出现了。它通过操作虚拟DOM,不产生额外的重排和重绘,并实现了双向的数据绑定。
5.实现双向绑定的方式:Angular使用的是脏值检测,Vue使用的是数据劫持。
6.Vue通过给data加工,给里面的key通过数据劫持的方式增加监听器,当数据变动时,再触发更新。通过compile函数实现指令和双花括号所在节点的绑定和更新,其中使用类数组Fragment实现VDOM。
7.微软放弃了IE和Edge。我们可以使用Can I Use网站查询兼容性。
8.SPA没有页面间的跳转,而是通过Ajax、Axios、Fetch等无刷新异步加载数据模式,或者通过锚点跳转对应路由,使用户体验上升了一大截,但首屏渲染时间长,且不利于SEO。
9.多图网站可采用懒加载、分页、内容分发、静态资源分多域名存储等方式提高网站的性能。
10.由于JS会阻塞浏览器的渲染,所以一般会放在DOM树后,等待DOM结构加载完才开始执行。
11.使用专门的图片服务器存储图片,数据库表存储url,再加上适当的缓存和懒加载,会大大减少服务器的压力,提高页面性能。
12.Chrome浏览器是6条请求并发进行,某一条请求完成后补充另外的请求进来。通过多域名访问,即静态资源与服务分离,分多域名存储,就可以解决浏览器的并发瓶颈。
13.JS不仅能做前端,还能做服务端,甚至做系统,WebGL和Three.js也使JS具备游戏、地图等大型3D应用的能力,JS还可以进行大数据分析、图像采集、特征提取。
第3章 前端的分层
1.<base>为页面上的所有链接规定默认地址或默认目标。
2.HTML5新增了<details>、<summary>、<dialog>等语义化标签,新增了input的标签类型,新增了<datalist>、<output>等表单元素,新增了多媒体标签和Canvas。
3.HTML5新增了强大的选择器,新增了localStorage、sessionStorage、WebSocket、querySelector、querySelectorAll。
4.使用CSS分为外链式、内联式和嵌入式。
5.less包含变量、混合、参数、嵌套、函数等功能。sass会比less复杂一些,具有强大的库Compass。Compass中有很多封装好的Mixin,如合成精灵图,使我们很方便地写出兼容的样式。
6.通过使用css的预处理器,我们可以实现样式的复用、多层嵌套简化,实现样式的兼容和循环样式。
7.JS分为ECMAScript、DOM、BOM三部分。
8.ES5时代,作用域只有全局作用域和局部作用域,window是全局,function是局部。我们使用let和const实现块级作用域。
9.ES6包括块级作用域、解构赋值、箭头函数、新的数据结构Set和Map、各类简写、Promise、模块化编程、模板字符串、Symbol数据类型、String的repeat方法、for...of、Object.keys()方法、forEach()、map()、filter()、reduce()、some()、every()。
10.使用箭头函数后,当函数在全局环境下执行时,内部的this指向的不再是window,而是undefined。
11.ES7包括async/await、arr.includes()、**的方法,ES8包括padStart()、padEnd()、Object.values、Object.entries等方法。
12.reduce方法可以求和、求最大值、实现数组去重。
第4章 HTML层级优化
1.减少HTML的层级嵌套,使用MVVM框架时也要注意,组件的嵌套不宜过深,最好不要超过3层。
2.减少空标签、无用标签的滥用,可使用:before和:after实现三角形效果。
3.减少内联样式,改为嵌入式或是外链引用样式,从而实现样式复用。
4.合理使用自定义属性,不宜过多,合理利用模板引擎。
5.可使用link标签的prefetch在浏览器空闲的时候预先下载将来页面的资源,使用dns-prefetch进行域名的预先转换,使用preload预先下载当前页面将要用到的JS文件。
6.可以通过将<img>设定一个固定的大小,避免页面小图换成大图时页面的重排和重绘。设置img标签的alt属性有利于SEO,爬虫会抓取alt的值,并依据该值把图片加到百度图库中,从而增加网站流量入口。
7.当src属性和href属性值为空时,一些浏览器可能把当前页面当成属性值加载,从而影响页面的性能。
8.iframe对网站性能影响大,具有隔绝的特性,目前的爬虫机器人不能从iframe中拿到数据。
第5章 CSS层级优化
1.样式多复用,少写重复代码。尽量避免同一类名多次渲染,可以给元素设置两个类,一个是基础类,另一个是重写了部分样式的类。
2.少用高优先级选择器,慎用!important。实现样式层和行为层的类名分离,如<div class='color-green helloBoy'>你好啊</div>。
3.使用伪选择器解决问题,避免写出过深层级的CSS选择器,不用曾经的CSS表达式。
4.减少昂贵的样式的使用,如box-shadow、gradients、filter、opacity、border-radius。
5.减少浏览器的重排和重绘,可以使用Chrome DevTools的Performance面板查看一个刷新操作会发生的动作,避免float滥用。
6.使用CSS Sprites,配合background-position属性可以减少对小图标的请求。
7.充分利用强大的CSS3,可以实现动画效果,减少JS的使用,从而优化页面的性能。CSS中有很多利用到GPU加速渲染的属性。
8.相较于window.resize,使用媒体查询实现页面自适应更好。
第6章 JavaScript层级优化
1.JS的运行机制是单线程+异步队列。
2.使用定时器要记得及时清除定时器。
3.合理使用CSS3动画,在使用该动画效果时添加类名即可,也可以通过requestAnimationFrame(animateUnit)来实现动画,提高用户体验。
4.使用for循环绑定事件会导致异步监听器过多,我们可以采用事件委托的方式监听,即委托给点击的父元素,可以解决分页导致元素未被绑定的问题。
5.JQ使用on函数来实现事件委托。
6.我们应避免重复的事件监听,即将原来的回调方法分离出来,当需要补充替换的时候,使用removeEventListener移除回调方法,JQ则使用off函数移除原有的事件即可。
7.事件的传播机制分为从外向里的事件捕获和从里向外的事件冒泡,很多浏览器都将事件冒泡作为默认的传播机制。
8.一般某个特定事件的传播机制只会是事件捕获和事件冒泡的其中一种。
9.为了防止JS在DOM树未创建前就加载执行,从而获取不到DOM节点,或出现事件绑定不到节点的问题,可以采用window.onload方法,等页面完全渲染完,再加载执行JS。
10.使用window.onload执行JS有时会过慢,使得页面处于假死状态,因此我们可以使用JQ的ready方法,该方法在DOM树构建完毕后执行,提升了页面的性能。
11.两个window.onload,后面会覆盖前面的。
12.Vue组件生命周期包括beforeCreate、created(可以使用methods和data)、beforeMount、mounted(可以操作DOM)、beforeUpdate、updated(更新后的DOM)、beforeDestroy、destroyed。
13.使用变量缓存,可以提升代码性能。用容器包裹全局变量,实现变量私有化,可以减轻window的负担,且避免全局变量造成变量污染。
第7章 资源加载优化
1.DNS解析之前都会先从浏览器的DNS缓存中拿数据,若无则继续去本地DNS缓存查找,若无则正式发起DNS解析过程。
2.使用dns-prefetch可以进行DNS的显式预解析,现在的浏览器很智能,会对a标签的域名进行隐式的预解析。
3.一般会将网站的动态资源和静态资源分站点存储,CDN只加速静态资源站点。
4.HTTP缓存是浏览器缓存的一种。如果命中强缓存,则返回200。如果命中协商缓存,则返回304,从缓存中读取数据。
5.强缓存使用Cache-Control和Expires来控制,分别表示相对时间和绝对时间,Cache-Control的优先级大于Expires。
6.Cache-Control的字段no-cache是指使用缓存需要服务器确认,no-store则是禁止使用缓存。
7.浏览器请求时,若有缓存,则判断缓存是否过期,若没有过期则读取强缓存,过期则判断ETag是否等于If-None-Match,再判断If-Modified-Since是否大于等于Last-Modified,是则读取协商缓存,否则向服务器请求数据。
8.懒加载又叫按需加载,可用jquery-lazyload.js实现。
9.console.dir()可以显示一个对象所有的属性和方法。
10.分页加载和Ajax加载都是优化性能的加载方式。Ajax通过原生的XMLHttpRequest对象发出HTTP请求,ES6+是通过Promise、fetch和async/await来实现Ajax。
11.Promise、fetch和async/await,还有Axios、Request等上层封装,思想与传统的Ajax一致,区别只在于代码的直观性和可维护性。
12.使用sass或less编写css、使用Koala压缩css和js文件,可以提升资源加载的速度。js在压缩时,配合一点混淆技巧,可以使得资源更加安全。
13.使用图片转换成base64,可以减少请求数。在上传文件时,一般都会将图片转换成base64字符串,传到后台后再重新转成文件。
14.使用大中小图片方案,进行图片压缩,屏蔽开发时的调试、日志代码,可以提升页面的性能。
15.用console打印日志,可以使用log、info、error、warn、table、trace、time、timeEnd等函数。
第8章 其他层级优化
1.为了优化性能,应不做重复性的加载,如少用iframe、权衡好公共样式和私有样式。
2.精简Cookie、合理利用sessionStorage和localStorage、减少同后端的交互请求数、使用中间件进行负载均衡,可以提升网站的性能。
3.浏览器的请求限制是6条同源的请求,请求可以是小图片。虽然请求并发限制数可以在浏览器更改,但是会导致浏览器不稳定。
4.减少请求的方法:通过CSS Sprite、调整数据结构实现请求的合并,使用Cookie、sessionStorage、localStorage、IndexedDB即浏览器数据库这些前端缓存,使用打包工具打包合并JS文件、使用WebSocket代替轮询。
5.需要有前端展示、后端处理的思维,JSON返回数据层级不宜过深。
6.同步进行请求所花时间远比异步时间久。
7.使用Promise可以使回调更加直观,async/await是基于Promise的一种改良,配合try...catch可以让开发者更容易分析抛出错误,像写同步代码一样写异步代码。
8.我们提升网站性能,可以充分利用硬件GPU加速。GPU即显卡,CSS3的变换属性会触发GPU加速,可以做出更加顺畅优美的动画,也不会对JS性能产生过大的影响。
9.耗费资源的程度:JS操作DOM>复合层渲染>普通DOM的渲染。
第9章 前端调试
1.360浏览器是IE+Chrome双内核。
2.Chrome浏览器具有网页打印功能,我们可以通过代码写一个网页,再通过Chrome转成PDF。
3.Chrome的常用插件有AdBlock、Vue-Devtool、React Developer Tools等。
4.Chome有Elements、Console、Sources、Performance、Network、Application、Security、Audits(可以检测到引入但未被使用的css)面板。
第10章 常见的自动化构建工具
1.前端的自动化构建工具包括Grunt、Gulp和Webpack。它们都是需要写一个配置文件,再通过配置文件自动完成任务。
2.Grunt和Gulp的缺点是缺乏JS模块化编程后的模块打包功能,需要配合require.js或是sea.js才能完成。
3.Grunt的配置文件是Gruntfile.js,使用Grunt需要全局安装grunt-cli工具,然后安装grunt到开发依赖中。
4.install可以用i代替,--save-dev可以用-D代替。
5.使用Grunt压缩代码,需要安装uglify插件到开发依赖中,再配置好配置文件的Task,执行grunt命令即可。
6.Gulp是基于Stream的构建工具,是Grunt的进化版,其配置文件是gulpfile.js,使用gulp需要安装gulp到开发依赖中。
7.gulp使用pipe()函数连接流程,若创建了一个名为scripts的task,则执行gulp scripts即可。
8.Webpack的配置文件是webpack.config.js,使用webpack需要将webpack和webpack-cli安装到开发依赖中。
9.Webpack配置文件中mode是用于查看输出的文件是否需要压缩,开发环境不做压缩,生产环境会做压缩。
10.要将打包后的JS文件引入HTML页面中,需要引入html-webpack-plugin插件,作为开发依赖安装。
11.Webpack配置文件的参数configureWebpack可以合并vue.config.js里的设置到webpack中去,chainWebpack可以链式访问特定loader,可以在vue.config.js文件中配置vue-loader等。
第11章 新技术对性能的提升
1.在即时通信领域,传统的长短连接轮询会降低效率,短连接会造成大量的空连接、无效连接,长连接则会使通信一直处于连接的转态,占用服务器资源,我们可以使用新技术Socket来解决问题。
2.订阅-发布模式,又叫观察者模式,目前常见的消息队列中间件、前端的事件监听,都是采用该模式。
3.WebSocket协议的实现实际上只是Socket.io的一个子集,是HTML5新增加的一种通信协议,是一种双向通信协议。
4.Socket.io可应用到即时通信(微信、QQ)、股市基金实时信息、警报告警、公众号订阅与推送等场景。
5.MVVM框架提高了开发效率和可维护性,提供了一个还过得去的性能,它减少了频繁操作DOM导致页面多次重排重绘的情况,它是由框架本身操作DOM。
6.虚拟DOM实际上就是将真实DOM中需要的元素挂靠在一个实例化的JS对象中,我们将多次的DOM操作先在该对象处理,再一次性的attach到DOM树上,可以避免因浏览器重排而导致的大量无用计算。
7.模板引擎之所以可以提高性能,是因为它将原本多次对页面进行的DOM操作暂时性的放入一个模板中,等处理完了再一次性加载。
8.虚拟DOM实际上是为Diff算法往真实DOM上打补丁(patch)服务的。
9.Diff算法的第一步,先做最外层的比对,判断新旧两个节点是否值得比较,如果不值得,就将旧节点整个替换。当新旧节点值得继续深入比对时,就继续递归查找。通过发现不值得深入比对的节点,可以提升页面实时更新的性能。
10.updateChildren函数通过sameVnode函数判断新旧节点的异同,通过patchVnode函数给真实的节点打补丁。
11.Diff算法是现代MVVM框架核心中的核心。现在Vue还没有在Diff算法中增加类型校验,只为提升它的性能。期待Vue可以进行优化,并支持TypeScript编程。
12.v-if会删除DOM节点,不适合频繁切换显示隐藏,但是首屏加载时间会降低;v-show是给DOM切换display值,适合频繁的切换显示隐藏,但是会增加首屏加载时间。
13.组件不宜拆分的过大和过小。父组件传值给子组件通过props进行传值,子组件传值给父组件则调用$emit方法,两者都需要在调用子组件时增加传参。
14.为了解决组件多级传值问题,我们可以向Vue实例的prototype注入一个变量store,组件中使用this.$store进行访问,这便是Vuex数据中心的使用。
15.JS应用到移动端,出现了小图占位的骨架屏(Skeleton),先将页面的骨架加载出来,可以提升用户体验。
16.JS应用到移动端,PWA(渐进式增强Web Apps)应运而生。Web通过Notification API和Service Worker解决了PWA推送和离线缓存的问题。相较于普通的Web应用而言,PWA最大的特点就是离线缓存,底层是微信App本身。
第12章 思想高于逻辑,逻辑强于代码
1.编写代码时,需要考虑代码的复用性、健壮性、业务拓展性。
2.首次架构重于迭代升级。好的架构的特点被总结为OOP设计模式的基本原则:单一职责、开放封闭、多级缓存、规范约束(BEM等)、权限管理。
3.良好的编码习惯有:用三目运算符代替if/else,符号位左右留空,常量在前表达式在后避免空指针、函数中功能需拆分、语义化命名,较少全局变量,编写高复用性代码。
4.重构可以提升系统的可维护性、简化UI、引入新技术新架构。重构一般都会让产品经理牵头,由比较有经验的开发人员负责。
5.要权衡好开发效率和性能,在有限的资源条件下,让产品做得更好。
第13章 性能优化案例分析
1.百度的页面优化方式为:分多域名取资源、使用CDN加速、使用大中小图片处理方案、利用localStorage缓存实现搜索历史、使用缓存(包括memory cache和disk cache)。
2.淘宝的页面优化方式为:通过base64减少请求数、使用CDN加速、分多域名取资源、图片懒加载、图片用专门的服务器存储、使用PWA实现离线缓存、变量缓存、分区域异步Ajax无刷加载。
3.新浪的页面优化方式为:CSS Sprite、长连接实现数据更新(Connection:Keep-Alive保证实时性,Cache-Control:no-cache保证准确性)、ws实现推送。
4.新浪由于是门户网站的性质,在技术上通常不会特别新,但兼容性会较好。它使用JSONP(JSON with Padding)实现跨域。