基础前端React

前端图片二三事

2020-07-13  本文已影响0人  CondorHero

前言:在我刚刚上大学的时候,也就是 17 的时候,以这个时间点为基准,再把时间往前推一点,那时候的前端是怎么处理图片的呢?

一、前端图片发展三阶段

这是一个精灵图也称雪碧图:


这是一个精灵图也称雪碧图: 简单的字体图标

最大的优点就是可以像使用字体一样使用图标,不用担心图标大小和字体对齐的问题,另外 CSS 的 font 属性对图标都可以生效,尤其是 font-sizecolor 贼好用。但是有个缺点只支持单色图标,一般默认是灰不拉几的颜色,一旦 color 改变,是红就全红,蓝就全蓝。

字体图标原理:不知道你有没有疑问🤔️?字体图标为什么可以用字体来表示,明明是图片呀。浏览器的文字之所以能够识别我们写的字是因为指定了编码,编码库里面有这个字,涉及到的就是 unicode 编码:

二、现在项目如何使用图片

现在我们公司图片项目都用阿里妈妈的 iconfont 来管理,一般在项目中:

三、精灵图的优化

自己一个前端写项目可能哪里用到直接上 background-position 等属性卡卡的怼起来了,多人写作可能会维护一个 CSS 文件,全部按图片名称命名好类名,用到直接使类名。

但是不容忽视缺点很大,设计师新增一个图标或删除一个图标就要动精灵图,一不小心就可能定位全部乱了

如果有一个工具,能把指定文件夹中的一个个小图标打包成一个精灵图,并自动生成 CSS 类文件,一点不需要我们手动维护就好了。有请 webpack-spritesmith 他就能干这个事情。

先推荐一个教程 vue-cli3中使用webpack-spritesmith配置雪碧图 这个作者写的挺详细的。

用 Vue 的项目自己动手实践下,先用 CSS 来实现。

四、上手使用 webpack-spritesmith

GitHub 源码地址: 上手使用 webpack-spritesmith源码

创建的目录结构:

|--webpack.config.js
|--index.html
   |--img
   |   |--sprite.css//插件生成
   |   |--sprite.png/插件生成
   |   |--sprites
   |   |   |--img_1x.png
   |   |   |--img_2x.png
   |   |   |--img_3x.png
   |   |   |--img_4x.png
   |--package-lock.json
   |--package.json
|--|--js
    |--App.vue
    |--main.js

各个文件的内容详细内容为:

package.json 的内容:

{
    "name": "webpack-spritesmith-test",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "dev": "rm -rf ./dist && webpack"
    },
    "author": "",
    "license": "ISC",
    "dependencies": {
        "copy-webpack-plugin": "^6.0.3",
        "css-loader": "^3.6.0",
        "file-loader": "^6.0.0",
        "less-loader": "^6.2.0",
        "style-loader": "^1.2.1",
        "vue": "^2.6.11",
        "vue-loader": "^15.9.3",
        "vue-template-compiler": "^2.6.11",
        "webpack": "^4.43.0",
        "webpack-cli": "^3.3.12",
        "webpack-spritesmith": "^1.1.0"
    }
}

webpack.config.js 内容:

 //引入node模块,一会要操作文件,输入,生成,输出都需要这玩意。
const path = require('path');
// 主角,必须引入。要不然咋用
const SpritesmithPlugin = require('webpack-spritesmith');
// vue-loader
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// copy插件,把一个文件或目录拷贝到另一个地方,这里用来拷贝index.html
// https://www.npmjs.com/package/copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin');


// 所有的配置都在这个导出里面
module.exports = {
    mode: "development",
    //配置入口文件
    entry: "./js/main.js",
    //配置产出文件
    output: {
        //产出文件文件夹
        path: path.resolve(__dirname, "dist"),
        //产出文件的文件名
        filename: "bundle.js"
    },
    //实时监测文件更新,一旦文件更新了,就重新合并打包一份
    watch: true,
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.css$/, use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /\.png$/, use: [
                    'file-loader?name=[hash].[ext]'
                ]
            }
        ]
    },
    resolve: {
        /*
        细节坑。文档里面写着 需要resolve,引入图片生成的位置,
        不加这行会报错。因为github,Readme里面有这句话
        resolve contains location of where generated image is
        (要把生成的地址resolve到modules里面。不写就错)
        一定要加,血的教训啊
        */
        modules: ["node_modules", "./img"],
        extensions: [".js", ".json", ".jsx", ".css",".vue"]
    },
    stats: 'errors-only',
    // 定义一个插件数组。用来覆盖,在里面使用我们的主角
    plugins: [
        new SpritesmithPlugin({
            /*
            目标小图标,这里就是你需要整合的小图片的老巢。
            现在是一个个的散兵,把他们位置找到,合成一个
            */
            src: {
                cwd: path.resolve(__dirname, './img/sprites'),
                glob: '*.png'
            },
            // 输出雪碧图文件及样式文件,这个是打包后,自动生成的雪碧图和样式,自己配置想生成去哪里就去哪里
            target: {
                image: path.resolve(__dirname, './img/sprite.png'),
                css: [
                    // webpack,会自动生成一个sprite.css的样式
                    [path.resolve(__dirname, './img/sprite.css')],
                ]
            },
            // 生成的样式文件中调用雪碧图地址写法(Readme这么写的)
            // 不写就是全局的绝对图片路径
            apiOptions: {
                cssImageRef: '~sprite.png'
            },
            // 让合成的每个图片有一定的距离,否则就会紧挨着,不好使用
            spritesmithOptions: {
                padding: 20
            }
        }),
        new VueLoaderPlugin(),
        new CopyWebpackPlugin({
            patterns: [
                { from: __dirname + "/index.html", to:  __dirname + "/dist" }
            ],
        
        })
    ]
}

index.html 内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>webpack-spritesmith</title>
</head>
<body>
    <div id="app"></div>
    <script src="./bundle.js"></script>
</body>
</html>

main.js

import Vue from "vue";
import App from "./App.vue";

new Vue({
    el:"#app",
    render(h){
        return h(App);
    }
});

App.vue 类的名字默认都是 icon-图片名例如:``icon-img_1x

<template>
    <div>
        <div class="icon-img_1x"></div>
    </div>
</template>

<script>
    export default {
        
    }
</script>

<style lang="css">
@import "../img/sprite.css";
</style>

最后运行代码:

npm run dev

sprite.css 文件里面四个类选择器,大致长成这样,简单的图片引入和定位:

.icon-img_1x {
    background-image: url(~sprite.png);
    background-position: 0px 0px;
    width: 120px;
    height: 120px;
}

成功访问效果:


好处大大的,终于不用我们合并图片和调整 position 的位置。

一般项目中都不是直接使用 CSS 的,往往会使用类似 less 这种预处理器,我就以 less 来演示下。webpack-spritesmith 编译的 CSS 就是简单类的使用,但是如果使用使用 sassless 编译就会用到一些稀奇古怪的语法,为了能看懂 less 编译之后的文件,我们需要了解一下 less 的知识,推荐 Less教程:

不想搭建环境,你也可以使用这个网站在线实时调试:https://lesstester.com/

五、less 前置知识了解

条件判断使用 when 来实现的,循环使用 mixin 和 when 来递归调用的。具体点击:less 使用笔记

列表函数函数一共两个方法:
1. Length 列表的长度。

// 列表可以使用空格或逗号间隔
p{
  @list: "a", "b", "c", "d", "e", "f", "g", "h", "k", "m", "l", "s";
  @font-size: length(@list);
  font-size: @font-size * 1px;
}

p{
  @list: "a" "b" "c" "d" "e" "f" "g" "h" "k" "m" "l" "s";
  @font-size: length(@list);
  font-size: @font-size * 1px;
}
//上面两种写法等效,都会编译为:
p {
  font-size: 12px;
}
//less 很麻烦单位连接 * 1px

  1. Extract 返回列表中指定位置的值。
extract 就像访问数组一样,提取下标对应位置的值,不过 extract 的下标是从1开始的
p{
  @list: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17;
  @font-size: extract(@list,16);
  font-size: @font-size * 1px;
}
编译之后:
p {
  font-size: 16px;
}

上面是一维的列表,我们来看看二维列表:

p{
  @a: 1 2 3 4 5 6 7 8;
  @b: 9 10 11 12 13 14 15 16;
  @c : @a @b;
  font-size: extract(extract(@c,2),8) * 1px;
}
编译之后:
p {
  font-size: 16px;
}
p{
  @a: 1 2 3 4 5 6 7 8;
  @b: 9 10 11 12 13 14 15 16;
  @c : 17 18 19 20;
  @d: @a @b;
  @e: @c @d; 
  font-size: extract(extract(@c,2),8) * 1px;
}

还可以三维列表:
p{
  @a: 1 2 3 4 5 6 7 8;
  @b: 9 10 11 12 13 14 15 16;
  @c : 17 18 19 20;
  @d: @a @b;
  @e: @c @d; 
  font-size: extract(extract(extract(@e,2),2),1) * 1px;
}
编译之后:
p {
  font-size: 9px;
}

知道多维列表和单维列表,就能明白为什么经过`webpack-spritesmith`编译之后生成的`less`文件对只有一个精灵图是无效的。

改改循环,相应的我们可以写出来处理一张图片的 less 代码,但是没必要因为一张图完全可以直接引入
.sprites(@sprites, @i: 10) when (@i = length(@sprites)) {
    @sprite-name: e(extract(@sprites, length(@sprites)));
    .@{sprite-name} {
      .sprite(@sprites);
    }
}
〜"some_text"中的任何内容将显示为 some_text 。
font-size: ~"12px";
编译之后:
font-size: 12px;

font-size: e("12px");
编译之后:
font-size: 12px;

〜"xxx" === e("xxx")

%此函数格式化一个字符串。 它可以写成以下格式:
font-size: e(%("12%d",px));
编译之后:
font-size: 12px;

六、看下 webpack-spritesmith 处理过的 less 文件

使用 less 精灵图要求必须两张以上,否则无法使用,为什么一张图片不行,在上面二维列表那里。

/*
LESS variables are information about icon's compiled state, stored under its original file name

.icon-home {
  width: @icon-home-width;
}

The large array-like variables contain all information about a single icon
@icon-home: x y offset_x offset_y width height total_width total_height image_path name;

At the bottom of this section, we provide information about the spritesheet itself
@spritesheet: width height image @spritesheet-sprites;
*/
@img-1x-name: 'img_1x';
@img-1x-x: 0px;
@img-1x-y: 0px;
@img-1x-offset-x: 0px;
@img-1x-offset-y: 0px;
@img-1x-width: 120px;
@img-1x-height: 120px;
@img-1x-total-width: 260px;
@img-1x-total-height: 120px;
@img-1x-image: '~sprite.png';
@img-1x: 0px 0px 0px 0px 120px 120px 260px 120px '~sprite.png' 'img_1x';
@img-2x-name: 'img_2x';
@img-2x-x: 140px;
@img-2x-y: 0px;
@img-2x-offset-x: -140px;
@img-2x-offset-y: 0px;
@img-2x-width: 120px;
@img-2x-height: 120px;
@img-2x-total-width: 260px;
@img-2x-total-height: 120px;
@img-2x-image: '~sprite.png';
@img-2x: 140px 0px -140px 0px 120px 120px 260px 120px '~sprite.png' 'img_2x';
@spritesheet-width: 260px;
@spritesheet-height: 120px;
@spritesheet-image: '~sprite.png';
@spritesheet-sprites: @img-1x @img-2x;
@spritesheet: 260px 120px '~sprite.png' @spritesheet-sprites;

/*
The provided classes are intended to be used with the array-like variables

.icon-home {
  .sprite-width(@icon-home);
}
.icon-email {
  .sprite(@icon-email);
}

Example usage in HTML:

`display: block` sprite:
<div class="icon-home"></div>

To change `display` (e.g. `display: inline-block;`), we suggest using a common CSS class:

// CSS
.icon {
  display: inline-block;
}

// HTML
<i class="icon icon-home"></i>
*/
.sprite-width(@sprite) {
  width: extract(@sprite, 5);
}

.sprite-height(@sprite) {
  height: extract(@sprite, 6);
}

.sprite-position(@sprite) {
  @sprite-offset-x: extract(@sprite, 3);
  @sprite-offset-y: extract(@sprite, 4);
  background-position: @sprite-offset-x @sprite-offset-y;
}

.sprite-image(@sprite) {
  @sprite-image: extract(@sprite, 9);
  background-image: e(%('url(%a)', e(@sprite-image)));
}

.sprite(@sprite) {
  .sprite-image(@sprite);
  .sprite-position(@sprite);
  .sprite-width(@sprite);
  .sprite-height(@sprite);
}

/*
The `.sprites` mixin generates identical output to the CSS template
  but can be overridden inside of LESS

This must be run when you have at least 2 sprites.
  If run with a single sprite, then there will be reference errors.

.sprites(@spritesheet-sprites);
*/
.sprites(@sprites, @i: 1) when (@i <= length(@sprites)) {
  @sprite: extract(@sprites, @i);
  @sprite-name: e(extract(@sprite, 10));
  .@{sprite-name} {
    .sprite(@sprite);
  }
  .sprites(@sprites, @i + 1);
}

了解完 less 的基础知识就会发现没啥难得,就是通过mixin接受列表参数,来无限循环展开类。Vue 中使用:

<template>
    <div>
        <div class="img_1x"></div>
    </div>
</template>

<script>
    export default {
        
    }
</script>

<style lang="less">
@import "../img/sprite.less";
.sprites(@spritesheet-sprites);
//调用.sprites(@spritesheet-sprites);less会生成如下类
// */
// .img_1x {
//   background-image: url(~sprite.png);
//   background-position: 0px 0px;
//   width: 120px;
//   height: 120px;
// }
// .img_2x {
//   background-image: url(~sprite.png);
//   background-position: -140px 0px;
//   width: 120px;
//   height: 120px;
// }
</style>

看到编译的结果是不是很失望,使用 less 和直接用 CSS 编译的结果基本没啥区别,这弄的花里胡哨的还把我们给整蒙了。最大的好处就是可以在项目使用

七、使用 webpack-spritesmith 的自定义模版

我们能看到经过 webpack-spritesmith 处理的精灵图自动添加的 CSS 属性都是比较简单的。引入个图片定个位就没了。但是如果项目中,出现这种下面图片👇:


这种图片特别的小,如果 UI 一比一切给前端,图片展现就比较模糊,所以 UI 会切成二倍图交给我们,然后我们前端使用 CSS3 的 transform: scale(.5);,给缩小下就行了,完美解决问题。如果这种图片过多能不能让 webpack-spritesmith 来帮我们处理下呢?答案是可以利用 webpack-spritesmith 的自定义模版。

完整 webpack.config.js 加注释如下:

 //引入node模块,一会要操作文件,输入,生成,输出都需要这玩意。
const path = require('path');
const{ writeFile } = require("fs");
// 主角,必须引入。要不然咋用
const SpritesmithPlugin = require('webpack-spritesmith');
// vue-loader
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// copy插件,把一个文件或目录拷贝到另一个地方,这里用来拷贝index.html
// https://www.npmjs.com/package/copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin');


/* 
这里我们可以自己修改生成的模板样式,默认 webpack,会根据你的target里的配置,自动生成一个sprite.(c|le|sc)ss的样式文件
如果自动生成的不满意,
我们可以在这里修改,
可以自己打印一下 data里面的参数,看着就会大概明白(先看下面的配置,最后看这个模板)
*/
const customSpritesFunction = function (data) {
    // 观察data 到底是个啥
    // console.log(data);
    writeFile("./data.json",JSON.stringify(data),function(err){
        if(!err){
            console.log("写入成功!");
        }
    });
    // 观察data发现是个对象其中属性items和属性sprites都是数组,而且内容相同,截取其数组第一个数据
    // 可以看到items和属性sprites里面存储的是图片的信息。宽高大小路径等各种参数
    /*{
        "x": 0,
        "y": 0,
        "width": 120,
        "height": 120,
        "name": "img_1x",
        "source_image": "/Users/lixinwei/Desktop/webpack-spritesmith-test/img/sprites/img_1x.png",
        "image": "~sprite.png",
        "escaped_image": "~sprite.png",
        "total_width": 260,
        "total_height": 260,
        "offset_x": 0,
        "offset_y": 0,
        "px": {
            "x": "0px",
            "y": "0px",
            "offset_x": "0px",
            "offset_y": "0px",
            "height": "120px",
            "width": "120px",
            "total_height": "260px",
            "total_width": "260px"
        }
    },
    */

    // data.sprites数组图片信息,根据图片信息自己把一些属性组合到对应图片的类名里面,
    return data.sprites.map(function (sprite) {
        return '.icon-NAME-box{\n\tdisplay: inline-block;\n\toverflow: hidden;\n\twidth: BOXWIDTHpx;\n\theight: BOXHEIGHTpx;\n}\n.icon-NAME{\n\tdisplay: inline-block;\n\twidth: WIDTHpx;\n\theight: HEIGHTpx;\n\tbackground-image: url(IMG);\n\tbackground-position: Xpx Ypx;\n\ttransform-origin: 0 0;\n\ttransform: scale(NUM);\n}'
            .replace(/IMG/g,sprite.image)
            .replace(/NAME/g, sprite.name)
            .replace(/WIDTH/g, sprite.width)
            .replace(/HEIGHT/g, sprite.height)
            .replace(/X/g, sprite.offset_x)
            .replace(/Y/g, sprite.offset_y)
            .replace(/BOXHEIGHT/g, sprite.name.indexOf('_2x') > -1 ? sprite.height / 2 : sprite.height)
            .replace(/BOXWIDTH/g, sprite.name.indexOf('_2x') > -1 ? sprite.width / 2 : sprite.width)
            .replace(/NUM/g, sprite.name.indexOf('_2x') > -1 ? 0.5 : 1);
    }).join('\n');
    // 最后三行replace就是根据图片名是否决定进行缩放
};

// 所有的配置都在这个导出里面
module.exports = {
    mode: "development",
    //配置入口文件
    entry: "./js/main.js",
    //配置产出文件
    output: {
        //产出文件文件夹
        path: path.resolve(__dirname, "dist"),
        //产出文件的文件名
        filename: "bundle.js"
    },
    //实时监测文件更新,一旦文件更新了,就重新合并打包一份
    watch: true,
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.(c|le)ss$/, use: [
                    'style-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test: /\.png$/, use: [
                    'file-loader?name=[hash].[ext]'
                ]
            }
        ]
    },
    resolve: {
        /*
        细节坑。文档里面写着 需要resolve,引入图片生成的位置,
        不加这行会报错。因为github,Readme里面有这句话
        resolve contains location of where generated image is
        (要把生成的地址resolve到modules里面。不写就错)
        一定要加,血的教训啊
        */
        modules: ["node_modules", "./img"],
        extensions: [".js", ".json", ".jsx", ".css",".vue"]
    },
    stats: 'errors-only',
    // 定义一个插件数组。用来覆盖,在里面使用我们的主角
    plugins: [
        new SpritesmithPlugin({
            /*
            目标小图标,这里就是你需要整合的小图片的老巢。
            现在是一个个的散兵,把他们位置找到,合成一个
            */
            src: {
                cwd: path.resolve(__dirname, './img/sprites'),
                glob: '*.png'
            },
            // 输出雪碧图文件及样式文件,这个是打包后,自动生成的雪碧图和样式,自己配置想生成去哪里就去哪里
            target: {
                image: path.resolve(__dirname, './img/sprite.png'),
                css: [
                    // webpack,会自动生成一个sprite.css的样式
                    [path.resolve(__dirname, './img/sprite.less'),{
                        // 引用自己的模板
                        format: 'custom_sprites_template'
                    }]
                ]
            },
            // 自定义模板入口,我们需要基本的修改webapck生成的样式,上面的大函数就是我们修改的模板
            customTemplates: {
                'custom_sprites_template': customSpritesFunction
            },
            // 生成的图片在 API 中被引用的路径。
            //简单来说,就是你上面输出了 image 和 css ,那么在 css 用什么样的路径书写方式来引用 image 图片(可以是别名,或相对路径)
            // 不写就是全局的绝对图片路径
            apiOptions: {
                cssImageRef: '~sprite.png'
            },
            // 让合成的每个图片有一定的距离,否则就会紧挨着,不好使用
            spritesmithOptions: {
                padding: 20
            }
        }),
        new VueLoaderPlugin(),
        new CopyWebpackPlugin({
            patterns: [
                { from: __dirname + "/index.html", to:  __dirname + "/dist" }
            ],
        
        })
    ]
}

App.vue 使用:

 //引入node模块,一会要操作文件,输入,生成,输出都需要这玩意。
const path = require('path');
const{ writeFile } = require("fs");
// 主角,必须引入。要不然咋用
const SpritesmithPlugin = require('webpack-spritesmith');
// vue-loader
const VueLoaderPlugin = require('vue-loader/lib/plugin');
// copy插件,把一个文件或目录拷贝到另一个地方,这里用来拷贝index.html
// https://www.npmjs.com/package/copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin');


/* 
这里我们可以自己修改生成的模板样式,默认 webpack,会根据你的target里的配置,自动生成一个sprite.(c|le|sc)ss的样式文件
如果自动生成的不满意,
我们可以在这里修改,
可以自己打印一下 data里面的参数,看着就会大概明白(先看下面的配置,最后看这个模板)
这个文档也稍微有点用,放上来
https://github.com/qq20004604/webpack-study/tree/master/8%E3%80%81%E6%8F%92%E4%BB%B6/webpack-spritesmith
*/
const customSpritesFunction = function (data) {
    // 观察data 到底是个啥
    // console.log(data);
    writeFile("./data.json",JSON.stringify(data),function(err){
        if(!err){
            console.log("写入成功!");
        }
    });
    // 观察data发现是个对象其中属性items和属性sprites都是数组,而且内容相同,截取其数组第一个数据
    // 可以看到items和属性sprites里面存储的是图片的信息。宽高大小路径等各种参数
    /*{
        "x": 0,
        "y": 0,
        "width": 120,
        "height": 120,
        "name": "img_1x",
        "source_image": "/Users/lixinwei/Desktop/webpack-spritesmith-test/img/sprites/img_1x.png",
        "image": "~sprite.png",
        "escaped_image": "~sprite.png",
        "total_width": 260,
        "total_height": 260,
        "offset_x": 0,
        "offset_y": 0,
        "px": {
            "x": "0px",
            "y": "0px",
            "offset_x": "0px",
            "offset_y": "0px",
            "height": "120px",
            "width": "120px",
            "total_height": "260px",
            "total_width": "260px"
        }
    },
    */

    // data.sprites数组图片信息,根据图片信息自己把一些属性组合到对应图片的类名里面,
    return data.sprites.map(function (sprite) {
        return '.icon-NAME-box{\n\tdisplay: inline-block;\n\toverflow: hidden;\n\twidth: BOXWIDTHpx;\n\theight: BOXHEIGHTpx;\n}\n.icon-NAME{\n\tdisplay: inline-block;\n\twidth: WIDTHpx;\n\theight: HEIGHTpx;\n\tbackground-image: url(IMG);\n\tbackground-position: Xpx Ypx;\n\ttransform-origin: 0 0;\n\ttransform: scale(NUM);\n}'
            .replace(/IMG/g,sprite.image)
            .replace(/NAME/g, sprite.name)
            .replace(/WIDTH/g, sprite.width)
            .replace(/HEIGHT/g, sprite.height)
            .replace(/X/g, sprite.offset_x)
            .replace(/Y/g, sprite.offset_y)
            .replace(/BOXHEIGHT/g, sprite.name.indexOf('_2x') > -1 ? sprite.height / 2 : sprite.height)
            .replace(/BOXWIDTH/g, sprite.name.indexOf('_2x') > -1 ? sprite.width / 2 : sprite.width)
            .replace(/NUM/g, sprite.name.indexOf('_2x') > -1 ? 0.5 : 1);
    }).join('\n');
    // 最后三行replace就是根据图片名是否决定进行缩放
};

// 所有的配置都在这个导出里面
module.exports = {
    mode: "development",
    //配置入口文件
    entry: "./js/main.js",
    //配置产出文件
    output: {
        //产出文件文件夹
        path: path.resolve(__dirname, "dist"),
        //产出文件的文件名
        filename: "bundle.js"
    },
    //实时监测文件更新,一旦文件更新了,就重新合并打包一份
    watch: true,
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {
                test: /\.(c|le)ss$/, use: [
                    'style-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test: /\.png$/, use: [
                    'file-loader?name=[hash].[ext]'
                ]
            }
        ]
    },
    resolve: {
        /*
        细节坑。文档里面写着 需要resolve,引入图片生成的位置,
        不加这行会报错。因为github,Readme里面有这句话
        resolve contains location of where generated image is
        (要把生成的地址resolve到modules里面。不写就错)
        一定要加,血的教训啊
        */
        modules: ["node_modules", "./img"],
        extensions: [".js", ".json", ".jsx", ".css",".vue"]
    },
    stats: 'errors-only',
    // 定义一个插件数组。用来覆盖,在里面使用我们的主角
    plugins: [
        new SpritesmithPlugin({
            /*
            目标小图标,这里就是你需要整合的小图片的老巢。
            现在是一个个的散兵,把他们位置找到,合成一个
            */
            src: {
                cwd: path.resolve(__dirname, './img/sprites'),
                glob: '*.png'
            },
            // 输出雪碧图文件及样式文件,这个是打包后,自动生成的雪碧图和样式,自己配置想生成去哪里就去哪里
            target: {
                image: path.resolve(__dirname, './img/sprite.png'),
                css: [
                    // webpack,会自动生成一个sprite.css的样式
                    [path.resolve(__dirname, './img/sprite.less'),{
                        // 引用自己的模板
                        format: 'custom_sprites_template'
                    }]
                ]
            },
            // 自定义模板入口,我们需要基本的修改webapck生成的样式,上面的大函数就是我们修改的模板
            customTemplates: {
                'custom_sprites_template': customSpritesFunction
            },
            // 生成的图片在 API 中被引用的路径。
            //简单来说,就是你上面输出了 image 和 css ,那么在 css 用什么样的路径书写方式来引用 image 图片(可以是别名,或相对路径)
            // 不写就是全局的绝对图片路径
            apiOptions: {
                cssImageRef: '~sprite.png'
            },
            // 让合成的每个图片有一定的距离,否则就会紧挨着,不好使用
            spritesmithOptions: {
                padding: 20
            }
        }),
        new VueLoaderPlugin(),
        new CopyWebpackPlugin({
            patterns: [
                { from: __dirname + "/index.html", to:  __dirname + "/dist" }
            ],
        
        })
    ]
}

成功截图:


八、iconfont 的使用

结合 React 或 Vue 框架对 iconfont 的图标进行封装,就能像使用 Antd 的 Icon 组件一样使用 iconfont 的图标了,入门使用的话推荐 iconfont 使用教程,没有比这个官网教程更专业的了。

不过 React 在使用 symbol 方法用 iconfont 图标有个坑,不能使用,然后我去 iconFont 的 GitHub 仓库提问官方也不理会,只能自问自答了,React 框架中使用 symbol引用 出错

九、最后

现在我是更能体会上课听老师讲课,相比现在简直都要幸福死了,老师上课已有知识无脑传授,我们无脑接受,不用实践也不管自己会不会,能过考试就完了,考前都不鸟它考完就更不鸟了😂。Alas 为了弄明白这个知识点花了两天一夜,真是太费时间了。🤪

最近要换房子了,又是挺麻烦的事,我要是能搬到五道口北京林业大学旁边就好了😇。

当前时间 Tuesday, July 14, 2020 01:27:50

上一篇下一篇

猜你喜欢

热点阅读