Data URLs 与 url-loader

除了file-loader这种通过copy物理文件的形式去处理文件资源以外还有一种通过date url的形式去表示文件。这种方式也非常常见。

Data URLs是一种特殊的url协议,可以用来直接去表示一个文件,传统的url要求服务器上有一个对应的文件,然后通过请求这个地址,得到服务器上对应的文件。

而Data URLs是一种当前url就可以去表示文件内容的方式,也就是说这种url当中的文本就已经包含了文件的内容。我们在使用这种url 的时候就不会再去发送任何的http请求。

data:[mediatype][;base64],<data>

data: -> 协议
[mediatype][;base64], -> 媒体类型和编码
<data> -> 文件内容

例如我们下面给出的这一段Data URLs浏览器可以根据这个url解析出来这是一个html类型的文件内容,编码是url-8,内容是一段包含h1的html代码。

data:text/html;charset=UTF-8,<h1>html content</h1>

如果是图片或者字体这一类无法通过文本去表示的2进制类型的文件。可以通过将文件的内容进行base64编码,以base64编码过后的结果也就是一个字符串,去表示这个文件内容。例如下面这样一段Data URLs

data:image/png;base64,iVBORw0KGgoAAAANSUhE...SuQmCC

这个url就是表示了一个png类型的文件, 文件的编码是base64,后面就是我们这张图片的base64编码。

当然了一般情况下我们base64的编码会比较长,浏览器同样可以解析出对应的文件。

在webpack打包静态资源模块时,我们同样可以使用这种方式去实现,那通过data urs我们就可以以代码的形式去表示任何类型的文件。具体做法就是我们需要用到一个专门的加载器,叫做 url-loader

yarn add url-loader --dev

在webpack的配置文件中找到之前的file-loader,将其修改为url-loader

const path = require('path');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'url-loader'
            }
        ]
    }
}

此时webpack再去打包时,再遇到.png文件就会使用url-loader将其转换为Data URLs的形式。我们重新打包,可以发现,dist目录下已经没有png文件了,打开bundle.js文件查看。可以发现在最后的文件模块中,导出的不再是之前的文件路径,而是一个完整的Data URLs。

/* 6 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ("data:image/png;base64,iVBORw0KGgoAAAANSUh...AAAABJRU5ErkJggg==");

/***/ })

我们刚刚也了解到了,因为这个Data URLs当中就已经包含了文件内容,所以说我们就不需要再有独立的物理文件了。我们可以尝试着把这个完整的Data URLs复制下来,打开浏览器去尝试访问这个地址,这张图片同样可以正常显示出来。

我们启动服务,可以发现,同样可以正常显示。

data:image/png;base64,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

这种方式十分适合项目当中体积比较小的资源,因为如果说体积过大的话就会造成打包结果非常大,从而影响运行速度。

最佳的实践方式应该是对项目中的小文件通过url-loader转换成Data URLs然后在代码当中去嵌入,从而减少应用发送请求次数。对于较大的文件仍然通过传统的file-loader方式以单个文件方式去存放,从而提高应用的加载速度。

url-loader支持通过配置选项的方式去实现我们刚刚说的这种最佳实践的方式,在配置文件中,具体的做法就是将url-loader字符串配置方式修改为一个对象的配置方式,对象中使用loader定义url-loader,然后额外添加一个options属性用来去为他添加一些配置选项,其实每一个需要配置的loader都是通过这种方式去配置的。

这里为url-loader添加一个叫做limit的属性,将其设置为 10kb(10 * 1024), 这里的单位是字节,所以需要乘以1024。

这样我们url-loader就会只将10kb以下的文件转换成Data URLs,将超过10kb的文件,仍然会交给file-loader去处理。

const path = require('path');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        limit: 10 * 1024
                    }
                }
            }
        ]
    }
}

重新进行打包,可以测试效果,如果是小于10kb的图片就会转换为Data URLs嵌入代码中去工作,如果是超过10kb的图片就会单独存放。这种方式就是实现我们刚刚所说的最佳实践了。

这里特别需要注意的是,如果按照这种方式去使用url-loader的话就一定要同时安装file-loader因为url-loader对于超出的文件还是会调用file-loader这个模块。例如我们删除掉file-loader重新打包,那么命令行就会报一个找不到file-loader的错误,这里需要注意。

yarn remove file-loader --dev
yarn webpack

常用加载器

资源加载器有点像我们工厂里面的车间,是用来处理和加工我们打包过程中所遇到的资源文件,那除了以上介绍到的加载器,社区当中还有许多其他的加载器。

对于常用的loader之后我们基本都会用到,但是在这之前呢为了可以更好的了解他们我们先将loader大致分为三类,做一个归纳。

首先就是最常见的编译转换类型的加载器,这种类型的loader会把我们加载到的资源模块转换为JavaScript代码,例如之前用到的css-loader,就是将css代码转换为了bundle当中一个js模块,从而去实现通过JavaScript去运行我们的css。

其次就是文件操作类型的文件加载器,通常文件类型的加载器都会把加载到的资源模块拷贝到输出目录,同时又将这个文件的访问路径向外导出,例如之前用的file-loader就是一个非常典型的文件操作加载器。

最后还有一种针对代码质量检查的加载器,就是对我们所加载到的资源文件,一般是代码,去进行校验的一种加载器(eslint-loader),那这种加载器呢他的目的是为了统一我们的代码风格,从而去提高代码质量,那这种类型加载器一般不会去修改我们生产环境的代码。

以上三种就是我们针对于常用的loader做的一个归纳,后续我们在接触到一个loader过后呢,我们需要先去明确他到底是一个什么样的加载器,那他的特点是什么,使用又需要注意什么。

处理ES2015

由于webpack默认就可以处理我们代码中的import和export,所以很自然的会有人认为,webpack会自动编译ES6的代码,实则不然,webpack仅仅是对于模块去完成打包工作,所以说他才会对我们代码中的import和export做一些相应的转换,除此之外它并不能转换我们代码中其他的ES6代码。

我们可以将main.js中的var修改为const,执行打包,打开bundle.js可以发现,const并没有被webpack额外处理。

如果我们需要webpack在打包过程中同时处理其他ES6特性的转换,我们需要为js文件配置一个额外的编译型loader,例如最常见的就是babel-loader。

我们先通过yarn去安装babel-loader, 由于babel-loader需要依赖额外的babel核心模块,所以我们需要安装@babel/core模块和用于完成具体特性转换插件的一个集合叫做@babel/preset-env。

yarn add babel-loader @babel/core @babel/preset-env --dev

安装完成过后我们回到我们的配置文件中,这里我们为我们的js文件指定加载器为babel-loader, 这样的话babel-loader就会取代默认的加载器,在打包过程当中呢,就会帮我们处理我们代码当中的一些新特性了。

const path = require('path');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.js$/,
                use: 'babel-loader'
            }
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'url-loader'
            }
        ]
    }
}

回到命令行当中,再次运行打包命令,打包过后我们查看bundle.js可以发现,const特性仍然没有被转换。其实这个原因我们前面已经介绍过了,因为babel严格上来说只是转换ES代码的一个平台。我们需要基于babel这样一个平台去通过插件来去转换我们代码当中一些具体的特性。

所以我们需要为babel去配置他需要使用的插件,回到配置文件当中,这里我们给babel-loader传入相应的配置就可以了,这里我们直接使用preset-env这样一个插件集合,因为这个集合当中就已经包含了全部的ES最新特性。

const path = require('path');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            }
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'url-loader'
            }
        ]
    }
}

完成以后我们再次回到命令行终端,重新打包,打包之后我们再去找到我们生成的代码,此时我们代码中的ES2015的一些新特性都被转换了。

总结一下就是,webpack默认只是一个打包工具,他不会去处理我们代码当中一些ES6或者更高版本的一些新特性,如果说你需要去处理这些新特性,我们可以通过为我们js代码单独去配置单独的加载器去实现。

模块加载方式

除了代码中的import可以去触发模块的加载,webpack中还提供了其他几种方式,具体就是以下这几种。

首先第一个就是我们一直在用的也是最常用到的一种方式就是遵循ES Module标准的import声明。

import heading from './heading.js';
import icon from './icon.png';

其次就是遵循CommonJS标准的require函数,不过如果你去通过require函数去载入一个ES Module的话,那你需要注意,对于ES Module的默认导出,我们需要通过导入,也就是require这个函数导入的结果的default属性去获取。

const heading = require('./heading.js').default;
const icon = require('./icon.png');

最后呢,遵循AMD标准的define函数和require函数,webpack也同样是支持的。

define(['./heading.js', './icon.png', './style.css'], (createHeading, icon) => {
    const heading = createHeading();
    const img = new Image();
    img.src = icon;
    document.body.append(heading);
    document.body.append(icon);
});

require(['./heading.js', './icon.png', './style.css'], (createHeading, icon) => {
    const heading = createHeading();
    const img = new Image();
    img.src = icon;
    document.body.append(heading);
    document.body.append(icon);
})

那换句话说webpack兼容多种模块化标准,不过以个人经验来说,除非必要的情况,否则一定不要在项目中去混合使用这些标准,如果混合用的话,可能会大大降低我们项目的可维护性,我们每个项目只需要去统一使用一个统一的标准就可以了。

除了JavaScript代码中的这三种方式以外,还有一些加载器他在工作时也会去处理我们所加载到的资源当中一些导入的模块,例如我们css-loader加载的css文件(@import指令和url函数)

在这样的文件当中,我们import指令和部分属性中的url函数,他们也会去触发相应的资源模块加载。

@import '';

还有像html-loader去加载的html文件当中的一些src属性也会去触发相应的模块加载,针对于这两种方式我们需要一起去尝试一下。

首先我们先来尝试一下在样式文件当中导入资源模块, 我们现在入口文件导入样式文件。

main.js

import './main.css';

在main.css中我们通过background-image添加一张背景图片

body {
    min-height: 100vh;
    background-image: url(background.png);
    background-size: cover;
}

webpack在遇到css文件时会使用css-loader进行处理,处理的时候发现css中有引入图片,就会将图片作为一个资源模块加入到打包过程。webpack会根据配置文件当中,针对于遇到的文件去找到相应的loader,此时这张图片是一张png图片,就会将这个图片交给url-loader去处理。

打包过后,启动服务,可以发现图片是可以正常显示的。这就表示在css-loader加载的样式文件当中url函数确实可以触发模块的加载。

样式文件当中除了属性文件使用的url函数还有import指令,他同样支持加载其他的样式资源模块。新建一个reset.css文件,随便写点什么,然后在main.css中引入该文件。

@import url(reset.css);
body {
    min-height: 100vh;
    background-image: url(background.png);
    background-size: cover;
}

重新打包运行,可以发现reset.css已经工作了,这也就意味着,刚刚的reset.css确实被打包进了我们bundle文件。

以上就是css-loader再去加载样式时,样式文件当中会去触发文件加载的两种方式。

接下来我们再来看看html文件当中加载资源的一些方式,因为html文件当中也会有引用其他文件的可能性,例如img标签的src,所以说我们这里需要再去尝试一下。

我们在项目中添加一个src/footer.html文件,在这个文件中我们只是通过简单的img标签去引入一张图片。

<footer>
    <img src="better.png" />
</footer>

完成以后我们再回到打包入口当中。我们需要导入footer.html文件,这样他才能参与我们webpack打包的过程,我们同样使用import去打包这个文件, 不过html文件默认会将html代码作为默认导出,所以我们这里需要接收一下导出的字符串。然后我们将它通过document.write的方式将它输出到页面。

import './main.css';

import footer from './footer.html';

document.write(footer);

那这个html模块准备好了过后,我们还需要为这个html模块配置对应的loader,否则webpack是没办法认识html模块的,我们通过yarn去安装一个html-loader

yarn add html-loader --dev

安装之后打开webpack的配置文件,为扩展名为html的文件去配置使用这个loader, 完成之后打包重启。可以看到html代码中的img标签所对应的图片可以正常显示出来。这就说明html文件当中src属性也可以去触发资源模块的加载。

const path = require('path');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            }
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'url-loader'
            },
            {
                test: /.html$/,
                use: 'html-loader'
            }
        ]
    }
}

但是我们在html文件当中不仅仅只有img的src需要去依赖资源,其他的标签也有可能需要一些资源的依赖,例如a标签的href属性。

比如a标签点击需要去加载应用当中的一个文件,这个地方我们就来尝试一下,我们回到html中去添加一个a标签, 然后我们将这个a标签的href属性指向一个文件资源。

<footer>
    <!-- <img src="better.png" /> -->
    <a href="better.png">download png</a>
</footer>

我们重新打包,启动服务,回到浏览器,刷新过后我们发现去点击这个a标签过后我们确找不到这个a标签所对应的这个文件。针对于这个问题是因为html-loader默认只会去处理img标签的src属性,如果我们需要其他标签的一些属性也能够触发打包的话,我们可以去相应的一些配置

具体的做法就是给html-loader添加一个attrs的一个属性, 也就是我们html加载的时候对页面上的属性做额外的处理,这个属性默认当中只有img:src,也就是表示img标签的src属性。

那我们就根据这样一个使用的规范,我们去添加一个a:href属性,让他去能支持我们a标签的href属性。

const path = require('path');

module.exports = {
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, 'dist'),
        publicPath: 'dist/'
    },
    module: {
        rules: [
            {
                test: /.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            }
            {
                test: /.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            },
            {
                test: /.png$/,
                use: 'url-loader'
            },
            {
                test: /.html$/,
                use: {
                    loader: 'html-loader',
                    options: {
                        attrs: ['img:src', 'a:href']
                    }
                }
            }
        ]
    }
}

完成以后我们在此回到命令行中运行打包。在打包的结果中我们就可以看到a标签用到的资源已经参与了打包。回到浏览器刷新页面,点击a标签就可以正常找到依赖文件。

通过刚刚的这些尝试我们发现,几乎我们在代码当中所有需要引用到的资源,就是有引用资源的可能性的地方,都会被webpack找出来,然后根据我们的配置,交给不同的loader去处理,最后将处理的结果整体打包到输出目录。webpack就是通过这样一个特点去实现我们整个项目的模块化。

webpack模块化加载方式