前端知识体系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。
发表评论 (审核通过后显示评论):