浅探前端图片优化
性能优化是前端开发必不可少的一环,而图片优化又是性能优化中必不可少的一环,但不知道有多少开发者在网页的开发过程中会注意图片的使用,图片使用不当可能会导致网页加载卡顿、网页加载速度慢等问题,这篇文章将会将我以往对图片的处理做个总结。
不同格式图片优劣对比
有人可能会问说好的图片优化呢?怎么说到图片格式了,其实在不同的场景选择使用不同格式的图片就是对图片的一种优化,这是最直接最重要但是最容易被忽略的,现在网页中常用的图片格式有JPG.PNG.SVG.WebP等,接下来我们就来介绍它们有何优劣
JPG
JPG格式的图片应该是使用场景最多的图片的格式了,由于JPG格式采用了极其高效的压缩算法,使其能在压缩50%甚至60%的情况下依旧可以保持不错的图片质量,因此在网站设计中使用类似背景图,轮播图等大图时都会考虑使用JPG格式的图片,但是JPG始终是有损压缩,在对线条感较强或者颜色比较丰富的图片做人为压缩时,可能会出现失真的情况,同时它也不支持透明度处理
PNG
PNG格式的图片特点大家都知道,就是高保真无损压缩,当对图片设计有较高要求时,首选PNG格式,显示高清细腻,但是它也有明显的问题就是体积过大
资源网站大全 https://55wd.com 我的007办公资源网站 https://www.wode007.com
SVG
SVG格式图片有个显著特点就是它是可编程的,是基于xml语法的,同时作为矢量图,它可以无限放大而不变形,因此可以方便的对不同手机屏幕做自适应,相比于PNG和JPG它的体积更小,只有1kb甚至更小,但是它最大的缺陷就是渲染成本过高,因此我们在选择一些小且色彩单一的图标时可以考虑使用SVG格式的图片,如图
一般情况下,我们会将SVG格式的图片上传到iconfont上,这样不仅方便管理而且方便使用,同时iconfont上还有许多其他设计师设计的优秀小图标可以直接拿来使用,是不是很方便呢?
WebP与gif
这两兄弟我们一般都是用来展示动图的,但是WebP也可以用来展示静态图片,WebP最大的优点就是无损压缩,体积小,但是浏览器支持太差,我们来看caniuse的数据:
从图上可以看到WebP格式在苹果设备和IE上基本不支持,因此浏览器的不支持是它的硬伤,因此在对动图做展示的时候我们不得不选gif,即便它的体积很大,渲染开销也大
图片优化方案
图片质量压缩
图片压缩应该是图片优化时最常用的方案,因为很简单,只需要将图片上传到tinypng或者智图这类的在线压缩图片平台,对图片进行压缩,就可以较小图片质量
雪碧图
雪碧图经常用来将多个小图标和成一张图片,然后将合成的图片当作背景图片是使用,这样可以减少图片的网络请求,使用之前可能需要请求10个网络小图标,而使用之后请求一个就可以搞定,我个人通常使用gopng这个网站在线生成,还可以自动生成对应的css代码
base64
将一个图片地址进行base64编码后会得到一串字符串,将这个字符直接放到img的src属性上,你会发现浏览器是可以识别这一串字符的,不需要发送网络请求直接解析,这样就可以达到减少网络请求的目的,但是base64编码后的图片质量比原图图片质量要大,因此也只会在一些质量较小的图标类图片上面使用,否则得不偿失,常见使用base64编码的方案就是webpack的url-loader,举个例子:
module.exports = { module: { rules: [ { test: /\.(png|jpg|gif)$/, use: [ { loader: 'url-loader', options: { limit: 8192 } } ] } ] } }
上面的这个配置就是把8k一下的通过url-loader进行base64编码,转换成一串DataUrl
css替换简单图标
这个优化方案应该都懂,其实就是在写代码之前先考虑一下设计稿里面的哪些内容是可以通过代码来实现的,能通过代码实现的尽量用代码实现,同时实现的时候多考虑绘制性能,能使用css3做GPU硬件加速的就尽量使用css3属性,这些都能减少图片使用而且不影响渲染性能
响应式图片加载
什么是响应式图片加载?其实就是在不同分辨率的设备上显示不同尺寸的图片,避免资源的浪费,常用的方法就是css3的媒体查询(media query),来看个例子:
@media screen and (max-width: 375px) { img { background-image: url('phone.png'); } } @media screen and (max-width: 768px) { img { background-image: url('tablet.png'); } }
懒加载
图片懒加载的目的就是为加快页面加载速度而做的,为了不让图片一次全部加载出来,通过将图片地址存放在一个img标签的属性上,当图片被滚动到页面上时,在将src属性替换成图片地址来达到懒加载的效果
webpack图片优化
图片压缩
webpack也可以对图片进行压缩操作,通过image-webpack-loader可以对输出的图片进行指定质量的压缩,来看具体例子:
{ test: /\.(png|jpg|gif|svg)$/, use: [ 'file-loader', { loader: 'image-webpack-loader', options: { bypassOnDebug: true, mozjpeg: { progressive: true, quality: 65 }, optipng: { enabled: false, }, pngquant: { quality: '65-90', speed: 4 }, gifsicle: { interlaced: false, }, // the webp option will enable WEBP webp: { enabled: false, }, limit: 1, name: '[name].[ext]?[hash]' } }] }
上面的配置指定了各个格式的图片的压缩质量,并且通过hash编码重新命名输出
合成雪碧图
webpack的webpack-spritesmith插件提供了自动合成雪碧图的功能并且可以自动生成对应的央视文件,非常方便,来看一个具体的例子:
const SpritesmithPlugin = require('webpack-spritesmith') new SpritesmithPlugin({ src: { cwd: path.resolve(__dirname, 'src/asserts'), glob: '*.png' }, target: { image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'), css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.css') }, apiOptions: { cssImageRef: "src/sprite.png" } })
通过上面配置就能将asserts目录下的所有png文件合成雪碧图,并且输出到对应目录,同时还可以生成对应的样式文件,样式文件的语法会根据你配置的样式文件的后缀动态生成,比如这里我们配置的是sprite.css,生成的文件内容就是css语法:
.icon-checkout { background-image: url(src/sprite.png); background-position: -96px -56px; width: 34px; height: 32px; } .icon-clock { background-image: url(src/sprite.png); background-position: -96px 0px; width: 56px; height: 56px; } .icon-close { background-image: url(src/sprite.png); background-position: 0px 0px; width: 96px; height: 96px; }
如果将配置中的sprite.css改成sprite.scss那么生成语法就是scss的语法:
@mixin sprite-width($sprite) { width: nth($sprite, 5); } @mixin sprite-height($sprite) { height: nth($sprite, 6); } @mixin sprite-position($sprite) { $sprite-offset-x: nth($sprite, 3); $sprite-offset-y: nth($sprite, 4); background-position: $sprite-offset-x $sprite-offset-y; } @mixin sprite-image($sprite) { $sprite-image: nth($sprite, 9); background-image: url(#{$sprite-image}); } @mixin sprite($sprite) { @include sprite-image($sprite); @include sprite-position($sprite); @include sprite-width($sprite); @include sprite-height($sprite); } @mixin sprites($sprites) { @each $sprite in $sprites { $sprite-name: nth($sprite, 10); .#{$sprite-name} { @include sprite($sprite); } } }
这样就可以根据你项目中使用的样式语言去生成所需要的语法,是不是很方便呢?