首页 > 其他分享 >webpack 打包实战解析

webpack 打包实战解析

时间:2024-08-04 16:07:18浏览次数:5  
标签:__ exports require __. module webpack 解析 打包

Webpack 打包实战

本文从一个简单的例子出发,比较一下,我们的代码经过webpack 打包后会变成啥样,带有HMR的情况下,会有什么不同

我们的代码

// index.js
import {greeting} from './moduleA'

let cleanup=null;
function render(){
    const node = document.getElementById('mount');
    const text = document.createElement('div');
    text.className='red';
    node.appendChild(text);
    text.innerHTML=greeting();
    return ()=>node.removeChild(text);
}

cleanup=render();

if(module.hot){
    module.hot.accept('./moduleA',()=>{
        cleanup?.();
        cleanup=render();
    });
    module.hot.accept();
}


// moduleA.js
function getName(){
    return 'user3'
}
export function greeting(){
    return `<div>${getName()} said: Hello World! chafd fd</div>`
}

经过webpack 打包,我们通过bundle.js, 我们可以看到如下代码

  • 整个bundle.js是在一个IIFE里面执行的,避免了对于全局的污染。
  • __webpack_modules__,这个对象里面是{moduleName:(module,module.exports,webpackrequire)=>{}}这样的结构。它将我们的模块转换成成对象。同时它拦截了我们对于代码的导入。代码片段如下。
var __webpack_modules__ = ({

    "./index.js": ((module, __webpack_exports__, __webpack_require__) => {
                "use strict";
                // 这个方法是为了定义这个属性: module.export.__esmodule=true
                __webpack_require__.r(__webpack_exports__);
                // 这个方法就是我们源码里面的import('./moduleA.js').它被webpack拦截了,通过__webpack_require__来导入到本地变量。注意,这个方法里面是有缓存的。一个模块只会导入依次。后面介绍它的实现
                /* harmony import */ var _moduleA__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./moduleA */ "./moduleA.js");

                // 下面就是我们代码的主逻辑。
                let cleanup=null;
                function render(){
                    const node = document.getElementById('mount');
                    const text = document.createElement('div');
                    text.className='red';
                    node.appendChild(text);
                    text.innerHTML=(0,_moduleA__WEBPACK_IMPORTED_MODULE_0__.greeting)();// 这个是为了在全局作用域中执行。这样的好处是greeting()里面的this,会是window,然而由于我们开启了严格模式,所以,它应该是undefined.
                    return ()=>node.removeChild(text);
                }



                cleanup=render();

                if(true){
                    module.hot.accept(/*! ./moduleA */ "./moduleA.js",__WEBPACK_OUTDATED_DEPENDENCIES__ => { 
                        // 这端代码就是webpack hmr插入的,当依赖模块被更新了,它会自动的更新moduleA.js.
                        /* harmony import */ _moduleA__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./moduleA */ "./moduleA.js");
                        (()=>{
                                cleanup?.();
                                cleanup=render();
                            })(__WEBPACK_OUTDATED_DEPENDENCIES__); });

                    // 这个是我们自己定义的自更新的代码
                    module.hot.accept();
                }
            }),

    "./moduleA.js":((module, __webpack_exports__, __webpack_require__) => {
            "use strict";
            __webpack_require__.r(__webpack_exports__);

            // 这个是将greeting这个方法定义到module.exports中。
            /* harmony export */ __webpack_require__.d(__webpack_exports__, {
            /* harmony export */   greeting: () => (/* binding */ greeting)
            /* harmony export */ });
            // 这个方法是为了使得module.exports 不能被修改。module.exports是从__webpack_exports__中读取的。
            /* module decorator */ module = __webpack_require__.hmd(module);

            function getName(){
                return 'user3'
            }
            function greeting(){
                return `<div>${(0,getName)()} said: Hello World! chafd</div>`
            }

            if(true){
                console.log(module);
                module.hot.accept(/*! ./moduleB.js */ "./moduleB.js",
                __WEBPACK_OUTDATED_DEPENDENCIES__ => { 
                    /* harmony import */ _moduleB_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./moduleB.js */ "./moduleB.js");
                    (()=>{
                            module.hot.invalidate();
                        })(__WEBPACK_OUTDATED_DEPENDENCIES__); })
            }

         })
});

  • __webpack_require__,这个方法是webpack拦截我们代码里面的import用的,import都被换成了这个。它的定义如下
	function __webpack_require__(moduleId) {
 		// Check if module is in cache
        // ================================导入模块优先从缓存里面读取==============================
 		var cachedModule = __webpack_module_cache__[moduleId];
 		if (cachedModule !== undefined) {
 			if (cachedModule.error !== undefined) throw cachedModule.error;
 			return cachedModule.exports;
 		}
 		// Create a new module (and put it into the cache)
        // =====================以模块名为key创建缓存对象,这个module对象也就是我们所理解的module,module.exports===============
 		var module = __webpack_module_cache__[moduleId] = {
 			id: moduleId,
 			loaded: false,
 			exports: {}  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  module.exports
 		};
 	
 		// Execute the module function
 		try {
 			var execOptions = { id: moduleId, module: module, factory: __webpack_modules__[moduleId], require: __webpack_require__ };
            //====================这个是模块加载拦截用的===============================
 			__webpack_require__.i.forEach(function(handler) { handler(execOptions); });
            //===================这个就是module, module.exports=====================
 			module = execOptions.module;
            //=====================执行我们在__webpack_modules__里面定义的模块工程方法,第一个是this,后面三个是它所需的参数========================================
 			execOptions.factory.call(module.exports, module, module.exports, execOptions.require);
 		} catch(e) {
 			module.error = e;
 			throw e;
 		}
 	
 		// Flag the module as loaded
        // 模块加载完成。
 		module.loaded = true;
 	
 		// Return the exports of the module
        // 返回模块的exports.
 		return module.exports;
 	}
  • __webpack_require__上面还定义了一些其他静态方法
    • __webpack_require__.d 这个是Object.defineProperty的封装,它是为了将module.exprts里面属性设置成只读。
    __webpack_require__.d = (exports, definition) => {
        for(var key in definition) {
            if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
                Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
            }
        }
    };
- `__webpack_require__.hmd` 这个其实是为了创建**module**对象实例,最重要的就是将**module.exports**设置成不能修改。
    // hmd: harmony module decorator
    __webpack_require__.hmd = (module) => {
 			module = Object.create(module);
 			if (!module.children) module.children = [];
 			Object.defineProperty(module, 'exports', {
 				enumerable: true,
 				set: () => {
 					throw new Error('ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: ' + module.id);
 				}
 			});
 			return module;
 		};
- `__webpack_require__.r` 在module中定义**__esmodule**属性
__webpack_require__.r = (exports) => {
 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
 			}
 			Object.defineProperty(exports, '__esModule', { value: true });
 		};
- `__webpack_require__.m = __webpack_modules__; __webpack_require__.c = __webpack_module_cache__;	__webpack_require__.i = [];` 设置一些快捷属性

下面的API是与hmr有关的

  • __webpack_require__.h这个是获取当前代码的hash码的 __webpack_require__.h = () => ("333c9438eb92e79fd271")
  • __webpack_require__.hmrF这个是hmr 在check的时候向后端检查是否有代码更新的路由名 __webpack_require__.hmrF = () => ("main." + __webpack_require__.h() + ".hot-update.json");
  • __webpack_require__.hu这个是hmr向后端请求代码更新的路由名,发送在prepare阶段。
__webpack_require__.hu = (chunkId) => {
			// return url for filenames based on template
 			return "" + chunkId + "." + __webpack_require__.h() + ".hot-update.js";
 		};
  • __webpack_require__.l这个是hmr向后端发送请求的方法。前面的两个方法只是产生url,最终交给这个方法去发请求。
var inProgress = {};
var dataWebpackPrefix = "hmr:";
// loadScript function to load a script via script tag
// url 是请求js的路由,done,是回调方法。它是借助script标签来完成js文件的请求的。
__webpack_require__.l = (url, done, key, chunkId) => {
	if(inProgress[url]) { inProgress[url].push(done); return; }
	var script, needAttach;
	if(key !== undefined) {
		var scripts = document.getElementsByTagName("script");
		for(var i = 0; i < scripts.length; i++) {
			var s = scripts[i];
			if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; }
		}
	}
	if(!script) {
		needAttach = true;
		script = document.createElement('script');

		script.charset = 'utf-8';
		script.timeout = 120;
		if (__webpack_require__.nc) {
			script.setAttribute("nonce", __webpack_require__.nc);
		}
		script.setAttribute("data-webpack", dataWebpackPrefix + key);

		script.src = url;
	}
	inProgress[url] = [done];
	var onScriptComplete = (prev, event) => {
		// avoid mem leaks in IE.
		script.onerror = script.onload = null;
		clearTimeout(timeout);
		var doneFns = inProgress[url];
		delete inProgress[url];
		script.parentNode && script.parentNode.removeChild(script);
		doneFns && doneFns.forEach((fn) => (fn(event)));
		if(prev) return prev(event);
	}
	var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);
	script.onerror = onScriptComplete.bind(null, script.onerror);
	script.onload = onScriptComplete.bind(null, script.onload);
	needAttach && document.head.appendChild(script);
};

标签:__,exports,require,__.,module,webpack,解析,打包
From: https://www.cnblogs.com/kongshu-612/p/18341841

相关文章

  • 如何在 java 或 python 中使用 HTTP(S) 解决无法解析的主机名或无法识别的名称错误?
    我尝试以编程方式访问网站的信息,但在Java和Python上都无法解析主机名。如果我指定IP地址,则会将错误更改为TLSV1_UNRECOGNIZED_NAME。不过,这个网站无需任何额外的工作就可以通过任何浏览器解决。我在这里浏览了很多潜在的解决方案,但对于Python,它说这个问题应该在2.7......
  • 【Linux】TCP全解析:构建可靠的网络通信桥梁
    文章目录前言1.TCP协议概述2.TCP报头结构3.如何理解封装和解包呢?4.TCP的可靠性机制4.1TCP的确认应答机制4.2超时重传机制5.TCP链接管理机制5.1经典面试题:为什么建立连接是三次握手?5.2经典面试题:为什么要进行四次挥手?6.流量控制7.滑动窗口机制8.拥塞控制9.......
  • webpack HMR API解析
    介绍一下webpackhmr相关的APIwebpack首先将模块变成对象的一个属性,该属性是一个方法,调用它就返回最新的模块。模块的变更就变成了更新这些方法的定义。其次,webpack对于我们代码的import,做了拦截,会变成从它的模块对象里面去读取模块,同时它做了缓存。最后,当模块变化的时候,它会依......
  • TypeError: ‘float’ object is not iterable 深度解析
    TypeError:‘float’objectisnotiterable深度解析与实战指南在Python编程中,TypeError:'float'objectisnotiterable是一个常见的错误,通常发生在尝试对浮点数(float)进行迭代操作时。这个错误表明代码中存在类型使用不当的问题,因为浮点数不是可迭代对象。本文将深入......
  • TypeError: ‘dict’ object is not callable 深度解析
    TypeError:‘dict’objectisnotcallable深度解析在Python编程中,TypeError:'dict'objectisnotcallable是一个常见的错误,通常发生在尝试调用一个字典对象时。这个错误表明代码中存在逻辑错误,可能是将字典误用为函数或方法。本文将深入探讨这一错误的常见原因,并提......
  • 【C语言】结构体内存布局解析——字节对齐
    ......
  • PHP中preg_replace函数解析
    preg_replace—执行一个正则表达式的搜索和替换mixedpreg_replace(mixed$pattern,mixed$replacement,mixed$subject)搜索subject中匹配pattern的部分,以replacement进行替换。常见于CTF竞赛中web题目中1、/g表示该表达式将用来在输入字符串中查找所有可能的匹配,返......
  • Android开发 - DetailFragment 类解析
    DetailFragment是什么DetailFragment专门用于显示详细信息。当用户在主界面(例如一个列表)中选择某个项时,应用会使用DetailFragment显示该项的详细信息。它通常与主界面的Fragment协同工作,形成一个主从结构(Master-Detail)使用场景新闻应用:主界面显示新闻列表,DetailFrag......
  • YOLOv8模型:从YAML文件到模型定义(代码逐行解析)
    鱼弦:公众号【红尘灯塔】,CSDN博客专家、内容合伙人、新星导师、全栈领域优质创作者、51CTO(Top红人+专家博主)、github开源爱好者(go-zero源码二次开发、游戏后端架构https://github.com/Peakchen)YOLOv8模型:从YAML文件到模型定义(代码逐行解析)简介YOLOv8是目前最先进的目......
  • 洛谷 统计天数 + 语句解析 题解
    题目:P1567统计天数P1597语句解析第一道:P1567统计天数题目描述炎热的夏日,KC非常的不爽。他宁可忍受北极的寒冷,也不愿忍受厦门的夏天。最近,他开始研究天气的变化。他希望用研究的结果预测未来的天气。经历千辛万苦,他收集了连续 N(1≤N≤10^6)天的最高气温数据。现在......