Webpack入门,资源处理流程

在前几篇的文章中,梳理了Webpack的模块打包功能。可以把Webpack打包模块理解成类似于工厂中的产品组装,也就是把一个个零部件拼起来得到最终的成品。接下来的几篇,则主要要关注资源的输入和输出,即产品的原材料从哪里来,以及组装后的产品送到哪里去。

资源处理流程

在Webpack中,开发者需要在流程的最开始,指定一个或多个入口(entry),也就是告诉Webpack具体从源码目录下的哪个文件开始打包。如果把工程中各个模块的依赖关系当作一颗树,那么入口就是这颗依赖树的根。

这些存在依赖关系的模块会在打包时被封装为一个chunk(代码块)。chunk(代码块)在Webpack中可以理解成被抽象和包装后的一些模块。它就像一个装着很多文件的文件袋,里面的文件就是各个模块,Webpack在外面加了一层包裹,从而形成了chunk。根据具体配置不同,一个工程打包时可能会产生一个或多个chunk。通常情况下,将由chunk得到的打包产物称为bundle。

Webpack入门,资源处理流程
entry、chunk、bundle的关系

配置资源入口

Webpack通过context和entry这两个配置项来共同决定入口文件的路径。在配置入口时,实际上是做了两件事:

  1. 确定入口模块位置,告诉webpack从哪里开始进行打包;
  2. 定义chunk name。如果工程只有一个入口,那么默认其chunk name为main;如果工程有多个入口,开发者需要为每个入口定义chunk name,来作为该chunk的唯一标识。

content可以理解为资源入口的路径前缀,在配置时要求必须使用绝对路径的形式。

// 下面两种配置效果相同,入口均为<工程根路径>/src/script/index.js
module.exports = {
  context: path.join(__dirname,"./src"),
  entry: "./script/index.js",
}

module.exports = {
  context: path.join(__dirname, "./scr/script"),
  entry: "./index.js"
}

配置context的主要目的是让entry的编写更加简洁,尤其是在多入口的情况下。

提示

context可以省略,默认值为当前工程的根目录,且context的值只能是字符串。

entry与context不同,entry的配置(值)可以有多种形式:字符串、数组、对象、函数。开发者可以根据不同的场景来选择entry的配置类型。

// 直接传入文件路径
module.exports = {
  entry: "./src/index.js",
  output; {
    filename: "bundle.js",
  },
  mode: "development",
}

// 数组类型入口
// 传入一个数组的作用是将多个资源预先合并,这样Webpack在打包时会将数组中的最后一个元素作为实际的入口路径。
module.exports = {
  entry: ['babel-polyfill','./src/index.js'],
  ...
}

// 对象类型入口
// 如果想要定义多个入口,则必须使用对象的形式。对象的属性名(key)是chunk name,属性值(value)是入口路径。
module.exports = {
  entry; {
    // chunk name为index,入口路径为./scr/index.js
    index: "./src/index.js",
    
    // chunk name为lib,入口路径为./src/lib.js
    lib: "./src/lib.js"
  },
  ...
}
// entry对象得属性值也可以为字符串或数组。
module.exports = {
  entry: {
    index: ['babel-polyfill','./src/index.js'],
    lib: './src/lib.js',
  },
}
// 在使用字符串或数组定义单入口时,我们没有办法更改chunk name,只能用默认得main。在使用对象来定义多入口时,则必须为每一个入口定义chunk name。

// 函数类型入口
// 开发者可以使用匿名函数来定义入口。即只要使匿名函数的返回值为上面介绍的任何配置形式即可。使用函数的优点在于开发者可以添加一些代码逻辑来动态地定义入口值。函数也支持返回一个Promise对象来进行异步操作。
// 返回一个字符串的入口
module.exports = {
  entry: () => './scr/index.js',
  ...
}
// 返回一个对象型的入口
module.exports = {
  entry: () => ({
    index: ['babel-polyfill','./src/index.js'],
    lib: './src/index.js',
  }),
  ...
}
// 返回Promise对象
module.exports = {
  entry: () => new Promise((resolve) => {
    // 模拟异步操作
    setTimeout(() => {
      resolve('./src/index.js');
    },1000);
  }),
  ...
}

对于单页应用(SPA),一般情况下定义单一入口即可。无论是框架、库,还是各个页面的模块,都由app.js单一的入口进行引用。这样做的好处是只会产生一个JS文件,依赖关系清晰。但是这种做法也有弊端,所有的模块都打包到一起,当应用的规模上升到一定程度之后会导致产生的资源体积过大,降低用户的页面渲染速度。

提示

在Webpack默认配置中,当一个bundle大于250KB时(压缩前),Webpack会发出警告。

为了解决这个问题,开发者可以使用提取vendor的方法。vendor的字面意思是“供应商”,在Webpack中则一般指工程所用的库、框架等第三方模块集中打包而产生的bundle。

module.exports = {
  context: path.join(__dirname,'./src'),
  entry: {
    app: './src/app.js',
    vendor: ['react','react-dom','react-router'],
  },
}

在上面的配置中,app.js仍然和最开始一样,其内容也不需要做任何改变,只是添加了一个新的chunk name为vendor的入口,并通过数组的形式把工程所依赖的第三方模块放了进去。

对于多页应用(MPA),为了尽可能减小资源的体积,开发者需要每个页面只加载各自必要的逻辑,而不是将所有页面打包到一个bundle中,也就是说,每个页面都需要有一个独立的bundle。

module.export = {
  entry: {
    pageA: './src/pageA.js',
    pageB: './src/pageB.js',
    pageC: './src/pageC.js',
    // 对于多页应用,同样可以使用提取vendor的方法,将各个页面之间的公共模块进行打包。
    vendor: ['react','react-dom'],
  }
  ...
}

在上面的配置中,入口与页面是一一对应的关系,这样每个HTML只要引入各自的JS就可以加载其所需要的模块。

配置资源出口

在Webpack中与资源出口相关的配置都集中在output对象中。output对象里可以包含数十个配置项,常用的配置项也就以下几个。

filename,控制输出资源的文件名,其值为字符串。

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: 'main.js',
    // filename的值中也可包含路径信息
    // filename: './js/main.js'
  }
}

在多入口的场景中,需要为对应产生的每个bundle指定不同的名字。Webpack支持使用一种类似模板语言的形式动态地生成文件名。

module.exports = {
  entry: {
    index: './src/app.js',
    vendor: './src/vendor.js',
  },
  output : {
    filename: '[name].js',
  }
}

在资源输出时,上面配置的filename中的[name]会被替换为chunk name,因此最后项目中实际生成的资源是vendor.js与app.js。

除了[name]可以指代chunk name以外,还有其他几种模板变量可以用于filename的配置中。

[contnethash]:指代当前chunk单一内容的hash;
[chunkhash]: 指代当前chunk内容的hash;
[id]:指代当前chunk的id;

使用变量一般有如下两种作用。

  • 当有多个chunk存在时对不同的chunk进行区分。[name]、[chunkhash]和[id],它们对于每个chunk来说都是不同的。
  • 实现客户端缓存。[contenthash]和[chunkhash]都与chunk内容直接相关,使用了这些变量后,当chunk的内容改变时,资源文件名也会随之更改,从而使用户在下一次请求资源文件时会立即下载新的版本,而不会使用本地缓存。

path,指定资源输出的位置,要求值必须为绝对路径。

const path = require('path');
module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname,'dist'),
  }
}

提示

在Webpack4之后,output.path已经默认是dist目录,除非需要更改它,否则不必单独配置。

publicPath,指定资源的请求位置。与path不同,资源的请求位置指由JS或CSS所请求的间接资源路径。

页面中的资源分两种,一种是由HTML页面直接请求的,比如通过script标签加载的js;另一种是由JS或CSS来发起请求的间接资源,如图片、字体等(也包括异步加载的JS)。

// 假设当前HTML地址为http://example.com/app/index.html
module.exports = {
  entry: './src/index.js',
  output: {
    publicPath: '', // 实际资源路径为http://example.com/app/
    // publicPath: '/assets', // 实际资源路径为http://example.com/assets/
    // publicPath: '/js', // 实际资源路径为http://example.com/app/js/
    // publicPath: './js', // 实际资源路径为http://example.com/app/js/
    // publicPath: '../assets', // 实际资源路径为http://example.com/assets/
    // publicPath: 'http://cdn.com/', // 实际资源路径为http://cdn.com/
  }
}

原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/webpack-resources.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
ZEROZERO
上一篇 2022年11月14日
下一篇 2022年11月21日

相关推荐

  • Java自学之继承

    在面向对象的设计过程中,类是基本的逻辑单位。但是对于这些基本的逻辑单位需要考虑到重用的设计问题,所以在面向对象的设计里提供有继承,并利用这一特点实现类的可重用性定义。 类继承定义 …

    2020年12月3日
    1.4K
  • spring boot练习篇之用户登录系统【接入数据库】

    抛弃JSP,只做纯粹的前后端分离项目。 写在前面 学习基础知识是枯燥无味的,之所以会这样,多数是因为心不静,对于如何运用它,感到茫然。所以建议大家在学习Java基础知识的时候,一定…

    2021年5月28日
    1.3K
  • 函数防抖与函数节流

    函数防抖 定义 触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间;更直白一点就是:一个需要频繁触发的函数,在规定时间内,只让最后一次生效,…

    2020年7月17日
    1.5K
  • vue3.0项目如何配置路径别名

    vue更新到3.0以后,在项目中已经深度集成了webpack,使用vue create命令新建项目之后,已经没有webpack配置文件了,这对于像小编这样没有系统学习过前端的同学来…

    2020年8月22日
    4.1K
  • Webpack入门,预处理器

    一个Web工程通常会包含HTML、JS、CSS、图片、字体等多种类型的静态资源,且这些资源之间都存在着某种联系。对于Webpack来说,所有这些静态资源都是模块,开发者可以像加载一…

    2022年11月21日
    700
  • Webpack入门,模块打包原理分析

    面对工程中成百上千个模块,Webpack究竟是如何将它们有序地组织在一起,并按照开发者预想的顺序运行在浏览器上的呢?本篇文章将通过一个简单的示例。分析一下Webpack模块打包的原…

    2022年11月14日
    816
  • js数组去重(区分object、“NaN”、NaN)

    数组去重在前端面试中比较常见,今天来复习复习。 对这样一个数组进行去重,我尝试了几种方法,大多数不能对对象去重,或者不能区分true和”true”、NaN和…

    2021年2月23日
    1.3K
  • CSS布局之圣杯与双飞翼布局

    所谓圣杯布局和双飞翼布局其实解决的问题是相同的,都是解决左右两栏固定宽度,中间部分自适应,其中某部分内容比其他内容高的时候,保证三者元素等高。他俩的区别就是:圣杯用padding。…

    2019年6月18日
    1.9K
  • 如何搭建MyBatis开发环境

    进入一段时间的学习及温习,已经可以说是初步掌握了Javaweb入门开发,由于我的中心思想是抛弃JSP,做纯粹的前后端分离项目,所以接下来计划学习持久层开发,现在主流的持久层开发工具…

    2022年4月6日
    802
  • Java自学之泛型

    在Java语言中,为了方便接收参数类型的统一,提供了核心类Object,利用此类对象可以接收所有类型的数据(包括基本数据类型和引用数据类型)。但是由于其所描述的数据范围过大,所以在…

    2020年12月8日
    1.3K