Skip to content

图片处理

本节来学习,如何在webpack中使用图片,并且能够让webpack打包图片。

实现图片的加载

图片的引用方式一般有三种

  1. 在js中创建图片来引入
  2. 在css中引入,比如 background('url')
  3. 在html中使用img标签引用,比如<img src='xxx' alt=''>

在js中创建图片

修改index.js,先尝试通过字符串引用,是不会生效的

js
// webpack-dev-1\src\index.js
// 在js中创建图片来引用
let image = new Image()
image.src = './logo.png' // 如果直接通过字符串引用,是没有效果的
// 也会被看成是个字符串
document.body.appendChild(image)

我们可以打包看看一下效果

image-20220113115944804

并没有真正的引用这张图片。因为图片并没有被打包。

如果想要被引用过来,需要有个导入关系,使用require语法,或者import,

再次修改index.js

js
// webpack-dev-1\src\index.js
import logo from './logo.png'
// 把图片引入,返回结果是一个新的图片地址
// 我们引的这个logo.png,内部发射出来一个新的文件名字,也是一个路径,是以hash戳的命名方式

console.log(logo);
// 在js中创建图片来引用
let image = new Image()
// image.src = './logo.png' // 如果直接通过字符串引用,是没有效果的
// 也会被看成是个字符串

image.src = logo
document.body.appendChild(image)

运行的话,会报错,告诉我们需要一个合适的loader去解析,

image-20220113120634954

安装一下 file-loader,

shell
yarn add file-loader@^3.0.1 -D

file-loader

file-loader的作用是会在内部生成一张新的图片到打包的目录中,然后把生成的图片名字返回过来。

修改配置文件

js
// webpack-dev-1\webpack.config.js
// webpack是node写出来的,所以使用node的语法

let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 引入插件
let MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入插件

let OptimizeCss = require('optimize-css-assets-webpack-plugin') // 引入插件 压缩css的
let UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 压缩js文件的插件


module.exports = {
    // 配置优化项
    optimization: {
        minimizer: [ // 是一个数组,还得优化js
            new UglifyJsPlugin({
                cache: true, // 是否用缓存
                parallel: true, // 是否并发压缩
                sourceMap: true,// 线上用来调试的映射文件
            }),
            new OptimizeCss(), // 开启优化css后,生产模式css文件也会被压缩,但是必须配置js压缩,如果不配置,js生产模式就不会被压缩了
        ]
    },

    devServer: { // 开发服务器的配置
        port: 3000, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './build', // 以哪个文件夹作为服务的根目录 
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩

    },

    mode: 'development', // 模式,默认两种模式 production 和 development

    entry: './src/index.js', // 入口

    output: {
        filename: 'bundle.js', // 打包后的文件名
        path: path.resolve(__dirname, 'build'), // 打包后的路径,必须是一个绝对路径
    },


    plugins: [ // 是一个数组,放着所有的webpack插件
        // 插件是一个类,通过new的方式来引用
        new HtmlWebpackPlugin({
            template: './src/index.html', // 告诉插件,是以这个目录下的index.html为模板
            filename: 'index.html', // 告诉插件,打包后的文件叫什么名字,这里也叫index.html
            hash: true, // 引用的时候可以加一个hash戳
        }),
        // 插件的使用就没有先后顺序了,随便放就行
        // 引入抽离css样式的插件,
        new MiniCssExtractPlugin({
            filename: 'main.css', // 告诉插件,抽离出的样式文件的名字叫什么,这里叫main.css
        }),
    ],
    module: { // 模块
        rules: [ // 规则,在这里面配置各种loader
            {
                test: /.(jpg|png|gif)$/, // 处理图片的loader
                use: 'file-loader'
            },
            {
                test: /\.js$/, // 匹配以js结尾的文件
                use: {
                    loader: 'babel-loader',
                    options: { // 用babel-loader 需要把ES6转为ES5
                        // 配置可以写在这里,还可以写在外面
                        // 在这里添加一个预设
                        presets: [ // 这是一个大插件的集合
                            '@babel/preset-env', // 这个插件就可以把ES6转ES5
                        ],
                        // 如果有一些预设的属性,需要配置一些小插件来转换还不是标准的js语法
                        plugins: [
                            ["@babel/plugin-proposal-decorators", { "legacy": true }],
                            ['@babel/plugin-proposal-class-properties', { "loose": true }],
                            "@babel/plugin-transform-runtime"
                        ]
                    }
                },
                include: path.resolve(__dirname, 'src'), // 只找src目录下的js  包括
                exclude: /node_modules/ // node_modules文件夹下的文件不用找  排除
            },

            // css-loader 解析css文件,@import语法的
            // style-loader 把解析后的css文件 插入到head标签中
            // loader有个特点,希望单一,一个loader干一件事
            /* 
                loader的用法
                1. 只用字符串,就是只用一个loader
                2. 多个loader,需要一个数组 [],数组里可以放字符串,或者对象,对象的话就可以配置loader的参数了
            */
            // loader的顺序,默认是从右向左执行,从下往上执行
            {
                test: /\.css$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                ]
            },
            {
                test: /\.less$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader, // 若果想抽离多个文件,可以在new一个出来,一个抽离css一个抽离less都行
                    // 这里就用一个了
                    'css-loader', // 解析 @import语法 解析 css
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                    'less-loader' // 把less 转换为 css
                ]
            },
        ],
    }
}

我们打包看一下,发现带hash戳的新的图片已经生成到build目录了。

image-20220113121257787

我们运行一下看看,成功了

image-20220113121356970

在css文件中引入图片 background: url('xxx')

我们修改一下index.css文件

css
/* webpack-dev-1\src\index.css */
.bg-logo {
    width: 100px;
    height: 100px;
    background: url('./logo.png');
}

因为之前我们已经使用过了css-loader,它会自动的将css中的background: url('./logo.png');这种写法,改为require的写法,所以最后也能找到打包后的图片。

修改index.html

html
<body>
    <div>内容区域</div>
    <div class="bg-logo"></div>
</body>

修改index.js

js
// webpack-dev-1\src\index.js
import './index.css' // 引入index.css
import logo from './logo.png'
// 把图片引入,返回结果是一个新的图片地址
// 我们引的这个logo.png,内部发射出来一个新的文件名字,也是一个路径,是以hash戳的命名方式

console.log(logo);
// 在js中创建图片来引用
let image = new Image()
// image.src = './logo.png' // 如果直接通过字符串引用,是没有效果的
// 也会被看成是个字符串

image.src = logo
document.body.appendChild(image)

运行一下看下效果,发现是好使的

image-20220113122318777

直接在html中使用img标签引用

修改index.html

html
<body>
    <img src="./logo.png" alt="">
    <div>内容区域</div>
    <div class="bg-logo"></div>
</body>

这样引的话,最后还是一串字符串,会找不到这个文件

image-20220113122634796

并没有找到,需要另外一个loader,将html页面中的图片链接,也解析成require的形式,

html-withimg-loader

html-withimg-loader是一个中国人写的,就是专门干这件事的,将html页面中的img标签的图片解析成require后的路径。

安装一下

shell
yarn add html-withimg-loader@^0.1.16 -D

修改配置文件,配置一下这个loader

js
// webpack-dev-1\webpack.config.js
// webpack是node写出来的,所以使用node的语法

let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 引入插件
let MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入插件

let OptimizeCss = require('optimize-css-assets-webpack-plugin') // 引入插件 压缩css的
let UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 压缩js文件的插件


module.exports = {
    // 配置优化项
    optimization: {
        minimizer: [ // 是一个数组,还得优化js
            new UglifyJsPlugin({
                cache: true, // 是否用缓存
                parallel: true, // 是否并发压缩
                sourceMap: true,// 线上用来调试的映射文件
            }),
            new OptimizeCss(), // 开启优化css后,生产模式css文件也会被压缩,但是必须配置js压缩,如果不配置,js生产模式就不会被压缩了
        ]
    },

    devServer: { // 开发服务器的配置
        port: 3000, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './build', // 以哪个文件夹作为服务的根目录 
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩

    },

    mode: 'development', // 模式,默认两种模式 production 和 development

    entry: './src/index.js', // 入口

    output: {
        filename: 'bundle.js', // 打包后的文件名
        path: path.resolve(__dirname, 'build'), // 打包后的路径,必须是一个绝对路径
    },


    plugins: [ // 是一个数组,放着所有的webpack插件
        // 插件是一个类,通过new的方式来引用
        new HtmlWebpackPlugin({
            template: './src/index.html', // 告诉插件,是以这个目录下的index.html为模板
            filename: 'index.html', // 告诉插件,打包后的文件叫什么名字,这里也叫index.html
            hash: true, // 引用的时候可以加一个hash戳
        }),
        // 插件的使用就没有先后顺序了,随便放就行
        // 引入抽离css样式的插件,
        new MiniCssExtractPlugin({
            filename: 'main.css', // 告诉插件,抽离出的样式文件的名字叫什么,这里叫main.css
        }),
    ],
    module: { // 模块
        rules: [ // 规则,在这里面配置各种loader
            {
                test: /\.html$/, // 解析html中的img标签图片路径
                use: 'html-withimg-loader'
            },
            {
                test: /.(jpg|png|gif)$/, // 处理图片的loader
                use: 'file-loader'
            },
            {
                test: /\.js$/, // 匹配以js结尾的文件
                use: {
                    loader: 'babel-loader',
                    options: { // 用babel-loader 需要把ES6转为ES5
                        // 配置可以写在这里,还可以写在外面
                        // 在这里添加一个预设
                        presets: [ // 这是一个大插件的集合
                            '@babel/preset-env', // 这个插件就可以把ES6转ES5
                        ],
                        // 如果有一些预设的属性,需要配置一些小插件来转换还不是标准的js语法
                        plugins: [
                            ["@babel/plugin-proposal-decorators", { "legacy": true }],
                            ['@babel/plugin-proposal-class-properties', { "loose": true }],
                            "@babel/plugin-transform-runtime"
                        ]
                    }
                },
                include: path.resolve(__dirname, 'src'), // 只找src目录下的js  包括
                exclude: /node_modules/ // node_modules文件夹下的文件不用找  排除
            },

            // css-loader 解析css文件,@import语法的
            // style-loader 把解析后的css文件 插入到head标签中
            // loader有个特点,希望单一,一个loader干一件事
            /* 
                loader的用法
                1. 只用字符串,就是只用一个loader
                2. 多个loader,需要一个数组 [],数组里可以放字符串,或者对象,对象的话就可以配置loader的参数了
            */
            // loader的顺序,默认是从右向左执行,从下往上执行
            {
                test: /\.css$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                ]
            },
            {
                test: /\.less$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader, // 若果想抽离多个文件,可以在new一个出来,一个抽离css一个抽离less都行
                    // 这里就用一个了
                    'css-loader', // 解析 @import语法 解析 css
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                    'less-loader' // 把less 转换为 css
                ]
            },
        ],
    }
}

重新运行一下,发现好使了

image-20220113123109127

图片加载的优化,小图片转base64

有时候我们不想因为图片的加载,向服务器发送那么多的http的请求,就可以在打包的时候将小的图片直接转换为base64,

这时候有个loader就可以干这件事,一般情况下,图片都是使用这个loader来加载的,并不会使用刚才的file-loader。

url-loader

安装一下

shell
yarn add url-loader@^1.1.2 -D

修改配置文件,刚file-loader替换为url-loader

js
// webpack-dev-1\webpack.config.js
// webpack是node写出来的,所以使用node的语法

let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 引入插件
let MiniCssExtractPlugin = require('mini-css-extract-plugin') // 引入插件

let OptimizeCss = require('optimize-css-assets-webpack-plugin') // 引入插件 压缩css的
let UglifyJsPlugin = require('uglifyjs-webpack-plugin') // 压缩js文件的插件


module.exports = {
    // 配置优化项
    optimization: {
        minimizer: [ // 是一个数组,还得优化js
            new UglifyJsPlugin({
                cache: true, // 是否用缓存
                parallel: true, // 是否并发压缩
                sourceMap: true,// 线上用来调试的映射文件
            }),
            new OptimizeCss(), // 开启优化css后,生产模式css文件也会被压缩,但是必须配置js压缩,如果不配置,js生产模式就不会被压缩了
        ]
    },

    devServer: { // 开发服务器的配置
        port: 3000, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './build', // 以哪个文件夹作为服务的根目录 
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩

    },

    mode: 'development', // 模式,默认两种模式 production 和 development

    entry: './src/index.js', // 入口

    output: {
        filename: 'bundle.js', // 打包后的文件名
        path: path.resolve(__dirname, 'build'), // 打包后的路径,必须是一个绝对路径
    },


    plugins: [ // 是一个数组,放着所有的webpack插件
        // 插件是一个类,通过new的方式来引用
        new HtmlWebpackPlugin({
            template: './src/index.html', // 告诉插件,是以这个目录下的index.html为模板
            filename: 'index.html', // 告诉插件,打包后的文件叫什么名字,这里也叫index.html
            hash: true, // 引用的时候可以加一个hash戳
        }),
        // 插件的使用就没有先后顺序了,随便放就行
        // 引入抽离css样式的插件,
        new MiniCssExtractPlugin({
            filename: 'main.css', // 告诉插件,抽离出的样式文件的名字叫什么,这里叫main.css
        }),
    ],
    module: { // 模块
        rules: [ // 规则,在这里面配置各种loader
            {
                test: /\.html$/, // 解析html中的img标签图片路径
                use: 'html-withimg-loader'
            },
            {
                test: /.(jpg|png|gif)$/,
                // 做一个限制,当图片小于 多少k的时候,用base64来转化
                // 如果大于这个限制,就会使用file-loader来去解析图片,将图片打包到build目录下
                use: {
                    loader: 'url-loader',
                    options: {
                        limit: 200 * 1024, // 200K
                        // 如果图片小于200K的话,全部变为base64
                        // 否则使用file-loader 产出真实的图片
                    }
                },
            },
            /* {
                test: /.(jpg|png|gif)$/, // 处理图片的loader
                use: 'file-loader'
            }, */
            {
                test: /\.js$/, // 匹配以js结尾的文件
                use: {
                    loader: 'babel-loader',
                    options: { // 用babel-loader 需要把ES6转为ES5
                        // 配置可以写在这里,还可以写在外面
                        // 在这里添加一个预设
                        presets: [ // 这是一个大插件的集合
                            '@babel/preset-env', // 这个插件就可以把ES6转ES5
                        ],
                        // 如果有一些预设的属性,需要配置一些小插件来转换还不是标准的js语法
                        plugins: [
                            ["@babel/plugin-proposal-decorators", { "legacy": true }],
                            ['@babel/plugin-proposal-class-properties', { "loose": true }],
                            "@babel/plugin-transform-runtime"
                        ]
                    }
                },
                include: path.resolve(__dirname, 'src'), // 只找src目录下的js  包括
                exclude: /node_modules/ // node_modules文件夹下的文件不用找  排除
            },

            // css-loader 解析css文件,@import语法的
            // style-loader 把解析后的css文件 插入到head标签中
            // loader有个特点,希望单一,一个loader干一件事
            /* 
                loader的用法
                1. 只用字符串,就是只用一个loader
                2. 多个loader,需要一个数组 [],数组里可以放字符串,或者对象,对象的话就可以配置loader的参数了
            */
            // loader的顺序,默认是从右向左执行,从下往上执行
            {
                test: /\.css$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                ]
            },
            {
                test: /\.less$/,
                use: [
                    // 这个插件上有个loader,我们不想再用style-loader把样式放在style标签里了,所以就用它的loader
                    MiniCssExtractPlugin.loader, // 若果想抽离多个文件,可以在new一个出来,一个抽离css一个抽离less都行
                    // 这里就用一个了
                    'css-loader', // 解析 @import语法 解析 css
                    // 应该在解析css之前增加前缀
                    'postcss-loader',
                    'less-loader' // 把less 转换为 css
                ]
            },
        ],
    }
}

我们打包来看一下结果,发现都变为了base64格式,配置成功。

image-20220113124023511

base64的优缺点

不会去请求服务器了,减少服务请求。

但是源文件会比之前大三分之一。

总结

在webpack中加载图片资源的三种方式

  1. 使用js创建图片,对应的src应该使用require新生成的路径,并且需要配置file-loader
    1. file-loader会将原来的图片在打包后的目录中新生成一份,并返回新图片的路径,以hash戳来命名的。
  2. 在css文件中引入的图片,比如background: url('xx')
    1. 之前配置的css-loader会将路径解析成require之后的新路径,所以打包后也能访问到
  3. 在html文件中使用img标签来访问
    1. 需要配置html-withimg-loader,会将img标签中的路径解析成require后的路径,所以最终也能访问到
  4. 一般来说,我们打包图片都会使用 url-loader
    1. url-loader可以配置一个属性,对于小于多少K的图片可以转换为base64格式,超过这个大小的图片会使用file-loader打包
    2. 所以可以对图片请求做一次优化,减少http请求。
    3. 但是base64会比源文件大三分之一。

参考

https://www.bilibili.com/video/BV1a4411e7Bz?p=10&spm_id_from=pageDriver