腾讯DeepOcean原创文章:dopro.io/webpack-spr…
前言
在HTTP/2.0还没有普及的现在,css sprite(雪碧图)仍是前端工程化中必备的一环,而随着webpack应用的普及,构建工具切换到webpack后雪碧图该如何处理呢?目标
先说下我们希望达到的效果。1、需要合成的雪碧图icon统一放在src / assets / sprite文件夹下
2、在src / img 会生成一份以上级目录名称命名的雪碧图,如icon文件夹中用到的图标拼合成icon.png
3、雪碧图从上到下的方式排列,并且每个图标间隔20px,如图所示
4、在sass中编写,按需合并。
5、编译后得到的样式如下,包括width、height也一同输出
常见方案
目前主要的解决方案有两种一种是以webpack-spritesmith、postcss-sprites为代表的插件,webpack-spritesmith主要的运行方式是会预先把sprite目录中的图标拼合好,页面主要依赖生成后的雪碧图。而postcss-sprites则是通过对已经生成的 CSS 文件进行分析,将其中的 background 或 background-image 作为依赖收集,合并成雪碧图后再将相关参数替换。
第二种是loader处理方式,不过网上基本上没有很好的方案,比如说这种
这种通过注释的方式来识别并不是很好。
目前市面上的方案都不能满足我们的个性化要求,相对于插件针对某一文件夹/文件处理方式,我们更希望采用loader链式处理的方式来实现。
解决方案
采用loader的话只能通过添加标记的方式告知需要合成,比如在background url加上“?_sprite”,然后利用正则获取图片路径,给到相关的库去合成。
这里主要利用spritesmith开源项目来进行开发,spritesmith很强大,它可以比较灵活的合成雪碧图,通过相应的API可以获得图标的相关信息。
其中sprites是需要被合成的图片路径,里面包含了图标的绝对路径
Spritesmith.run({src: sprites,algorithm:'top-down',padding: 20}, function handleResult (err, result) {if(err) {throw err;}});
Spritesmith.run({src: sprites,algorithm:'top-down',padding: 20}, function handleResult (err, result) {if(err) {throw err;}});
利用这些信息我们可以对返回的样式进行重新处理,满足前面第5点的样式要求
let imageW = image.width;let imageH = image.height;if(mobile){imageW = imageW/2;imageH = imageH/2;}let imgWidth = 'width:' + imageW + 'px;';let imgHeight = 'height:' + imageH + 'px;';if(i < afterContent.length) {if(afterContent[i] == ';') {end = i + 1;afterContent = afterContent.substring(0, end) + backgroundSize+ imgWidth+ '\n' + imgHeight + afterContent.substring(end);} else {end = i;afterContent = afterContent.substring(0, end) + ';\n' + backgroundSize +imgWidth+ '\n' + imgHeight+ afterContent.substring(end);}}let imagePosX = image.x;let imagePosY = image.y;if(mobile){imagePosX = imagePosX/2;imagePosY = imagePosY/2;}let imageX = image.x == 0 ? ' 0' : ' -' + imagePosX + 'px';let imageY = image.y == 0 ? ' 0' : ' -' + imagePosY + 'px';let imagePosition = '';if(image.x || image.y){imagePosition = imageX + imageY;}let cssVal = 'url("' + spriteRelativePath + '")' + imagePosition;
module:{rules:[{test:/\.(scss|css)$/,use: ExtractTextPlugin.extract({fallback: "style-loader",publicPath: '../../',use: [{loader:'css-loader'},{loader:'isprite-loader',options:{outputPath:'./src/assets/img/',mobile:true}},{loader:'sass-loader'}],})},]}
最后
到这里已经基本能达到预期的效果,不过仍有些问题没有处理好,比如每次都会生成雪碧图,这对于编译速度会有一定的影响,针对这种问题,可以采用hash值进行对比,如果文件没有改动的话则不处理。每一个业务都有不同的需求场景,这种方式可能不一定适用于其他项目,希望对大家有所帮助。
附上demo
欢迎关注"腾讯DeepOcean"微信公众号,每周为你推送前端、人工智能、SEO/ASO等领域相关的原创优质技术文章:
看小编搬运这么辛苦,关注一个呗:)
