Webpack实战——加载资源

加载图片

使用file-loader

file-loader可以把JavaScript和CSS中导入图片的语句替换成正确的地址,并同时把文件输出到对应的位置。例如CSS源码是这样写的:

#app {
  background-image: url(./imgs/a.png);
}

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

#app {
  background-image: url(5556e1251a78c5afda9ee7dd06ad109b.png);
}

并且在输出目录dist中也多出./imgs/a.png对应的图片文件5556e1251a78c5afda9ee7dd06ad109b.png,输出的文件名是根据文件内容的计算出的Hash值。
同理在JavaScript中导入图片的源码如下:

import imgB from './imgs/b.png';

window.document.getElementById('app').innerHTML = `
<img src="${imgB}"/>
`;

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

module.exports = __webpack_require__.p + "0bcc1f8d385f78e1271ebfca50668429.png";

也就是说imgB的值就是图片对应的URL地址。
在Webpack中使用file-loader非常简单,相关配置如下:

module.exports = {
  module: {
    rules: [
      {
        test: /\.png$/,
        use: ['file-loader']
      }
    ]
  }
};

使用 url-loader

url-loader 可以把文件的内容经过base64编码后注入到JavaScript或者CSS中去。
例如CSS源码是这样写的:

#app {
  background-image: url(./imgs/a.png);
}

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

#app {
  background-image: url(data:image/png;base64,iVBORw01afer...); /* 结尾省略了剩下的 base64 编码后的数据 */
}

同理在JavaScript中效果也类似。
从上面的例子中可以看出url-loader会把根据图片内容计算出的 base64 编码的字符串直接注入到代码中,由于一般的图片数据量巨大, 这会导致JavaScript、CSS文件也跟着变大。 所以在使用url-loader时一定要注意图片体积不能太大,不然会导致JavaScript、CSS文件过大而带来的网页加载缓慢问题。

一般利用url-loader把网页需要用到的小图片资源注入到代码中去,以减少加载次数。因为在HTTP/1协议中,每加载一个资源都需要建立一次HTTP链接, 为了一个很小的图片而新建一次HTTP连接是不划算的。

url-loader考虑到了以上问题,并提供了一个方便的选择limit,该选项用于控制当文件大小小于limit时才使用url-loader,否则使用fallback选项中配置的loader。 相关Webpack配置如下:

module.exports = {
  module: {
    rules: [
      {
        test: /\.png$/,
        use: [{
          loader: 'url-loader',
          options: {
            // 30KB 以下的文件采用 url-loader
            limit: 1024 * 30,
            // 否则采用 file-loader,默认值就是 file-loader 
            fallback: 'file-loader',
          }
        }]
      }
    ]
  },
};

除此之外,你还可以做以下优化:

以上加载图片的方法同样适用于其它二进制类型的资源,例如PDF、SWF等等。

加载 SVG

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

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

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

body {
  background-image: url(./svgs/activity.svg);
}

也可以在HTML中使用:

<img src="./svgs/activity.svg"/>

也就是说可以直接把SVG文件当成一张图片来使用,方法和使用图片时完全一样。 所以使用file-loader和使用url-loader对SVG来说同样有效,只需要把Loader test配置中的文件后缀改成.svg,代码如下:

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg/,
        use: ['file-loader']
      }
    ]
  },
};

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

使用 raw-loader

raw-loader 可以把文本文件的内容读取出来,注入到JavaScript或CSS中去。
例如在JavaScript中这样写:

import svgContent from './svgs/alert.svg';

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

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

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

window.document.getElementById('app').innerHTML = svgContent;

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

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        use: ['raw-loader']
      }
    ]
  }
};

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

使用 svg-inline-loader

svg-inline-loader 和上面提到的raw-loader非常相似, 不同在于svg-inline-loader会分析SVG的内容,去除其中不必要的部分代码,以减少SVG的文件大小。
在使用画图工具如Adobe Illustrator、Sketch制作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>

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

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

也就是说svg-inline-loader增加了对SVG的压缩功能。
使用svg-inline-loader时相关的Webpack配置如下:

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        use: ['svg-inline-loader']
      }
    ]
  }
};

加载 Source Map

由于在开发过程中经常会使用新语言去开发项目,最后会把源码转换成能在浏览器中直接运行的 JavaScript 代码。 这样做虽能提升开发效率,在调试代码的过程中你会发现生成的代码可读性非常差,这给代码调试带来了不便。

Webpack支持为转换生成的代码输出对应的Source Map文件,以方便在浏览器中能通过源码调试。 控制Source Map输出的Webpack 配置项是devtool,它有很多选项。

devtool 含义
不生成 Source Map
eval 每个 module 会封装到 eval 里包裹起来执行,并且会在每个eval语句的末尾追加注释 //# sourceURL=webpack:///./main.js
source-map 会额外生成一个单独Source Map文件,并且会在JavaScript文件末尾追加 //# sourceMappingURL=bundle.js.map
hidden-source-map source-map类似,但不会在JavaScript文件末尾追加 //# sourceMappingURL=bundle.js.map
inline-source-map 和 source-map 类似,但不会额外生成一个单独Source Map文件,而是把Source Map转换成base64编码内嵌到JavaScript中
eval-source-map 和 eval 类似,但会把每个模块的Source Map转换成base64编码内嵌到eval语句的末尾,例如 //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW...
cheap-source-map source-map类似,但生成的Source Map文件中没有列信息,因此生成速度更快
cheap-module-source-map cheap-source-map类似,但会包含Loader生成的Source Map

其实以上表格只是列举了devtool可能取值的一部分, 它的取值其实可以由source-mapevalinlinehiddencheapmodule这六个关键字随意组合而成。 这六个关键字每个都代表一种特性,它们的含义分别是:

  • eval:用eval语句包裹需要安装的模块;
  • source-map:生成独立的Source Map文件;
  • hidden:不在JavaScript文件中指出Source Map文件所在,这样浏览器就不会自动加载Source Map;
  • inline:把生成的Source Map转换成base64格式内嵌在JavaScript文件中;
  • cheap:生成的Source Map中不会包含列信息,这样计算量更小,输出的Source Map文件更小;同时Loader输出的Source Map不会被采用;
  • module:来自Loader的Source Map被简单处理成每行一个模块;

该如何选择

Devtool配置项提供的这么多选项看似简单,但很多人搞不清楚它们之间的差别和应用场景。

如果你不关心细节和性能,只是想在不出任何差错的情况下调试源码,可以直接设置成 source-map,但这样会造成两个问题:

  • source-map模式下会输出质量最高最详细的Source Map,这会造成构建速度缓慢,特别是在开发过程需要频繁修改的时候会增加等待时间;
  • source-map模式下会把 Source Map 暴露出去,如果构建发布到线上的代码的Source Map暴露出去就等于源码被泄露;

为了解决以上两个问题,可以这样做:

  • 在开发环境下把 devtool 设置成 cheap-module-eval-source-map,因为生成这种 Source Map 的速度最快,能加速构建。由于在开发环境下不会做代码压缩,Source Map 中即使没有列信息也不会影响断点调试;
  • 在生产环境下把 devtool 设置成 hidden-source-map,意思是生成最详细的 Source Map,但不会把 Source Map 暴露出去。由于在生产环境下会做代码压缩,一个 JavaScript 文件只有一行,所以需要列信息。

在生产环境下通常不会把Source Map上传到HTTP服务器让用户获取,而是上传到JavaScript错误收集系统,在错误收集系统上根据Source Map和收集到的JavaScript运行错误堆栈计算出错误所在源码的位置。
不要在生产环境下使用inline模式的Source Map, 因为这会使JavaScript文件变得很大,而且会泄露源码。

加载现有的 Source Map

有些从Npm安装的第三方模块是采用ES6或者TypeScript编写的,它们在发布时会同时带上编译出来的JavaScript文件和对应的Source Map文件,以方便你在使用它们出问题的时候调试它们;

默认情况下Webpack是不会去加载这些附加的Source Map文件的,Webpack只会在转换过程中生成 Source Map。 为了让Webpack加载这些附加的Source Map文件,需要安装 source-map-loader 。 使用方法如下:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        // 只加载你关心的目录下的 Source Map,以提升构建速度
        include: [path.resolve(root, 'node_modules/some-components/')],
        use: ['source-map-loader'],
        // 要把source-map-loader的执行顺序放到最前面,
        // 如果在source-map-loader之前有Loader转换了该 JavaScript文件,会导致Source Map映射错误
        enforce: 'pre'
      }
    ]
  }
};

由于source-map-loader在加载Source Map时计算量很大,因此要避免让该Loader处理过多的文件,不然会导致构建速度缓慢。通常会采用include去命中只关心的文件。
再安装新引入的依赖:

npm i -D source-map-loader

重启Webpack后,你就能在浏览器中调试node_modules/some-components/目录下的源码了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容

  • 版权声明:本文为博主原创文章,未经博主允许不得转载。 webpack介绍和使用 一、webpack介绍 1、由来 ...
    it筱竹阅读 11,142评论 0 21
  • webpack 是什么? 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(mo...
    IT老马阅读 3,321评论 2 27
  • 学习流程 参考文档:入门Webpack,看这篇就够了Webpack for React 一. 简单使用webpac...
    Jason_Zeng阅读 3,141评论 2 16
  • 前端将大型项目分成一个个单独的模块,一般封装好的每个模块都会实现一个目的明确的完成的功能。如何处理这些模块以及模块...
    pixels阅读 3,427评论 1 14
  • 让世界因我更闪亮,明光 20161021 今天的诵读是在大课间举行的,因为吃过早饭后课代表忘了发晨诵作业,我们没有...
    河南麦子的书写阅读 584评论 0 1