前端知识体系5.性能优化

本文目录: 1.前端性能优化思路 2.如何理解回流和重绘? 3.CDN 是什么? 4.Webpack性能优化 优化开发体验 ①缩小文件搜索范围 ②DllPlugin可以将特定的类库提前打包然后引入 ③HappyPack 优化输出质量 ①按需加载路由 ②使用Tree Shaking,删除项目中未被引用的代码 ③开启Scope Hoisting ④区分环境--减小生产环境代码体积 ⑤使用webpack内置的UglifyJS插件,压缩JS代码 ⑥使用CDN加速静态资源加载 ⑦多页面应用提取页面间公共代码,以利用缓存 ⑧分割代码以按需加载 ⑨使用Prepack提前求值 ⑩使用Scope Hoisting 1.前端性能优化思路 建立完善的开发规范,提高代码的渲染效率及可维护性。 压缩代码,合并代码,减少文件体积 减少图片等静态资源的体积,并使用雪碧图,图片懒加载等技术 可以使用url-loader把小图片转换成base64嵌入到JS或CSS中,减少加载次数 使用多域名负载网页内的多个文件、图片 注意阻塞,将CSS样式定义放置在文件头部,JS脚本放在文件末尾 尽量减少页面中重复的HTTP请求数量 服务器开启gzip压缩功能,对用户请求的页面进行压缩处理 上面的这些回答太过笼统,并且大多数的前端都会这样说,其实这个问题的拓展性非常强,特别能体现出对前端领域的掌握深度,网络层面?浏览器渲染层面?css、js执行层面?框架层面?详细说,越详细越好 css:尽量使用id和class选择器关联所有样式,尽量不使用行内样式和属性选择器,避免选择器层级嵌套太深,深入理解样式继承(font、color等文字类的样式可继承)。 2.如何理解回流和重绘? 回流: 当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。 重绘: 当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。由此我们可以看出,重绘不一定导致回流,回流一定会导致重绘。 避免方法 1.减少使用行内样式,使用clas关联样式 2.对于需要经常隐藏和显示的内容在页面布局写入后使用display:none;属性进行操作,这样不会引发页面的回流重绘,尽量减少DOM操作。 3.避免频繁读取会引发回流重绘的元素,如果需要最好是缓存起来 4.对复杂动画元素使用绝对定位,使它脱离文档流 5.减少table布局以及css表达式(如calc())的使用。 3.CDN 是什么? CDN,中文名叫做「内容分发网络」,它的作用是减少传播时延,找最近的节点,实际上,尽管互联网帮助我们实现了地球村,但是从中国到日本和从中国到中国台湾的时延仍旧是不一样的, CDN 的优点 1.访问加速 2.减轻源站(服务器)负载 一个非常简单就能想明白的问题,如果 CDN 已经能帮我返回数据了,那么请求就不会到达源站,源站(服务器)的负载就减轻了。 3.抗住攻击 既然源站的负载被减轻了,那么在受到 DDOS 攻击的时候,也能谈笑风生。 **CDN 的缺点? 1.首先可控性差,比如某个依赖节点挂了,导致项目无法正常运行,会直接导致用户的损失,尤其是体量大的公司依赖 CDN 进行静态资源管理的时候,发生这样的事情后果会非常严重。 2.其次,便宜没好货:本来在只有网宿而没有阿里云的时代,CDN 是很昂贵的,阿里腾讯在拉低 CDN 价格的同时,也拉低了 CDN 的质量,部分节点的访问质量不太高会导致有些用户访问的网络质量非常差。 然后,一个微小的科普:什么是混合 CDN——混合 CDN 这个名词看着很高端,实际上就是,我们用了多家厂商的 CDN,可能也包括自己建的,然后谁好的选谁,但是有的时候反而会造成服务不可控,进一步劣化 CDN 的质量。 CDN 这个东西本质就是一个缓存,只是这个缓存离你特别特别的近,作为用户还是开发都能从中享受到一点福利,但作为一个服务于企业的开发人员,不仅要考虑 CDN 的优点,也要知道 CDN 给我们带来的坑,这样才能成为靠谱的 CDN 使用者。 4.Webpack性能优化 优化可以从两个方面考虑,一个是优化开发体验,一个是优化输出质量。 优化开发体验 ①缩小文件搜索范围 resolve字段告诉webpack怎么去搜索文件,所以首先要重视resolve字段的配置: 由于loader对文件转换操作很耗时,应该尽量减少loader处理的文件,可以使用include命中需要处理的文件,缩小命中范围。 ②DllPlugin可以将特定的类库提前打包然后引入 这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案 ③HappyPack 因为Node是单线程运行的,所以Webpack在打包的过程中也是单线程的,特别是在执行Loader的时候,这样会导致等待的情况,HappyPack可以将Loader的同步执行转换为并行的 优化输出质量 优化输出质量最大的好处就是可以减少首屏的加载时间 ①按需加载路由 如果我们把十几个页面甚至更多的路由页面,把这些页面全部打包进一个JS文件的话,虽然将多个请求合并了,但是同样也加载了很多并不需要的代码,耗费了更长的时间。那么为了首页能更快地呈现给客户,这时候我们就可以使用按需加载,将每个路由页面单独打包为一个文件 ②使用Tree Shaking,删除项目中未被引用的代码 ③开启Scope Hoisting Scope Hoisting会分析出模块之间的依赖关系,尽可能的把打包出来的模块合并到一个函数中,让Webpack打包出来的代码更小、运行更快 ④区分环境--减小生产环境代码体积 代码运行环境分为开发环境和生产环境,代码需要根据不同环境做不同的操作,许多第三方库中也有大量的根据开发环境判断的if else代码,构建也需要根据不同环境输出不同的代码,所以需要一套机制可以在源码中区分环境,区分环境之后可以使输出的生产环境的代码体积减小。Webpack中使用DefinePlugin插件来定义配置文件适用的环境。 const DefinePlugin = require('webpack/lib/DefinePlugin'); //... plugins:[ new DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production') } }) ] 注意,JSON.stringify('production') 的原因是,环境变量值需要一个双引号包裹的字符串,而stringify后的值是'"production"' 然后就可以在源码中使用定义的环境: if(process.env.NODE_ENV === 'production'){ console.log('你在生产环境') doSth(); }else{ console.log('你在开发环境') doSthElse(); } ⑤使用webpack内置的UglifyJS插件,压缩JS代码 ParallelUglifyPlugin会分析JS代码语法树,理解代码的含义,从而做到去掉无效代码、去掉日志输入代码、缩短变量名等优化。常用配置参数如下: const UglifyJSPlugin = require('webpack/lib/optimize/UglifyJsPlugin'); //... plugins: [ new UglifyJSPlugin({ compress: { warnings: false, //删除无用代码时不输出警告 drop_console: true, //删除所有console语句,可以兼容IE collapse_vars: true, //内嵌已定义但只使用一次的变量 reduce_vars: true, //提取使用多次但没定义的静态值到变量 }, output: { beautify: false, //最紧凑的输出,不保留空格和制表符 comments: false, //删除所有注释 } }) ] 伴随着越来越多的浏览器支持ES6,出现了很多第三方插件,可以直接压缩ES6代码,在使用的同时注意防止babel-loader转换ES6代码,要在.babelrc中去掉babel-preset-env,因为正是babel-preset-env负责把ES6转换为ES5。 ⑥使用CDN加速静态资源加载 CDN通过将资源部署到世界各地,使得用户可以就近访问资源,加快访问速度。要接入CDN,需要把网页的静态资源上传到CDN服务上,在访问这些资源时,使用CDN服务提供的URL。 由于CDN会为资源开启长时间的缓存,例如用户从CDN上获取了index.html,即使之后替换了CDN上的index.html,用户那边仍会在使用之前的版本直到缓存时间过期。业界做法: HTML文件:放在自己的服务器上且关闭缓存,不接入CDN 静态的JS、CSS、图片等资源:开启CDN和缓存,同时文件名带上由内容计算出的Hash值,这样只要内容变化hash就会变化,文件名就会变化,就会被重新下载而不论缓存时间多长。 另外,HTTP1.x版本的协议下,浏览器会对于向同一域名并行发起的请求数限制在4~8个。那么把所有静态资源放在同一域名下的CDN服务上就会遇到这种限制,所以可以把他们分散放在不同的CDN服务上,例如JS文件放在js.cdn.com下,将CSS文件放在css.cdn.com下等。这样又会带来一个新的问题:增加了域名解析时间,这个可以通过dns-prefetch来解决 来缩减域名解析的时间。形如//xx.com 这样的URL省略了协议,这样做的好处是,浏览器在访问资源时会自动根据当前URL采用的模式来决定使用HTTP还是HTTPS协议。 总之,构建需要满足以下几点: 静态资源导入的URL要变成指向CDN服务的绝对路径的URL 静态资源的文件名需要带上根据内容计算出的Hash值 不同类型资源放在不同域名的CDN上 ⑦多页面应用提取页面间公共代码,以利用缓存 大型网站通常由多个页面组成,每个页面都是一个独立的单页应用,多个页面间肯定会依赖同样的样式文件、技术栈等。如果不把这些公共文件提取出来,那么每个单页打包出来的chunk中都会包含公共代码,相当于要传输n份重复代码。如果把公共文件提取出一个文件,那么当用户访问了一个网页,加载了这个公共文件,再访问其他依赖公共文件的网页时,就直接使用文件在浏览器的缓存,这样公共文件就只用被传输一次。 ⑧分割代码以按需加载 单页应用的一个问题在于使用一个页面承载复杂的功能,要加载的文件体积很大,不进行优化的话会导致首屏加载时间过长,影响用户体验。做按需加载可以解决这个问题。具体方法如下: 将网站功能按照相关程度划分成几类 每一类合并成一个Chunk,按需加载对应的Chunk 例如,只把首屏相关的功能放入执行入口所在的Chunk,这样首次加载少量的代码,其他代码要用到的时候再去加载。最好提前预估用户接下来的操作,提前加载对应代码,让用户感知不到网络加载 ⑨使用Prepack提前求值 Prepack是一个部分求值器,编译代码时提前将计算结果放到编译后的代码中,而不是在代码运行时才去求值。通过在便一阶段预先执行源码来得到执行结果,再直接将运行结果输出以提升性能。但是现在Prepack还不够成熟,用于线上环境还为时过早。 ⑩使用Scope Hoisting 译作“作用域提升”,是在Webpack3中推出的功能,它分析模块间的依赖关系,尽可能将被打散的模块合并到一个函数中,但不能造成代码冗余,所以只有被引用一次的模块才能被合并。由于需要分析模块间的依赖关系,所以源码必须是采用了ES6模块化的,否则Webpack会降级处理不采用Scope Hoisting。

本文章由javascript技术分享原创和收集

发表评论 (审核通过后显示评论):