webpack是目前使用比较流行的一个前端模块打包器,前端的任何资源都被当成一个模块来处理,如图片、css文件等等。在基于webpack构建的前端项目中,一般都会配置有关css文件处理的规则,这其中也包括css文件中图片资源的处理,那么webpack到底是怎么处理它的呢?笔者之前也遇到过类似图片路劲的问题,为此还写过一篇博文webpack生成的css文件background-image url图片无法加载。今天就来说说webpack是怎么处理css文件中的图片路径的,首先上一个具体的例子。

一个具体问题

最近使用umi搭建前端的一个项目,在使用过程可能遇到一个umi的bug,为此还提出了一个issue 项目配置css module影响到css-loader对第三方库css文件中图片url的处理。顺便在简述下:

  • 在项目中通过设置cssLoaderOptions.modules为true来开启css module

  • 项目中引入了第三方库kindeditor的css文件。

    import 'kindeditor/themes/default/default.css'

    该default.css文件中有通过url引入图片资源,并且图片资源非相对路径写法,例如其中一处的写法:

      .ke-toolbar-icon-url {
    background-image: url(background.png);
    }

然后通过npm start开启本地服务进行预览时,编译报错,如下图:

奇怪,明明对应的图片资源是存在的,webpack编译时为啥找不到呢?苦苦寻思了一番没有找到答案,于是一头扎进webpack和css-loader源码的海洋中开启"寻宝"之旅。

不卖关子了,导致上述的直接原因:

css-loader没有对css文件的url方法进行处理(转化为相对路径)

这样导致webpack在整合经过loader处理后的default.css模块时,因为模块用到require(background.png)来引用图片资源,此时就用到Nodejs的模块加载机制,其具体可以查看本博客另一篇文章谈谈npm依赖管理,也可以查看nodejs官网module章节。nodejs在解析background.png图片路径时,会将其解析为第三方模块,这样会从node_modules中查找,通过在webpack中打印错误日志,可以从其中看出一些端倪,如下图,missing字段表示查找过的路径均没有找到对应的资源文件。

umi内部其实使用css-loader-1(fork css-loader@1.x而来)来处理css文件的, 导致css-loader没有对css文件图片路径进行处理的底层原因:

项目开启css module后,不该影响到node_modules中css文件的css module的情况而实际上产生了影响;导致没有对第三方库中的css文件中图片路径进行处理

webpack是怎么转化css中的图片路径的?

如果单纯为了解决上面问题就可以到此为止,但是处于好奇,毕竟被坑了几次,想知道webpack是怎么处理css文件中的图片路径的。例如我们在项目中这样写过css:

.xxa {
background: url(background.png)
}

或者这样:

.xxb {
background: url(~alias/background.png)
}

在项目中,不论我们用less、stylus还是sass等css预处理库编写css,其最终是通过对应的loader如less-loader将编写的样式转换变换为css,然后通过css-loader来处理css中有关路径的转换,其作用拿其官网的介绍来说:

The css-loader interprets @import and url() like import/require() and will resolve them.

最常见处理css样式的项目,一般经过以下几个loader从右到左顺序执行,拿less编写的样式来说,以内联loader的展示形式来说明:

!!css-loader!post-loader!less-loader!./xxx/xx.less

当然css-loader处理后还要经过style-loader或者mini-css-extract-plugin提供的loader处理,但是这不在本次谈论范围。

下面通过一幅图来看看经过webpack解析模块到css产出这一过程,webpack帮我们做了什么。

具体就来简单分析整个流程,可能分析有不正确的地方,还请大家批评指正

NormalModuleFactory解析并创建模块

  • 首先从入口文件(entry配置的文件)开始构建,使用NormalModuleFactory来解析并创建模块

  • NormalModuleFactory使用enhance-resolve来解析依赖的模块绝对地址,如果模块地址解析错误就会如文章开头的问题抛出错误,解析正确则会创建依赖模块。如上图中的./index.css,模块地址解析成功后,webpack为index.css创建的模块属性如下图:

    创建的一个模块,一般包括模块的type、context、request、userRequest、rawRequest、resource、dependencies和loaders等模块相关信息。

    模块创建后,会用其依赖处理的loader来编译模块内容,模块依赖的loader存放在模块的loader属性数组中;对于css文件最后是用css-loader来处理。

css-loader编译css文件

css-loader官网说的会对css文件的url/@import进行处理,但是具体实现细节并没有详细阐述。下面来简单说说对css-loader的主要功能:

  • 转换css中的url@importrequire/import

    例如url中的地址(绝对地址除外)会被解析为相对地址,防止webpack在解析模块地址时出错;这其中包括webpack alias别名组成的地址和node_moduels库中地址。顺便说下:

    css-loader内部是通过postcss生成css的ast并遍历找出其url方法来完成转换的

  • 按comonjs模块的形式生成css文件模块内容

    css文件最终转换后的commonjs模块形式,模块的后缀还是.css,其内容如下图所示:

  • css-loader还处理css module,也是通过遍历css的ast来完成转换

这样通过css-loader完成了css文件中图片url路径的转换,有助于webpack寻找图片资源的具体位置。

url-loader处理图片资源

其实,在css-loader处理完css模块过程中,会再次通过NormalModuleFactory来解析并创建其内部的图片模块,webpack模块对应的属性如下图:

生成图片对应的commonjs模块内容可能为base64的内容,如下图:

也可能为图片资源产出的引用地址,如下图:

这主要取决于url-loader在处理图片资源时是否指定limit配置项值,该值会跟图片内容大小进行比较。在limit值小于图片内容大小时,则使用file-loader来实现图片提出到webpack编译产出的对应位置下。

上面图片模块内容为图片产出地址,正是file-loader处理的结果,其实现以下几项功能:

  • 图片内容会抽离到webapck的编译产出位置。

  • 按照loader的name配置和webpack的publicPath配置项生成最终的url。

    因为图片的内容被抽离掉,那么webpack生成的图片模块内容应该为该图片的引用地址。这涉及到两部分

    • 根据file-loader的name配置项生成相对地址部分。

      如下file-loader配置项:

    {
    test: /\.(png|jpe?g|gif)$/i,
    loader: 'file-loader',
    options: {
    name: 'static/[name].[hash:8].[ext]',
    },
    }

    然后根据loader-utilsinterpolateName方法解析对应的url。例如上面css文件的图片地址转换结果:

    ./background.png会转换为: static/background.a9153e95.png

    • 根据webpackConfig.output.publishPath生成图片的引用地址。

      最终生成的地址为:

      __webpack_public_path__ + "static/background.a9153e95.png";

经过上面步骤的处理,我们看到产出的最终css文件的效果如下图:

顺便说一下,如果产出的css文件经过mini-css-extract-plugin提供的loader进行统一抽离,那么它也可能会影响css文件中图片的引用路径,尤其该loader配置了publicPath内容,如下面loader的配置:

{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: 'public/path/to/'
}
}

那么css文件中图片的路径最终结果如下图:

这就是webapck通过各种loader处理css中图片路劲的过程,通过这一过程我们可能只是大概对这一过程有一个大概的认知,如果要深入理解还是需要花时间研究。

参考文献

透过现象看webpack处理css文件中图片路径转换的具体过程的更多相关文章

  1. webpack打包 css文件里面图片路径 替换位置

    { test: /\.css$/, use: ExtractTextPlugin.extract({ use: ['css-loader?minimize', 'autoprefixer-loader ...

  2. 用extract-text-webpack-plugin提取出来的css文件中背景图片url的不正确的问题

    在一个main.js中require一个scss文件,scss文件中用了背景图片,图片url是用的相对路径,用extract-text-webpack-plugin插件提取出的css文件背景图片路径不 ...

  3. webpack模块加载css文件及图片地址

    webpack支持css文件加载并打包,只需安装相应加载器并在配置文件中配置 . 加载的css文件内容会与该模块里的js内容混合封装,这样做的好处是一个js文件包含了所有的css与js内容,有效减少了 ...

  4. webpack抽取CSS文件与CSSTreeShaking

    webpack抽取CSS文件 CSSTreeShaking 一.webpack抽取CSS文件 抽取CSS文件的插件:mini-css-extract-plugin npm install --save ...

  5. webpack(5)webpack处理css文件

    css文件处理-准备工作 (以下项目配置都是基于上一篇webpack(4)的基础上) 在项目开发中,我们必然需要添加很多的样式,而样式我们往往写到一个单独的文件中. 这里我们就在src目录中创建一个n ...

  6. 怎样将多个CSS文件导入一个CSS文件中

    问题: 在HTML中引入css的其中的两个方法:    导入式和链接式的目的都是将一个独立的css文件引入一个文件中,二者的区别不大,事实上,二者最大的区别在于链接式使用html的标记引入外部css文 ...

  7. webpack快速入门——CSS分离与图片路径处理

    1.在终端安装extract-text-webpack-plugin 2.引入插件 const extractTextPlugin = require("extract-text-webpa ...

  8. JAVA文件中获取路径及WEB应用程序获取路径方法

    JAVA文件中获取路径及WEB应用程序获取路径方法 1. 基本概念的理解 `绝对路径`:你应用上的文件或目录在硬盘上真正的路径,如:URL.物理路径 例如: c:/xyz/test.txt代表了tes ...

  9. css和javascript中图片路径的不同

    之前在写前端代码时,在图片路径的设置那里经常会遇到一个问题.比方说,我 (1)在根目录下面新建了个"images"文夹,里面放了张图片top.gif (2)在根目录下另外新建了两个 ...

随机推荐

  1. 记一次linux磁盘清理 - 已经删除的文件占用了大量磁盘空间

    今天开发环境磁盘占满了,导致开发环境上的 nginx .redis 等组件总是报异常. 跳到系统根目录下 cd / 检查磁盘占用情况 df -h 哇,40G硬盘全用完了.看看是哪些文件占了那么多内存. ...

  2. HashMap、lru、散列表

    HashMap HashMap的数据结构:HashMap实际上是一个数组和链表("链表散列")的数据结构.底层就是一个数组结构,数组中的每一项又是一个链表. hashCode是一个 ...

  3. win32简单的sockeTCP协议通信

    什么也不说了看代码 首先是服务端代码 #include <stdio.h> #include <stdlib.h> #include <WinSock2.h> #i ...

  4. $Noip2011/Luogu1314$ 聪明的质监员 二分+巧妙前缀和

    $Luogu$ $Sol$ 首先$W$一定是某个$w_i$.于是一种暴力方法就出炉了,枚举$W$再计算. 注意到,满足$S-Y$的绝对值最小的$Y$只可能是两种,一种是$<S$的最大的$Y$,一 ...

  5. 菜鸟系列Fabric源码学习 — MVCC验证

    Fabric 1.4 源码分析 MVCC验证 读本节文档之前建议先查看[Fabric 1.4 源码分析 committer记账节点]章节. 1. MVCC简介 Multi-Version Concur ...

  6. javascript-void keyword

    javascript-void keyword 写在前面 ECMA-262定义了ECMAScript所支持的关键字(keyword),关键字不能用作ECMAScript程序的标识符(Indetifie ...

  7. Angular Schematics 三部曲之 Add

    前言 因工作繁忙,差不多有三个月没有写过技术文章了,自八月份第一次编写 schematics 以来,我一直打算分享关于 schematics 的编写技巧,无奈还是拖到了年底. Angular Sche ...

  8. Keil uVision4的简单使用

    1. 项目创建 打开安装好的Keil uVision4可以看到如下界面 选择New uVision Project来创建一个新项目 选择项目存放的位置并为项目文件命名 选择要进行模拟仿真的设备(此处以 ...

  9. iOS - 点击背景视图收起系统键盘

    我们在 IOS 开发中经常会需要在输入框输入数据后,需要收起系统键盘,比如由于手机屏幕不是很大,可能由于输入信息后,系统键盘就会遮挡住下一步的按钮,而系统键盘有没有收起键,所以我们可以实现点击背景视图 ...

  10. Flink系列之Time和WaterMark

    当数据进入Flink的时候,数据需要带入相应的时间,根据相应的时间进行处理. 让咱们想象一个场景,有一个队列,分别带着指定的时间,那么处理的时候,需要根据相应的时间进行处理,比如:统计最近五分钟的访问 ...