原文地址: https://segmentfault.com/a/1190000021487215
作者: Fw恶龙
本文首发于: 思否
一、前言
基于Webpack的CSS Sprites实现方案,若是直接在html中调用雪碧图图标已经很方便,但是实际开发过程可能遇到需要在伪元素中使用雪碧图,或者需要hover切换另一个图标,这种情况下就无法在css中直接调用图标类名。这时,就需要css预处理器,当然不限于stylus,less、sass都是可以的,本文介绍stylus方案。
二、项目配置
1. 目录结构预览
+ node_modules
+ src // 开发目录
+ css
- icon.styl // webpack-spritesmith生成
- index.styl
+ images
- icon.png // webpack-spritesmith生成
+ icon
..png
..png
+ js
+ main.js
+ dist // 代码产出目录
– index.html
+ js
- main.js
+ css
- index.css
+ images
- icon.png
– package.json
– package-lock.json
- postcss.config.js
– webpack.config.js
2. 初始化项目
npm init
3. package.json
{
"name": "cwwebpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack --mode development"
},
"author": "",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^9.1.2",
"clean-webpack-plugin": "^1.0.0",
"css-loader": "^1.0.0",
"cssnano": "^4.0.5",
"file-loader": "^1.1.11",
"postcss-loader": "^3.0.0",
"stylus": "^0.54.5",
"url-loader": "^1.1.1",
"webpack": "^4.17.0",
"webpack-cli": "^3.1.2",
"webpack-spritesmith": "^0.5.4"
},
"dependencies": {
"mini-css-extract-plugin": "^0.4.1",
"stylus-loader": "^3.0.2"
}
}
4. 安装相关modules
npm install
5. webpack.config.js
var path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
const SpritesmithPlugin = require('webpack-spritesmith');
var templateFunctionStylPC = function(data) {
var sharedRem = '.ico\n display: inline-block\n background-image: url("I")\n'
.replace('I', data.sprites[0].image);
var perSpriteRem = data.sprites.map(function(sprite) {
return '.ico-N\n width: Wpx\n height: Hpx\n background-position: Xpx Ypx'
.replace('N', sprite.name.replace(/_/g, '-'))
.replace('W', sprite.width)
.replace('H', sprite.height)
.replace('X', sprite.offset_x)
.replace('Y', sprite.offset_y);
}).join('\n');
return sharedRem + '\n' + perSpriteRem;
};
var templateFunctionStylMobile = function(data) {
var sharedRem = '.ico\n display: inline-block\n background-image: url(I)\n background-size: Drem Hrem'
.replace('I', data.sprites[0].image)
.replace('D', data.sprites[0].total_width / 100)
.replace('H', data.sprites[0].total_height / 100);
var perSpriteRem = data.sprites.map(function(sprite) {
return '.ico-N\n width: Wrem\n height: Hrem\n background-position: Xrem Yrem'
.replace('N', sprite.name.replace(/_/g, '-'))
.replace('W', sprite.width / 100)
.replace('H', sprite.height / 100)
.replace('X', sprite.offset_x / 100)
.replace('Y', sprite.offset_y / 100);
}).join('\n');
return sharedRem + '\n' + perSpriteRem;
};
module.exports = {
entry: __dirname + "/src/js/main.js", //唯一入口文件
output: {
path: __dirname + "/dist/js", //打包后的文件存放的地方
filename: "main.js" //打包后输出文件的文件名
},
watch: false, //开启自动编译
module: {
rules: [
{
test: /\.styl$/,
use: [{
loader: MiniCssExtractPlugin.loader
}, {
loader: "css-loader"
}, {
loader: "postcss-loader"
}, {
loader: "stylus-loader"
}]
},
{
test: /\.(png|svg|jp?g|gif)$/,
loader: 'url-loader',
options: {
limit: 8192, //大于 1KB = 1024B = 8192字节 的图片正常打包,小于8192字节的图片以base64的方式引用
name: '../images/[name].[ext]'
}
}
]
},
plugins: [
new CleanWebpackPlugin(
['dist/css', 'dist/images'], //打包前需要清除的文件夹
{
root: __dirname, //根目录
verbose: true, //开启在控制台输出信息
dry: true //关闭删除文件
}
),
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'src/images/icon'),
glob: '*.png'
},
target: {
image: path.resolve(__dirname, 'src/images/icon.png'),
css: [
[path.resolve(__dirname, 'src/css/icon.styl'), {
format: 'function_based_template'
}]
]
},
customTemplates: {
'function_based_template': templateFunctionStylPC
},
apiOptions: {
cssImageRef: '../images/icon.png'
},
spritesmithOptions: {
algorithm: 'binary-tree',
padding: 2
}
}),
new MiniCssExtractPlugin({
filename: "../css/index.css",
}),
],
};
5. 在src/images/icon中放置需要合成雪碧图的图标
6. index.styl
@import "icon.styl"
7. 执行打包命令
npm start
- icon.styl示例(由webpack-spritesmith生成)
.ico
display: inline-block
background-image: url("../images/icon.png")
.ico-btn-android-active
width: 147px
height: 59px
background-position: -370px -250px
.ico-btn-android
width: 147px
height: 59px
background-position: -370px -320px
8. index.styl(书写stylus代码)
@import "icon.styl"
.btn
@extend .ico
@extend .ico-btn-android
&:hover
@extend .ico
@extend .ico-btn-android-active
9. 再次执行打包命令
npm start //若嫌每次手动打包麻烦的话,可以在webpack.config.js中开启自动编译watch: true
10. index.css(webpack打包后的文件)
.ico,
.btn,
.btn:hover {
display: inline-block;
background-image: url(../images/icon.png);
}
.ico-btn-android-active,
.btn:hover {
width: 147px;
height: 59px;
background-position: -370px -250px;
}
三、相关链接
四、错误记录
stylus相关
-
expected "indent", got "outdent"
- tab和空格混用缩进,导致stylus编译出错
-
expected "indent", got "literal ../"
- 图片路径需要双引号
-
expected "indent" got "eos"
- 混用了tab和空格,只能用其中一种
webpack相关
-
清除旧文件
-
npm operation not permitted
- 试试重启电脑(可能是什么进程锁住了文件),不行?再看看下面其他回答
- 知乎
五、后记
或许以上还不是最优方案,但仍在自动化的边缘反复试探,在试探的过程中发现stylus的函数有点好用,配合图片使用,可以不用再去测量图片的宽高,还有其他特性慢慢摸索吧(ง'-̀'́)ง