# webpack深入浅出
# 简介
webpack是一个打包模块化 JavaScript 的工具,在 webpack里一切文件皆模块,通过 Loader 转换文件,通过 Plugin 注入钩子,最后输出由多个模块组合成的文件。 webpack专注于构建模块化项目。
# webpack的优点是
- 专注于处理模块化的项目,能做到开箱即用、 一
- 可通过 Plugin 扩展,完整好用又不失灵活
- 使用场景不局限于 Web 开发:
- 社区庞大活跃 经常引 入紧跟时代发展的新特性扩展:
- 良好的开发体验
# webpack的缺点
- 只能用于采用模块化开发的项目
# webpack核心概念
- Entry 入口, webpack执行构建的第 步将从 Entry开始,可抽象成输入
- Module 模块,在webpack里一切皆模块,一个模块对应一个文件webpack从配置的 Entry 开始,递归找出所有依赖的模块
- Chunk代码块 Chunk 由多个模块组合而成,用于代码合并与分割
- Loader 模块转换器,用于将模块的原内容按照需求转换成新内容
- Plugin 扩展插件,在webpack构建流程中的特定时机会广播对应的事件,插件可以监听这些事件的发生,在特定的时机做对应的事情
# webpack流程概括
webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
- 初始化参数 从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
- 开始编译 用上一步得到的参数初始Compiler对象,加载所有配置的插件,通 过执行对象的run方法开始执行编译
- 确定入口 根据配置中的 Entry 找出所有入口文件
- 编译模块 从入口文件出发,调用所有配置的 Loader 对模块进行编译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
- 完成模块编译 在经过第4步使用 Loader 翻译完所有模块后, 得到了每个模块被编译后的最终内容及它们之间的依赖关系
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再将每个 Chunk 转换成一个单独的文件加入输出列表中,这是可以修改输出内容的最后机会
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,将文件的内容写入文件系统中。
在以上过程中, Webpack 会在特定的时间点广播特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,井且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
# 打包模型的基本流程
- 利用babel完成代码转换,并生成单个文件的依赖
- 生成依赖图谱
- 生成最后打包代码
先看一个简化后的 webpack 打包好的文件:
(function(modules) { // webpackBootstrap
var installedModules = {};
function __webpack_require__(moduleId) {
// Check if module is in cache
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
return module.exports;
}
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// define getter function for harmony exports
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};
// define __esModule on exports
__webpack_require__.r = function(exports) {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
// create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
__webpack_require__.t = function(value, mode) {
if(mode & 1) value = __webpack_require__(value);
if(mode & 8) return value;
if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, 'default', { enumerable: true, value: value });
if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
return ns;
};
// getDefaultExport function for compatibility with non-harmony modules
__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;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})({
"./src/index.js":(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _info_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./info.js */ "./src/info.js");
console.log(_info_js__WEBPACK_IMPORTED_MODULE_0__["default"])
}),
"./src/info.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _name_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./name.js */ "./src/name.js");
const info = `${_name_js__WEBPACK_IMPORTED_MODULE_0__["name"]} is beautiful`
/* harmony default export */ __webpack_exports__["default"] = (info);
}),
"./src/name.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "name", function() { return name; });
const name = 'winty'
})
});
这里的moduleId就是模块路径,如./src/commonjs/index.js。
webpack4中只有optimization.namedModules为true,此时moduleId才会为模块路径,否则是数字id。为了方便开发者调试,在development模式下optimization.namedModules参数默认为true。
ES6 module: export/export default 打包
"./src/info.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _name_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./name.js */ "./src/name.js");
const info = `${_name_js__WEBPACK_IMPORTED_MODULE_0__["name"]} is beautiful`
/* harmony default export */ __webpack_exports__["default"] = (info);
}),
"./src/name.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "name", function() { return name; });
const name = 'winty'
})
这里用到了挂载在__webpack_require__上的两个函数d和r:
- d在exports对象上为某一属性设置getter函数。
- r在exports对象上设置属性__esModule: true。
设置getter是为了实现ES6模块的动态绑定,即export的值修改之后能够动态更新到import。但如果export default一个非函数或class,则不会动态绑定。
打包更多相关:https://cloud.tencent.com/developer/article/1172453
关注作者公众号
一起学习成长,一起浪~

加群学习交流
扫描二维码,备注加群~
