在项目中不可避免的会依赖图片资源,引入图片的方式有如下几种:

  • 在js中通过new Image()对象或者document.createElement('img')的形式引入;
  • 在css中通过background(url)的形式引入;
  • 直接在html中通过img标签引入。

file-loader

yarn add file-loader -D
1

需要注意:file-loader可以将引用文件打包到指定目录,同时获得文件的地址。可以处理任何文件,并不局限于图片。

我们这里用来处理图片,file-loader默认会将js和css文件中引入图片代码替换为正确的图片地址,同时将图片文件输出到打包目录下。

相关配置如下:

module.exports = {
    module: {
        rules: [
            {
                test: /\.(png|jpg|jpeg|gif)$/,
                use: {
                    loader: 'file-loader',
                    options: {
                        // 配置占位符,[name]是源文件名称,[ext]是源文件后缀
                        // [hash:6]是根据文件内容的计算出的Hash值的前6位,用来防止缓存
                        name: '[name]-[hash:6].min.[ext]' // 配置打包后的文件名称
                    }
                }
            }
        ]
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

在js中通过new Image()对象的形式引入

// 通过import语法把图片引入
// avatar返回的结果是一个新的图片地址
import avatar from './assets/avatar.jpg';
const image = new Image();
// console.log(avatar);
// image.src = './assets/avatar.jpg'; // 不能直接这样写,就是一个普通的字符串
image.src = avatar;
document.body.appendChild(image);
1
2
3
4
5
6
7
8

在css中通过backgroud(url)的形式引入

.box {
    width: 300px;
    height: 300px;
    background: url('../assets/avatar.jpg') no-repeat;
}
1
2
3
4
5

这种情况下,css-loader会将css里面的图片引用转化为require的格式,有了require,相当于引用了图片,webpack就会对相应的图片打包。例如上述代码实际上被转化为:

.box {
    width: 300px;
    height: 300px;
    background: url(require('../assets/avatar.jpg')) no-repeat;
}
1
2
3
4
5

被file-loader转换后输出的CSS会变成这样:

.box1 {
    background: url(/Users/liujie/study/Blog/前端相关/Webpack学习总结/webpack4-study/demo5/dist/avatar-9bf3b4.min.jpg) no-repeat;
}
1
2
3

并且在输出目录dist中也多出../assets/avatar.jpg对应的图片文件avatar-9bf3b4.min.jpg,输出的文件名的命名规则为:[name]-[hash:6].min.[ext]

  • name:原图片名称;
  • hash:6:根据文件内容的计算出的Hash值的前6位;
  • ext:原图片的后缀名

同理,在JS中导入图片的源码如下:

import avatar from './assets/avatar.jpg';

document.querySelector('.box2').innerHTML = `
    <img src='${avatar}' />
`;
1
2
3
4
5

经过file-loader处理后输出的JS代码如下:

/* 1 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__.p + "48c211196cf9b1f5b994e8bcf7a41763.jpg";

/***/ })
1
2
3
4
5
6

也就是说,avatar的值就是图片对应的URL地址。

直接在html中通过<img>标签引入

<body>
    <div class="box"></div>
    <div class="box2"></div>
    <img src="./assets/avatar.jpg">
</body>
1
2
3
4
5

需要使用html-withimg-loader进行处理:

yarn add html-withimg-loader -D
1
rules: [
    {
        test: /\.html/,
        use: ['html-withimg-loader']
    }
]
1
2
3
4
5
6

url-loader

url-loader可以将指定大小的图片经过base64编码后注入到JavaScript或者CSS中去。

举个🌰:

.box3 {
    background: url(../assets/images/test.jpg) no-repeat;
}
1
2
3

被url-loader转换后输出的CSS会变成这样:

.box3 {
    background: url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/
    /* 结尾省略了剩下的base64编码后的数据 */
}
1
2
3
4

从上面的例子中可以看出,url-loader会将根据图片内容计算出base64编码的字符串直接插入到对应的代码中。

合理使用file-loader和url-loader

  • file-loader:会把图片打包到dist目录下,对于小图片而言,会额外增加一次http请求,有点浪费;
  • url-loader:通过设置limit参数来合理控制图片的打包形式,小图片进行base64编码插入到代码中去以减少http请求数。因为在HTTP/1.x中,每加载一个资源都需要建立一次HTTP连接,对于小图片而言,新建一次HTTP连接是没有必要的。对于大图片则打包到dist目录下。但是需要注意:在使用url-loader时,图片体积限制不能设置太大,否则会导致JavaScript或者CSS文件过大而带来的页面加载缓慢的问题。

具体配置如下:

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: 'none',
    entry: {
        app: './src/index.js'
    },
    output: {
        publicPath: __dirname + '/dist/', // 静态资源引用路径或者CDN地址
        path: path.resolve(__dirname, 'dist'), // 打包文件的输出目录
        filename: '[name].bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            },
            {
                test: /\.(png|jpg|jpeg|gif)$/,
                use: {
                    // loader: 'file-loader',
                    // options: {
                    //     name: '[name]-[hash:6].min.[ext]'
                    // }
                    loader: 'url-loader',
                    options: {
                        name: '[name]-[hash:6].min.[ext]',
                        limit: 1024 * 30, // 30kb以下的文件采用url-loader转为base64编码
                        outputPath: 'assets/images/', // 指定打包后文件放到dist目录下的assets/images目录下
                        publicPath: 'www.baidu.com',
                        // 否则采用file-loader,默认值是file-loader
                        fallback: 'file-loader'
                    }
                }
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[id].css'
        })
    ]
};
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

options中的publicPath和outputPath

loader: 'url-loader',
options: {
    name: '[name]-[hash:5].min.[ext]',
    limit: 1024 * 30, // 30kb以下的文件采用url-loader转为base64编码,否则使用file-loader生成对应的图片文件
    outputPath: 'assets/images/', // 指定打包后文件放到dist目录下的assets/images目录下
    publicPath: 'www.baidu.com',
    // 否则采用file-loader,默认值也是file-loader
    fallback: 'file-loader'
}
1
2
3
4
5
6
7
8
9
  • outputPath:设置静态资源打包后存放的目录
  • publicPath:指定静态资源的CDN存放地址或者服务器上静态资源的存放地址

图片压缩(imagemin-webpack-plugin)

yarn add imagemin-webpack-plugin --dev
1

引包:

const ImageminPlugin = require('imagemin-webpack-plugin').default;
1

配置:

new ImageminPlugin({
    // disable: process.env.NODE_ENV !== 'production',
    pngquant: {
        quality: '80'
    }
})
1
2
3
4
5
6

压缩前:

压缩后:

自动合成雪碧图

yarn add postcss-loader postcss-sprite -D
1
const spritesConfig = {
    spritePath: './dist/assets/images/',  // 设置雪碧图的输出位置
    retina: true // 处理两倍图片的大小
};
{
    loader: 'postcss-loader',
    // 会把项目中用到的图片合并成雪碧图
    options: {
        ident: 'postcss',
        plugins: [require('postcss-sprites')(spritesConfig)]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
.sprite1 {
    background: url('../assets/douban.png');
}
.sprite2 {
    background: url('../assets/qq.png');
}
.sprite3 {
    background: url('../assets/weibo.png');
}
1
2
3
4
5
6
7
8
9

还可以通过webpack-spritesmith插件制作雪碧图。

雪碧图的实际应用

雪碧图是为了减少网络请求,所以被处理雪碧图的图片多为各式各样的 logo 或者大小相等的小图片。而对于大图片,还是不推荐使用雪碧图。 除此之外,雪碧图要配合css代码进行定制化使用。要通过css代码在雪碧图上精准定位需要的图片(可以理解成从雪碧图上裁取需要的图片)。

SVG打包处理

SVG好处

SVG作为矢量图的一种标准格式,已经得到了各大浏览器的支持,也成为了Web中矢量图的代名词。在网页中采用SVG代替位图有如下好处:

  • SVG比位图更清晰,在任意缩放的情况下都不会破坏图形的清晰度,能方便地解决高分辨率屏幕下图像显示不清楚的问题。
  • 在图形线条比较简单的情况下,SVG文件的大小要小于位图,在扁平化UI流行的今天,在多数情况下SVG会更小。
  • 图形相同的SVG比对应的高清图有更好的渲染性能。
  • SVG采用和HTML一致的XML语法描述,灵活性很高。

画图工具能导出一个个.svg文件,SVG的导入方法和图片类似,既可以像下面这样在CSS中直接使用:

.box {
    width: 200px;
    height: 200px;
    background: url(../assets/images/svg1.svg) no-repeat;
}
1
2
3
4
5

也可以在HTML中使用:

<img src="../assets/images/svg1.svg" />
1

也就是说,可以直接把SVG文件当成一张图片来使用,方法和使用图片时完全一样。

使用file-loader和使用url-loaderSVG来说同样有效,只需要把Loader test配置中的文件后缀改成.svg,具体webpack配置如下:

const path = require('path');

module.exports = {
    mode: 'none',
    entry: './src/index.js',
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        publicPath: __dirname + '/dist/'
    },
    module: {
        rules: [
            {
                test: /.svg$/,
                use: 'file-loader'
            },
            {
                test: /.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

index.css

.box {
    width: 200px;
    height: 200px;
    background: url(../assets/images/svg1.svg) no-repeat;
}
1
2
3
4
5

index.js

import './style/index.css';

console.log('hello svg');
1
2
3

index.html

<body>
    <div>
        <h2>加载svg了</h2>
        <div class="box"></div>
    </div>
    <script src="./dist/main.bundle.js"></script>
</body>
1
2
3
4
5
6
7

运行webpack打包:

7cbf77aab7630d15533e658baf364c90.png

publicPath(一般只用于生产环境中)

不配置publicPath:

f66aa822c72f5e3620791f4d0037f36b.png

配置publicPath:设置了打包后的静态资源加载目录。

7d240cb45944d9bf69b4b3373809bc75.png

由于SVG文本格式的文件,除了以上两种方法外还有其它方法,下面来一一说明。

使用Raw-Loader

raw-loader可以将文本文件的内容读取出来,注入到JavaScript或CSS中去。

例如,在index.js中这样写:

import svgContent from './assets/images/svg1.svg';
document.querySelector('.box').innerHTML = svgContent;
console.log('hello svg666');
1
2
3

经过raw-loader处理后输出的代码如下:

module.exports = "<svg xmlns=\"http://www.w3.org/2000/svg\"... </svg>" // 末尾省略 SVG 内容
1

也就是说,svgContent的内容就等于字符串形式的SVG,由于SVG本身就是 HTML元素,所以在获取到SVG内容后,可以直接通过以下代码将SVG插入到网页中:

document.querySelector('.box').innerHTML = svgContent;
1

使用raw-loader时相关的Webpack配置如下:

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        use: 'raw-loader'
      }
    ]
  }
};
1
2
3
4
5
6
7
8
9
10

如下图所示:.box的div里填充该svg图片。

a795217c32206ae7ec78aac346bb789f.png

需要注意:由于raw-loader会直接返回SVG的文本内容,并且无法通过CSS去展示SVG的文本内容,因此采用本方法后无法在CSS中导入SVG。也就是说,在CSS中不可以出现background-image: url(./svgs/activity.svg)这样的代码,因为background-image: url(<svg>...</svg>)是不合法的。

使用Svg-Inline-Loader(增加了对SVG的压缩功能)

svg-inline-loader和上面提到的raw-loader非常相似,不同之处在于:svg-inline-loader会分析SVG的内容,去除其中不必要的部分代码,以减少SVG的文件大小。

在使用画图工具如Adobe IllustratorSketch制作SVG后,在导出时这些工具会生成对网页运行来说不必要的代码。举个例子,以下是Sketch导出的SVG 的代码:

<svg class="icon" verison="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
     stroke="#000">
  <circle cx="12" cy="12" r="10"/>
</svg>
1
2
3
4

svg-inline-loader处理后会精简成如下:

<svg viewBox="0 0 24 24" stroke="#000"><circle cx="12" cy="12" r="10"/></svg>
1

也就是说svg-inline-loader增加了对SVG的压缩功能。

使用svg-inline-loader时相关的Webpack配置如下:

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        use: 'svg-inline-loader'
      }
    ]
  }
};
1
2
3
4
5
6
7
8
9
10