前言
首页的高速加载和渲染一直是前端开发者们津津乐道的事情,因此各种技术也应运而生。在 HTTP1.1 时代,为了减少请求的发送,加快首页加载,压缩和合并成了必不可少的技术,其中包括了 JavaScript 文件的压缩、混淆和合并,还有 CSS 文件的压缩和合并,最后还有一个是针对小图片的请求优化,也就是 CSS Sprite
,也叫 雪碧图
或 CSS 精灵
。其大概的思想就是将多个小图片按照一定的尺寸和位置排列好,然后合成一张图片,最后用户访问页面时,只要请求这一张合成图,而开发者利用 background-position
等属性控制显示合成图某个位置的图片,即可达到一张图片多个图标的效果,同时也将请求数量压缩为一个。当然,这次我们要说的并不是这个技术,而是与之运用思想类似的 SVG Sprite
。
SVG Sprite
SVG Sprite
使用 <symbol>
标签来定义一个图形模板对象,好处在于其可以重复利用,我们可以看一下 MDN 中对 <symbol>
的定义:
symbol元素用来定义一个图形模板对象,它可以用一个元素实例化。symbol元素对图形的作用是在同一文档中多次使用,添加结构和语义。结构丰富的文档可以更生动地呈现出来,类似讲演稿或盲文,从而提升了可访问性。注意,一个symbol元素本身是不呈现的。只有symbol元素的实例(亦即,一个引用了symbol的 元素)才能呈现。
可以看到,<symbol>
定义的图形并不会第一时间显示出来,只有使用了 <use>
标签进行实例化以后才会显现。MDN 中对其做出了如下定义:
use元素在SVG文档内取得目标节点,并在别的地方复制它们。它的效果等同于这些节点被深克隆到一个不可见的DOM中,然后将其粘贴到use元素的位置,很像HTML5中的克隆模板元素。
而要使用 <use>
来实例化一个 svg图形模板对象
,则要使用其中的 xlink:href
属性,在我们处理好的 <symbol>
上都会带有一个 id
,如下所示(伪代码):
1 2 3 4 5 6 7
| <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0;visibility:hidden"> <defs> <symbol id="icon1">...</symbol> <symbol id="icon2">...</symbol> <symbol id="icon3">...</symbol> </defs> </svg>
|
根据每一个 <symbol>
的 id
,我们可以使用 <use>
根据这些 id
来使用 svg
,如下所示:
1 2 3 4 5
| <div class="icons"> <svg><use xlink:href="#icon1"/></svg> <svg><use xlink:href="#icon2"/></svg> <svg><use xlink:href="#icon3"/></svg> </div>
|
SVG Sprite
的基本原理就是运用这些元素,相比较 CSS Sprite
,SVG Sprite
显得更为友好,不用多余的例如 background-position
属性来控制位置。
兼容性
运用一个技术的前提都是其兼容性满足项目的最低要求,或者在不兼容的情况下有相对应的替代方案。在饿了么 Web 的兼容性要求为 PC 端 IE9+,安卓移动端 4.4+,IOS7+,具体可以看 ElemeFE 的 style-guide。下面是在 caniuse 上的结果:
可以看到兼容性在 PC 端上是完全没有问题的,而在移动端上也能支持,所以可以安心地用起来了。
结合 Webpack 使用 SVG Sprite
我们使用 Webpack
来对多个分离的 SVG 文件进行自动化处理为合并好的多个 <symbol>
,并插入到 <body>
顶部。首先我们要对 webpack.config.js
进行配置。
首先看一下基本的文件目录结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ├── LICENSE ├── README.md ├── css │ └── index.css ├── dist │ ├── app.css │ ├── app.js │ └── index.html ├── js │ └── index.js ├── package.json ├── static │ ├── analytics.svg │ ├── archives.svg │ ├── businessman.svg │ ├── businessmen.svg │ ├── certificate.svg │ ├── chat.svg │ └── contract.svg ├── template │ └── index.html └── webpack.config.js
|
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| const path = require('path'); const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); var plugins = [ new HtmlWebpackPlugin({ filename: './index.html', template: './template/index.html' }) ]; plugins.push( new ExtractTextPlugin('[name].css') ); module.exports = { entry: { app: './js/index.js', }, output: { path: './dist', publicPath: './', filename: '[name].js' }, resolve: { extensions: [ '', '.js' ] }, module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }, { test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css') }, { test: /\.svg$/, loaders: [ 'svg-sprite-loader', 'svgo-loader?useConfig=svgoConfig' ] }, { test: /\.(gif|png|jpg|ttf|woff2|woff|eot)$/, loader: 'url?limit=1000&name=[path][name].[hash:6].[ext]' } ] }, svgoConfig: { plugins: [ { removeTitle: true }, { convertColors: { shorthex: true } }, { convertPathData: true }, { cleanupAttrs: true }, { removeComments: true }, { removeDesc: true }, { removeUselessDefs: true }, { removeEmptyAttrs: true }, { removeHiddenElems: true }, { removeEmptyText: true } ] }, plugins: plugins };
|
可以看到,配置文件中使用了 svg-sprite-loader
和 svgo-loader
对 svg
文件进行处理,svg-sprite-loader
的作用就是将多个 svg
文件合并为一个 <svg>
元素。至于 svgo-loader
,作用是将 <svg>
中一些无用的信息过滤去除,精简结构,详细配置可以自行查阅对应的文档说明,可以根据实际需求进行过滤。接下来将列出 index.css
, index.js
, index.html
的内容:
index.css
1 2 3 4 5 6 7 8
| * { box-sizing: border-box; } .icons svg { width: 100px; height: 100px; }
|
index.js
1 2 3 4 5 6 7 8 9 10
| import indexStyle from '../css/index.css'; import analytics from '../static/analytics.svg' import archives from '../static/archives.svg' import businessman from '../static/businessman.svg' import businessmen from '../static/businessmen.svg' import certificate from '../static/certificate.svg' import chat from '../static/chat.svg' import contract from '../static/contract.svg' console.log('demo complete');
|
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>svg-sprites-demo</title> </head> <body> <div class="icons"> <svg><use xlink:href="#analytics"/></svg> <svg><use xlink:href="#archives"/></svg> <svg><use xlink:href="#businessman"/></svg> <svg><use xlink:href="#businessmen"/></svg> <svg><use xlink:href="#certificate"/></svg> <svg><use xlink:href="#chat"/></svg> <svg><use xlink:href="#contract"/></svg> </div> </body> </html>
|
以上就是主要的一些配置和内容,如果需要完整的项目,可以到我的 github 下 clone 项目到你的本地进行构建。下面是构建后的网页效果和结构:
优点 & 缺点
优点:
- 将多个请求压缩为无请求
- svg 对比 image 其屏幕适应性更好,任何分辨率都能达到高清效果
- svg 体积更小
- 每一个
<symbol>
都可以重复利用
缺点:
- svg 不利于变动性大的图片,例如需要经常修改颜色
- 兼容性对于需要兼容 IE8- 的网站不好,需要对低版本浏览器有替代方案
参考资料:
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/symbol
https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/defs
https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/use