继上篇文章介绍了Webpack的基本概念,完整流程,以及打包过程中广播的一些事件的作用,这篇文章主要讲生成的chunk文件如何输出成具体的文件。分同步和异步两种情况来分析输出的文件使用的webpack版本:3.8.0

模块文件show.js

    function show(content) {
window.document.getElementById('app').innerText = 'Hello,' + content;
} // 通过 CommonJS 规范导出 show 函数
module.exports = show;

同步加载

// main.js

import show from './show';

show('TWENTY-FOUR K');

生成的bundle文件

// webpackBootstrap启动函数
// modules存放的是所有模块的数组,每个模块都是一个函数
(function(modules) {
var installedModules = {}; // 缓存安装过的模块,提升性能
//去传入的modules数组中加载对应moduleId(index)的模块,与node的require语句相似
function __webpack_require__(moduleId) {
// 检查是否已经加载过,如果有的话直接从缓存installedModules中取出
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 如果没有的话,就直接新建一个模块,并且存进缓存installedModules
var module = installedModules[moduleId] = {
i: moduleId, // 对应modules的索引index,也就是moduleId
l: false, // 标志模块是否已经加载
exports: {}
};
// 执行对应模块函数,并且传入需要的参数
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 将标志设置为true
module.l = true;
// 返回这个模块的导出值
return module.exports;
}
// 储存modules数组
__webpack_require__.m = modules;
// 储存缓存installedModules
__webpack_require__.c = installedModules;
// 为Harmony导出定义getter函数
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
configurable: false,
enumerable: true,
get: getter
});
}
};
// 用于与非协调模块兼容的getdefaultexport函数,将module.default或非module声明成getter函数的a属性上
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
// 工具函数,hasOwnProperty
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// webpack配置中的publicPath,用于加载被分割出去的异步代码
__webpack_require__.p = "";
// 使用__webpack_require__函数去加载index为0的模块,并且返回index为0的模块也就是主入口文件的main.js的对应文件,__webpack_require__.s的含义是启动模块对应的index
return __webpack_require__(__webpack_require__.s = 0);
})
/************************************************************************/
([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict";
// 设置__esModule为true,影响__webpack_require__.n函数的返回值
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
// 同步加载index为1的依赖模块
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__show__ = __webpack_require__(1);
// 获取index为1的依赖模块的export
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__show___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__show__);
// 同步加载 __WEBPACK_IMPORTED_MODULE_0__show___default()('wushaobin'); /***/ }),
/* 1 */
/***/ (function(module, exports) { function show(content) {
window.document.getElementById('app').innerText = 'Hello,' + content;
} // 通过 CommonJS 规范导出 show 函数
module.exports = show; /***/ })
]);

异步加载

// main.js

// 异步加载 show.js
import('./show').then((show) => {
// 执行 show 函数
show('TWENTY-FOUR K');
});

经webpack打包会生成两个文件0.bundle.js和bundle.js,怎么做的原因,是可以吧show.js以异步加载形式引入,这也是分离代码,达到减少文件体积的优化方法,两个文件分析如下。

0.bundle.js

// 加载本文件(0.bundle.js)包含的模块, webpackJsonp用于从异步加载的文件中安装模块,挂载至全局(bundle.js)供其他文件使用
webpackJsonp(
// 在其他文件中存放的模块id
[0],[
// 本文件所包含的模块 /***/ (function(module, exports) { function show(content) {
window.document.getElementById('app').innerText = 'Hello,' + content;
} // 通过 CommonJS 规范导出 show 函数
module.exports = show; /***/ })
]);

bundle.js

 (function(modules) { // webpackBootstrap启动函数
// 安装用于块加载的JSONP回调
var parentJsonpFunction = window["webpackJsonp"];
// chunkIds 异步加载文件(0.bundle.js)中存放的需要安装的模块对应的chunkId
// moreModules 异步加载文件(0.bundle.js)中存放需要安装的模块列表
// executeModules 异步加载文件(0.bundle.js)中存放需要安装的模块安装后需要执行的模块对应的index
window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
// 将 "moreModules" 添加到modules对象中,
// 将所有chunkIds对应的模块都标记成已经加载成功
var moduleId, chunkId, i = 0, resolves = [], result;
for(;i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if(installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0;
}
for(moduleId in moreModules) {
if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
// 执行
while(resolves.length) {
resolves.shift()();
} }; // 缓存已经安装的模块
var installedModules = {}; // 储存每个chunk的加载状态
// 键为chunk的id,值为0代表加载成功
var installedChunks = {
1: 0
}; // 去传入的modules数组中加载对应moduleId(index)的模块,与node的require语句相似,同上,此处省略
function __webpack_require__(moduleId) {
...
} // 用于加载被分割出去的需要异步加载的chunk对应的文件,
// chunkId需要异步加载的chunk对应的id,返回的是一个promise
__webpack_require__.e = function requireEnsure(chunkId) {
// 从installedChunks中获取chunkId对应的chunk文件的加载状态
var installedChunkData = installedChunks[chunkId];
// 如果加载状态为0,则表示该chunk已经加载成功,直接返回promise resolve
if(installedChunkData === 0) {
return new Promise(function(resolve) { resolve(); });
} // installedChunkData不为空且不为0时表示chunk正在网络加载中
if(installedChunkData) {
return installedChunkData[2];
} // installedChunkData为空,表示该chunk还没有加载过,去加载该chunk对应的文件
var promise = new Promise(function(resolve, reject) {
installedChunkData = installedChunks[chunkId] = [resolve, reject];
});
installedChunkData[2] = promise; // 通过dom操作,向html head中插入一个script标签去异步加载chunk对应的javascript文件
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = "text/javascript";
script.charset = 'utf-8';
script.async = true;
script.timeout = 120000;
// HTMLElement 接口的 nonce 属性返回只使用一次的加密数字,被内容安全政策用来决定这次请求是否被允许处理。
if (__webpack_require__.nc) {
script.setAttribute("nonce", __webpack_require__.nc);
}
// 文件的路径由配置的publicPath、chunkid拼接而成
script.src = __webpack_require__.p + "" + chunkId + ".bundle.js";
// 设置异步加载的最长超时时间
var timeout = setTimeout(onScriptComplete, 120000);
script.onerror = script.onload = onScriptComplete;
// 在script加载和执行完成时回调
function onScriptComplete() {
// 防止内存泄漏
script.onerror = script.onload = null;
clearTimeout(timeout); var chunk = installedChunks[chunkId];
// 判断chunkid对应chunk是否安装成功
if(chunk !== 0) {
if(chunk) {
chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
}
installedChunks[chunkId] = undefined;
}
};
head.appendChild(script); return promise;
}; // 这里会给__webpack_require__设置多个属性和方法,同上,此处省略 // 异步加载的出错函数
__webpack_require__.oe = function(err) { console.error(err); throw err; }; // Load entry module and return exports
return __webpack_require__(__webpack_require__.s = 0);
})
/************************************************************************/
([
// 存放没有经过异步加载的,随着执行入口文件加载的模块,也就是同步的模块
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
// 通过__webpack_require__.e异步加载show.js对应的chunk
// 异步加载 show.js
__webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, 1)).then((show) => {
// 执行 show 函数
show('Webpack');
}); /***/ })
]);

总结

这里我们通过对比同步加载和异步加载的简单应用,去分析两种情况webpack打包出来文件的差异性,我们发现,对于异步加载的bundle.js与同步的bundle.js基本相似,都是模拟node.js的require语句去导入模块,有所不同的是多了一个__webpack_require__.e函数,用来加载分割出的需要异步加载的chunk对应的文件,以及一个wepackJsonp函数,用于从异步加载的文件中去安装模块。

Webpack学习-工作原理(下)的更多相关文章

  1. JavaWeb与Asp.net工作原理比较分析

    一.概述 不管是什么语言开发的web应用程序,都是在解决一个问题,那就是用户输入url怎么把对应的页面响应出来,如何通过url映射到响应的类,由于自己做asp.net的时间也不短了,还算是对asp.n ...

  2. 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL

    周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...

  3. 分布式计算框架学习笔记--hadoop工作原理

    (hadoop安装方法:http://blog.csdn.net/wangjia55/article/details/53160679这里不再累述) hadoop是针对大数据设计的一个计算架构.如果你 ...

  4. servlet入门学习之工作原理解析

    从 Servlet 容器说起 要介绍 Servlet 必须要先把 Servlet 容器说清楚,Servlet 与 Servlet 容器的关系有点像枪和子弹的关系,枪是为子弹而生,而子弹又让枪有了杀伤力 ...

  5. Android艺术开发探索第四章——View的工作原理(下)

    Android艺术开发探索第四章--View的工作原理(下) 我们上篇BB了这么多,这篇就多多少少要来点实战了,上篇主席叫我多点自己的理解,那我就多点真诚,少点套路了,老司机,开车吧! 我们这一篇就扯 ...

  6. 深入学习NAT工作原理

    深入学习NAT工作原理 我们单位中的电脑很多,组成了一个局域网,网络中只有一个电脑和外网(Internet)相连,当然有一个外网地址,但仅仅一个.我以前一直不明白,我们局域网的电脑均能上网,几台同时上 ...

  7. 计算机原理学习(1)-- 冯诺依曼体系和CPU工作原理

    前言 对于我们80后来说,最早接触计算机应该是在95年左右,那个时候最流行的一个词语是多媒体. 依旧记得当时在同学家看同学输入几个DOS命令就成功的打开了一个游戏,当时实在是佩服的五体投地.因为对我来 ...

  8. 移动端自动化测试之Appium的工作原理学习

    Appium 简介 参考官网文档说明:http://appium.io/docs/en/about-appium/intro/ Appium官方文档上介绍,Appium 是一个自动化测试的开源工具,支 ...

  9. Salesforce学习之路(十)Aura组件工作原理

    很喜欢曾经看到的一句话:以输出倒逼输入.以输出的形式强制自己学习,确实是高效的学习方式,真的很棒.以下仅为个人学习理解,如有错误,欢迎指出,共同学习. 1. 什么是Lightning Componen ...

  10. 代码管理工具 --- git的学习笔记二《git的工作原理》

    通过几个问题来学习代码管理工具之git 一.git是什么?为什么要用它?使用它的好处?它与svn的区别,在Mac上,比较好用的git图形界面客户端有 git 是分布式的代码管理工具,使用它是因为,它便 ...

随机推荐

  1. Windows10 引导修复

    [问题]最近遇到一些用户使用的操作系统为Win10,但是使用过程中由于错误系统优化.卸载软件错误.误删系统文件.windows更新错误等,影响系统BCD引导文件,造成开机出现该BCD蓝屏报错,如下图所 ...

  2. IDEA 创建 web项目

    创建web步骤: 1.创建一个project File -> New Project -> 选择Java,Project SDK为1.7,勾选Web Application(创建web.x ...

  3. 清华源和中科大源都停止对Anaconda的支持之后,换腾讯云镜像的方法

    直接下载下面的文件解压后放在用户文件夹下即可,windows为"C:\用户\你的用户名\",Linux为"/home/你的用户名/"即用户主目录下. 点我下载 ...

  4. 什么时候使用“RCC_APBXPeriph_AFIO”

    什么时候需要开启复用时钟: (1)使用EXTI (2)重映射(用到外设的重映射功能时才需要使能AFIO的时钟) 举例:重映射USART2 USART2的TX/RX在PA.2/3.但是,PA.2已经被T ...

  5. HTTP 方法:Get与Post分析

    GET - 从指定的资源请求数据 POST - 向指定的资源提交要被处理的数据 GET 方法 GET 请求可被缓存 GET 请求保留在浏览器历史记录中 GET 请求可被收藏为书签 GET 请求不应在处 ...

  6. webpack 4.X 基础编译

    webpack4.x的打包已经不能用webpack 文件a 文件b的方式,而是直接运行webpack --mode development或者webpack --mode production,这样便 ...

  7. HTML5外包团队:HTML5 Canvas使用教程

    canvas 元素用于在网页上绘制图形. 什么是 Canvas? HTML5 的 canvas 元素使用 JavaScript 在网页上绘制图像. 画布是一个矩形区域,您可以控制其每一像素. canv ...

  8. 在微信浏览器中 location.reload() 不刷新解决方案(直接调用方法)

    1.问题 在微信浏览器中,需要时刷新当前页面. 正常情况下我们直接使用 location.reload 方法来刷新. 2.解决方法 function realod(){ var {search,hre ...

  9. loadrunner 参数化-如何从数据库中取数据-连接数据库进行参数化

    LoadRunner提供两种参数化取值方式,一种是手动编辑,另一种就是通过连接数据库取值.一般在大型业务并发压力测试时,数据量肯定也都是非常大的,所以手动去编辑就不切实际了,这时用连接数据库的功能就方 ...

  10. stress负载生成器使用简介

    一.Stress工具原始网页: https://people.seas.harvard.edu/~apw/stress/ 二.Docker镜像的构建过程(dockerfile): progrium/s ...