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

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 [email protected]而来)来处理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中图片路劲的过程,通过这一过程我们可能只是大概对这一过程有一个大概的认知,如果要深入理解还是需要花时间研究。

参考文献

原文地址:https://www.cnblogs.com/wonyun/p/12261101.html

时间: 2024-08-06 02:51:53

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

透过现象看本质——回头再看Nginx(进程模型、异步非阻塞、源码目录结构)

透过现象看本质--回头再看Nginx Nginx的进程模型 ? 使用过nginx的朋友都知道nginx的性能很高,而其原因可能少有人知.首先,nginx的架构就奠定了其高性能的基础.那么就先来看看nginx的基础架构吧,如下图所示:(不能完全理清楚所有内容也没关系,因为本小节讲述的主要内容是Nginx的进程模型) ? 本小节先来说说Nginx基础架构中的进程模型: ? 所谓进程模型,即Nginx响应请求或服务时程序运行(机器执行指令集)的方式,一般在nginx服务启动后,在Unix系统中会以da

透过现象看本质——Nginx模块

透过现象看本质--Nginx模块 前言 ? 上篇文章主要讲述了有关Nginx的主配置文件,为什么需要非常关注Nginx的主配置文件呢?这是因为它是我们与nginx的核心,这里的核心不是说nginx的内核部分,而是人机交互的核心,除非基于nginx做二次开发,例如开发第三方模块,一般我们只需要使用nginx实现我们的业务需求.无论是web网站.负载均衡还是反向代理,都需要对其主配置文件熟悉,这样才能玩好其优化. ? 本文就接着来讲述有关nginx模块的相关内容. Nginx核心工作者--模块 ?

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

webpack支持css文件加载并打包,只需安装相应加载器并在配置文件中配置 . 加载的css文件内容会与该模块里的js内容混合封装,这样做的好处是一个js文件包含了所有的css与js内容,有效减少了http请求次数,显著提高了页面响应性能的用户体验. 加载css文件时,如果css里含有图片的引用地址,编译时webpack会将图片资源处理并输出到设置的publicPath参数位置,该参数可以是以页面为基准的相对地址,也可以是以根目录为基准的绝对地址.url-laoder会在这个地址下兴建一个im

透过现象看本质——谈谈ML2 plugin这回事儿

透过现象看本质--谈谈ML2 plugin这回事儿 本文关键词:OpenStack.Neutron Plugin.Neutron Agent.Core Plugin.ML2插件.ML2架构.Driver.紧耦.解耦. 前言 ? 在OpenStack中,其控制管理着计算.存储.网络三大资源.要想明白OpenStack是如果对计算.存储和网络资源进行管理的,就需要清楚OpenStack的架构,模块组成和各自分工的任务等等. ? 而网络是作为OpenStack中最为核心之一的.也是相对于其他最为复杂的

透过现象看本质 大数据核心并不在规模大

透过现象看本质 大数据核心并不在规模大谆籽做谞谞诅资祝仔渍庄昨赚缀阻透过现象看本质 大数据核心并不在规模大 http://www.songtaste.com/user/10226369/info http://www.songtaste.com/user/10226373/info http://www.songtaste.com/user/10226374/info http://www.songtaste.com/user/10226382/info http://www.songtaste

javascript 在js文件中获取路径

如果在*.js文件中获取当自己当前的路径是很重要的. 举个例子,如果一个css文件中引用图片,如background-img: url('./Images/bg.png').那么图片的路径,是相对于css文件而言的. 但是,如果我们再js文件中引用图片,如img.src = './images/bg.png';  但是,图片却不是根据js的相对路径的.而是根据(引用该js)的html的相对路径来决定的.这显然不合理. 所以如果我们能获取js文件的绝对路径的话,就会好很多.譬如 img.src =

kindeditor文本编辑器删除文本中图片路径出错

string[] imgname; MODEL.Strategy modelMenu = bllMenu.GetModel(int.Parse(strId)); imgname = getPicUrl.getPicUrls(modelMenu.SContent).Split('|'); foreach (string c in imgname) { ImageHelper.DeleteImg(HttpContext.Current.Server.MapPath(c)); } kindeditor

css文件中的样式类被覆盖,js文件中的变量未定义问题问题

Extjs控件中css样式表中的样式类部分被莫名其妙的覆盖 问题原因: 为什么呢? 因为在调用组件W的css样式时,我们自己写了css样式A,Ext组件又自带css样式B,A是我们用cls:'A'放进去.那么这个W到底是用两个样式重复的哪一个种呢? 这就要看css样式表的引入顺序了,如果先引入自己写的,再引入Ext自带的,那么浏览器先读自己写的css样式,然后再读Ext自带的样式.结果我们的自己写的css样式就被覆盖了. 解决办法: 这样就对了. js文件中的变量未定义问题 问题描述: read

Vue系列之 => webpack处理css文件

处理css文件 安装 npm i style-loader css-loader -D main.js import $ from 'jquery' //Es6中导入模块的方式 import './css/index.css' // import './css/index.css' webpack默认只能打包处理js类型文件 //如果需要处理非js类型的文件,我们需要手动安装一些合适的第三方loader加载器 // 1.打包处理css文件,需要安装 npm i style-loade css-l