Webpack笔记二

85 阅读7分钟

配置文件

webpack提供的cli支持很多的参数,例如--mode,但更多的时候,我们会使用更加灵活的配置文件来控制webpack的行为

默认情况下,webpack会读取webpack.config.js文件作为配置文件,但也可以通过CLI参数--config来指定某个配置文件

配置文件中通过CommonJS模块导出一个对象,对象中的各种属性对应不同的webpack配置

注意:配置文件中的代码,必须是有效的node代码

当命令行参数与配置文件中的配置出现冲突时,以命令行参数为准。

基本配置:

  1. mode:编译模式,字符串,取值为development或production,指定编译结果代码运行的环境,会影响webpack对编译结果代码格式的处理
  2. entry:入口,字符串(后续会详细讲解),指定入口文件
  3. output:出口,对象(后续会详细讲解),指定编译结果文件

devtool 配置

source map 源码地图

本小节的知识与 webpack 无关

前端发展到现阶段,很多时候都不会直接运行源代码,可能需要对源代码进行合并、压缩、转换等操作,真正运行的是转换后的代码

2020-01-08-16-38-26.png

这就给调试带来了困难,因为当运行发生错误的时候,我们更加希望能看到源代码中的错误,而不是转换后代码的错误

jquery压缩后的代码:code.jquery.com/jquery-3.4.…

为了解决这一问题,chrome浏览器率先支持了source map,其他浏览器纷纷效仿,目前,几乎所有新版浏览器都支持了source map

source map实际上是一个配置,配置中不仅记录了所有源码内容,还记录了和转换后的代码的对应关系

下面是浏览器处理source map的原理

2020-01-08-16-58-06.png

2020-01-08-17-01-13.png

最佳实践

  1. source map 应在开发环境中使用,作为一种调试手段
  2. source map 不应该在生产环境中使用,source map的文件一般较大,不仅会导致额外的网络传输,还容易暴露原始代码。即便要在生产环境中使用source map,用于调试真实的代码运行问题,也要做出一些处理规避网络传输和代码暴露的问题。

webpack中的source map

使用 webpack 编译后的代码难以调试,可以通过 devtool 配置来优化调试体验

具体的配置见文档:www.webpackjs.com/configurati…

module.exports = {
    mode: "production",
    devtool: "source-map"
}

webpack 编译过程 {ignore}

webpack 的作用是将源代码编译(构建、打包)成最终代码

2020-01-09-10-26-15.png

整个过程大致分为三个步骤

  1. 初始化
  2. 编译
  3. 输出

2020-01-09-10-53-28.png

初始化

此阶段,webpack会将CLI参数配置文件默认配置进行融合,形成一个最终的配置对象。

对配置的处理过程是依托一个第三方库yargs完成的

此阶段相对比较简单,主要是为接下来的编译阶段做必要的准备

目前,可以简单的理解为,初始化阶段主要用于产生一个最终的配置

编译

  1. 创建chunk

chunk是webpack在内部构建过程中的一个概念,译为,它表示通过某个入口找到的所有依赖的统称。

根据入口模块(默认为./src/index.js)创建一个chunk

2020-01-09-11-54-08.png

每个chunk都有至少两个属性:

  • name:默认为main
  • id:唯一编号,开发环境和name相同,生产环境是一个数字,从0开始
  1. 构建所有依赖模块

2020-01-09-12-32-38.png

AST在线测试工具:astexplorer.net/

简图

2020-01-09-12-35-05.png

  1. 产生chunk assets

在第二步完成后,chunk中会产生一个模块列表,列表中包含了模块id模块转换后的代码

接下来,webpack会根据配置为chunk生成一个资源列表,即chunk assets,资源列表可以理解为是生成到最终文件的文件名和文件内容

2020-01-09-12-39-16.png

chunk hash是根据所有chunk assets的内容生成的一个hash字符串 hash:一种算法,具体有很多分类,特点是将一个任意长度的字符串转换为一个固定长度的字符串,而且可以保证原始内容不变,产生的hash字符串就不变

简图

2020-01-09-12-43-52.png

  1. 合并chunk assets

将多个chunk的assets合并到一起,并产生一个总的hash

2020-01-09-12-47-43.png

输出

此步骤非常简单,webpack将利用node中的fs模块(文件处理模块),根据编译产生的总的assets,生成相应的文件。

2020-01-09-12-54-34.png

总过程

2020-01-09-15-51-07.png

2020-01-09-12-32-38.png

涉及术语

  1. module:模块,分割的代码单元,webpack中的模块可以是任何内容的文件,不仅限于JS
  2. chunk:webpack内部构建模块的块,一个chunk中包含多个模块,这些模块是从入口模块通过依赖分析得来的
  3. bundle:chunk构建好模块后会生成chunk的资源清单,清单中的每一项就是一个bundle,可以认为bundle就是最终生成的文件
  4. hash:最终的资源清单所有内容联合生成的hash值
  5. chunkhash:chunk生成的资源清单内容联合生成的hash值
  6. chunkname:chunk的名称,如果没有配置则使用main
  7. id:通常指chunk的唯一编号,如果在开发环境下构建,和chunkname相同;如果是生产环境下构建,则使用一个从0开始的数字进行编号

入口和出口

2020-01-09-15-51-07.png

node内置模块 - path: nodejs.org/dist/latest…

出口

这里的出口是针对资源列表的文件名或路径的配置

出口通过output进行配置

入口

入口真正配置的是chunk

入口通过entry进行配置

规则:

  • name:chunkname
  • hash: 总的资源hash,通常用于解决缓存问题
  • chunkhash: 使用chunkhash
  • id: 使用chunkid,不推荐
var path = require("path")

module.exports = {
    mode: "production",
    entry: {
        main: "./src/index.js", //属性名:chunk的名称, 属性值:入口模块(启动模块)
        a: ["./src/a.js", "./src/index.js"] //启动模块有两个
    },
    output: {
        path: path.resolve(__dirname, "target"), //必须配置一个绝对路径,表示资源放置的文件夹,默认是dist
        filename: "[id].[chunkhash:5].js" //配置的合并的js文件的规则
    },
    devtool: "source-map"
}

入口和出口的最佳实践 {ignore}

具体情况具体分析

下面是一些经典场景

一个页面一个JS

2020-01-10-12-00-28.png

源码结构

|—— src
    |—— pageA   页面A的代码目录
        |—— index.js 页面A的启动模块
        |—— ...
    |—— pageB   页面B的代码目录
        |—— index.js 页面B的启动模块
        |—— ...
    |—— pageC   页面C的代码目录
        |—— main1.js 页面C的启动模块1 例如:主功能
        |—— main2.js 页面C的启动模块2 例如:实现访问统计的额外功能
        |—— ...
    |—— common  公共代码目录
        |—— ...

webpack配置

module.exports = {
    entry:{
        pageA: "./src/pageA/index.js",
        pageB: "./src/pageB/index.js",
        pageC: ["./src/pageC/main1.js", "./src/pageC/main2.js"]
    },
    output:{
        filename:"[name].[chunkhash:5].js"
    }
}

这种方式适用于页面之间的功能差异巨大、公共代码较少的情况,这种情况下打包出来的最终代码不会有太多重复

一个页面多个JS

2020-01-10-12-38-03.png

源码结构

|—— src
    |—— pageA   页面A的代码目录
        |—— index.js 页面A的启动模块
        |—— ...
    |—— pageB   页面B的代码目录
        |—— index.js 页面B的启动模块
        |—— ...
    |—— statistics   用于统计访问人数功能目录
        |—— index.js 启动模块
        |—— ...
    |—— common  公共代码目录
        |—— ...

webpack配置

module.exports = {
    entry:{
        pageA: "./src/pageA/index.js",
        pageB: "./src/pageB/index.js",
        statistics: "./src/statistics/index.js"
    },
    output:{
        filename:"[name].[chunkhash:5].js"
    }
}

这种方式适用于页面之间有一些独立、相同的功能,专门使用一个chunk抽离这部分JS有利于浏览器更好的缓存这部分内容。

思考:为什么不使用多启动模块的方式?

单页应用

所谓单页应用,是指整个网站(或网站的某一个功能块)只有一个页面,页面中的内容全部靠JS创建和控制。 vue和react都是实现单页应用的利器。

2020-01-10-12-44-13.png

源码结构

|—— src
    |—— subFunc   子功能目录
        |—— ...
    |—— subFunc   子功能目录
        |—— ...
    |—— common  公共代码目录
        |—— ...
    |—— index.js

webpack配置

module.exports = {
    entry: "./src/index.js",
    output:{
        filename:"index.[hash:5].js"
    }
}