Webpack入门与实战——读书笔记

1. Webpack简介

Webpack是一个开源的JavaScript模块打包工具,其最核心的功能是解决模块之间的依赖,把各个模块按照特定的规则和顺序组织在一起,最终合并为一个JS文件(有时会有多个,这里讨论的只是最基本的情况)。这个过程就叫作模块打包。

  • 手动引入script文件的缺点:

    1. 需要手动维护JavaScript的加载顺序
    2. 每一个script标签都需要向服务器发送一次请求,增加负担
    3. 每个script中,顶层作用域是全局作用域,会造成全局作用域污染
  • 模块化的优点:

    1. 通过导入导出语句可以清晰地看到模块间的依赖关系
    2. 模块可以借助工具打包为合并后的资源文件,减少了网络开销
    3. 各个模块间的作用域隔离,避免了命名冲突
  • 模块化打包工具:解决模块间的依赖

    工作方式:1.将存在依赖关系的模块按照特定规则合并为单个JS文件,一次全部加载进页面

    ​ 2.在页面初始时加载一个入口模块,其他模块异步加载

  • 流行的打包工具:Webpack Parcel Rollup

2. 模块打包

  • CommonJs(Node.js中版本)

    1. 模块

      CommonJS模块会形成一个属于模块自身的作用域,所有的变量及函数只有自己能访问,对外是不可见

    2. 导出

      moudle.exports = {
          name: 'calculater',
          add: function(a,b) {
              return a+b;
          }
      }
      
    3. 导入(获取的是一份导出值的拷贝,不会改变该模块

      const calculator = require('./calculator.js');
      const sum = calculator.add(2,3);
      

      导入模块时有两种情况:

      • require的模块第一次被加载,首先执行该模块,然后导出内容
      • require的模块曾被加载过,直接导出上次执行后得到的结果
      • 模块会有一个module对象用来存放其信息,这个对象中有一个属性loaded用于记录该模块是否被加载过。它的值默认为false,当模块第一次被加载和执行过后会置为true,后面再次加载时检查到module.loaded为true,则不会再次执行模块代码
      • require函数可以接受表达式,可以动态地指定模块加载路径
    4. 特点

      CommonJs对模块依赖地解决是”动态的——模块依赖关系地建立发生在代码运行阶段“,CommonJs模块被执行前,没有办法确定明确的依赖关系,模块的导入导出发生在代码的运行阶段

  • ES6 Moudle(只读值的动态映射,不能更改导入值

    ES6 Moudle会自动采用严格模式

    1. 导出

      • 命名导出

        const name = 'calculator';
        const add = function(){};
        export { name, add as getSUm };
        
      • 默认导出

        export default {
            name: 'calulator',
            add: function(){}
        }
        
    2. 导入

      // 命名导入
      import {name, add} from './calculator.js';
      import * as calculator from './calculator.js';
      // 默认导入
      import myCalculator from './calculator.js';
      // 混合导入
      import React, { Component } from 'react';
      
    3. 特点

      • ES6 Moudle对模块依赖是“静态的”——模块依赖关系的建立发生在代码编译阶段
      • ES6 Moudle的导入导出语句是声明式的,它不支持导入的路径是一个表达式,并且导入导出语句必须位于模块的顶层作用域(不能放在if语句中)
      • 死代码监测和排除
      • 模块变量类型检查
      • 编译器优化
  • AMD(Asyncronous Module Definition异步模块定义)

    在AMD中使用define函数来定义模块,它可以接受3个参数。第1个参数是当前模块的id,相当于模块名;第2个参数是当前模块的依赖,比如上面我们定义的getSum模块需要引入calculator模块作为依赖;第3个参数用来描述模块的导出值,可以是函数或对象。如果是函数则导出的是函数的返回值;如果是对象则直接导出对象本身。

    // 定义AMD模块
    define('getSum', ['calculator'], function(math) {
        return function(a,b) {
            // xxx
        }
    })
    
    // 异步加载模块
    require(['getSum'], function(getSum) {
        getSum(2,3);
    })
    
  • UMD(Universal Moudule Definition通用模块标准)

    它的目标是使一个模块能运行在各种环境下,不论是CommonJS、AMD,还是非模块化的环境(当时ES6 Module还未被提出)

    UMD其实就是根据当前全局对象中的值判断目前处于哪种模块环境当前环境是AMD,就以AMD的形式导出;当前环境是CommonJS,就以CommonJS的形式导出

  • 模块打包原理

    // 立即执行匿名函数
    (function(modules) {
        //模块缓存
        var installedModules = {};
        // 实现require
        function _webpack_require_(moduleId) {
            ...
        }
        // 执行入口模块的加载
        return _webpack_require_(_webpack_require_.s = 0);
    })({
        // moudles: 以key-value形式存储所有被打包的模块
        0: function(module, exports, _webpack_require_) {
            // 打包入口
            module.exports = _webpack_require_("3qiv");
        },
        "3qiv": function(module, exports, _webpcak_require_) {
            // index.js内容
        },
        jkzz: function(module,exports) {
            // calculator.js内容
        }
    });
    
    • installedModules对象。每个模块只在第一次被加载的时候执行,之后其导出值就被存储到这个对象里面,当再次被加载的时候直接从这里取值,而不会重新执行。
    • webpack_require__函数。对模块加载的实现,在浏览器中可以通过调用_webpack_require(module_id)来完成模块导入
    • modules对象。工程中所有产生了依赖关系的模块都会以key-value的形式放在这里。key可以理解为一个模块的id,由数字或者一个很短的hash字符串构成value则是由一个匿名函数包裹的模块实体,匿名函数的参数则赋予了每个模块导出和导入的能力

3. 资源输入输出

Webpack入门与实战——读书笔记

Webpack会从入口文件开始检索,并将具有依赖关系的模块生成一棵依赖树,最终得到一个chunk(代码块)。由这个chunk得到的打包产物我们一般称之为bundle

  • Webpack通过context和entry这两个配置项来共同决定入口文件的路径,context可以理解为资源入口的路径前缀,在配置时要求必须使用绝对路径的形式,entry的配置可以有多种形式:字符串、数组、对象、函数,传入一个数组的作用是将多个资源预先合并,在打包时Webpack会将数组中的最后一个元素作为实际的入口路径,如果想要定义多入口,则必须使用对象的形式。对象的属性名(key)是chunk name,属性值(value)是入口路径

    Webpack入门与实战——读书笔记

  • 单页应用

    在Webpack默认配置中,当一个bundle大于250kB时(压缩前)会认为这个bundle已经过大了,在打包时会发生警告

    提取vendor(供应商):在Webpack中vendor一般指的是工程所使用的库、框架等第三方模块集中打包而产生的bundle

    Webpack入门与实战——读书笔记

    通过这样的配置,app.js产生的bundle将只包含业务模块,其依赖的第三方模块将会被抽取出来生成一个新的bundle,由于vendor仅仅包含第三方模块,这部分不会经常变动,因此可以有效地利用客户端缓存,在用户后续请求页面时会加快整体的渲染速度

  • 多页应用

    每个页面都只加载各自必要的逻辑,而不是将所有页面打包到同一个bundle中。因此每个页面都需要有一个独立的bundle

    Webpack入门与实战——读书笔记

  • 配置资源出口

r仅仅包含第三方模块,这部分不会经常变动,因此可以有效地利用客户端缓存,在用户后续请求页面时会加快整体的渲染速度

  • 多页应用

    每个页面都只加载各自必要的逻辑,而不是将所有页面打包到同一个bundle中。因此每个页面都需要有一个独立的bundle

    [外链图片转存中…(img-F93x3HVZ-1635051292682)]

  • 配置资源出口

    所有与出口相关的配置都集中在output对象里

上一篇:如何使用包之间的调用


下一篇:使用反射 和接口 做一个计算器