webpack原理与实践

本文详细介绍了webpack的模块化演进,包括文件划分、命名空间和IIFE的优缺点,以及模块加载的问题和解决方案。重点讲解了webpack的打包过程、配置文件、Loader和Plugin的使用,以及Dev Server、模块热更新(HMR)、SourceMap等功能。同时,讨论了webpack项目优化,如Tree Shaking、Code Splitting和项目中遇到的问题及解决策略。
摘要由CSDN通过智能技术生成

webpack原理

模块化演进

1. 文件划分方式

<html lang="en">
<script src="module-a.js"></script>
<script src="module-b.js"></script>
<script>
// 直接使用全局成员
foo()// 可能命名冲突
data = [] // 数据可能会被修改
</script>
</html>

缺点:

  • 模块直接在全局工作,大量模块成员污染全局作用域
  • 没有私有空间,所有模块内的成员都可以在模块外部被访问或者修改
  • 一旦模块增多,容易产生命名冲突
  • 无法管理模块与模块之间的依赖关系
  • 在维护过程中也很难分辨每个成员所属的模块

2. 命名空间方式

只允许暴露一个全局对象,其他模块挂载在全局对象上

// module-a.js
window.moduleA = {
   
	method1: function() {
   
		console.log("method1");
	}
}
// module-b.js
window.moduleA = {
   
	data: 1,
	method1: function() {
   
		console.log("method1");
	}
}

只解决了命名冲突,其他问题依旧存在

3. IIFE

在2的基础上,变成立即执行函数,为模块提供私有空间

// module-a.js
(function () {
   
	var name = "module-a";
	function method1() {
   }
	window.moduleA = {
   
		method1: method1
	}
})()

// module-b.js
(function ($) {
    // 通过参数明显表明这个模块的依赖
	// 私有成员,通过闭包访问
	var name = "module-b";
	function method1() {
   }
	window.moduleB = {
   
		method1: method1
	}
})(jQuery)

解决了命名冲突和全局作用域污染以及模块之间的依赖关系

以上三种解决方式,只解决了模块的组织问题,模块加载的问题并未解决

模块加载的问题

通过在html里面script加载模块,这种方式让模块加载不受控制;
在出现模块未引入页面,或者引入的模块已经移除等情况,会影响程序

理想的方式: 在页面中引入一个JS入口文件,其余用到的模块可以通过代码控制按需加载

模块化规范

目前通过约定实现模块化,为了统一不同开发者,不同项目之间的差异,需要制定一个行业标准去规范模块化的实现方式。

  • 一个统一的模块化标准规范
  • 一个可以自动加载模块的基础库
  1. CommonJS规范:(同步模块)
    Node.js所遵循的模块规范,该规范约定一个文件就是一个模块,每个模块都有单独的作用域;通过module.exports 导出成员,require函数载入模块。
  2. AMD: 异步模块
// 定义模块
define(['jquery', './module2.js'], function($, module2) {
   
	// return 导出模块
	return {
   
		start: function() {
   }
	}
})
// 载入模块
require(['./modules/module1.js'], function(module1){
   
	module1.start()
})
  1. CMD

  2. 模块化标准规范
    Node.js环境中,遵循CommonJS规范组织模块
    浏览器环境,遵循ES Modules规范(es6)

模块打包工具

  • ES Modules 模块系统本身存在环境兼容问题,尽管如今主流浏览器的最新版本都支持这一特性,但目前无法保证用户的浏览器使用情况
  • 模块化的方式划分出来的模块文件过多,而前端应用又运行在浏览器中,每一个文件都需要单独从服务器请求回来,零散的模块文件必然会导致浏览器的频繁发送网络请求,影响应用的工作效率。
  • 随着应用日益复杂,前端应用开发过程中,除了Js代码需要模块化,HTML和CSS这些资源文件也需要被模块化,也应该看作前端应用中的模块,只不过种类和用途跟JS不同。
  1. 编译:(babel 转译代码新特性) 解决环境兼容问题
  2. 打包:模块打包(在开发阶段必要,组织代码)
  3. 不同种类资源的模块打包(1,2,3webpack)(1,2 gulp)

webpack静态模块打包器(打包工具),发展成对整个前端项目的构建系统

webpack

  • webpack作为一个模块打包工具,本身可以实现模块化打包,通过webpack可以将零散的JS代码打包到一个JS文件中
  • 对于环境兼容,webpack可以在打包过程中通过Loader对其实现编译转换,然后再进行打包
  • 对于不同类型的前端模块,webpack支持在JS中以模块化的方式载入任意类型的资源文件;例如通过webpack在js中加载css文件,被加载的css文件将会通过style标签的方式工作
  • 其他
  • 还具备代码拆分能力,能够将应用中的所有模块按需分块打包;不用担心全部代码打包到一起,产生单个文件过大,导致加载慢的问题。

webpack打包过程详解

webpack版本: 5.61.0核心模块
webpack-cli 用于在命令行中调用webpack, (它所提供的命令行程序存在于node_modules/.bin文件中)

npx webpack
在执行过程中,webpack默认会自动从src/index.js文件开始打包
然后根据代码中的模块导入操作,自动将所有用到的模块打包到一起
在根目录下生成一个dist目录,打包结果就会存在dist/main.js中
在html文件中引入打包后的文件

// bundle.js
(() => {
    // webpackBootstrap(打包到同一个文件,并且提供基础代码,让模块与模块之间的依赖关系还可以保持)
	var __webpack_modules__ = ({
   
	// key 模块路径
	 "./node_modules/babel-loader/lib/index.js??clonedRuleSet-2[0].rules[0].use[0]!./node_modules/vue-loader/lib/index.js??vue-loader-options!./src/App.vue?vue&type=script&lang=js&":
	 ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
   

"use strict";
// eval执行打包过的模块字符串代码
// __webpack_require__.r r函数的作用是为了给导出对象添加标记
/*
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {"default": () => (__WEBPACK_DEFAULT_EXPORT__) });
var _compB_vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/compB.vue");
const __WEBPACK_DEFAULT_EXPORT__ = ({
    name: 'App',
    components: {
        CompB: _compB_vue__WEBPACK_IMPORTED_MODULE_0__["default"]
    },
    data: function data() { 
        return {
            message: '',    
            messageFromBus: '',      
            sender: ''    
        };  
    }, 
    mounted: function mounted() {    
        var _this = this;    
        this.$bus.$on('sendMessage', function (obj) {     
            // 通过eventBus监听sendMessage事件           
            var sender = obj.sender,          
            message = obj.message;      
            _this.sender = sender;      
            _this.messageFromBus = message;    
        });  
    },  
    methods: {    
        sendMessage: function sendMessage() {      
            this.$bus.$emit('sendMessage', {        
                // 通过eventBus触发sendMessage事件             
                sender: this.$options.name,        
                message: this.message      
            });    
        }  
    }
});
*/
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _compB_vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./compB.vue */ \"./src/compB.vue\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({\n  name: 'App',\n  components: {\n    CompB: _compB_vue__WEBPACK_IMPORTED_MODULE_0__[\"default\"]\n  },\n  data: function data() {\n    return {\n      message: '',\n      messageFromBus: '',\n      sender: ''\n    };\n  },\n  mounted: function mounted() {\n    var _this = this;\n\n    this.$bus.$on('sendMessage', function (obj) {\n      // 通过eventBus监听sendMessage事件     \n      var sender = obj.sender,\n          message = obj.message;\n      _this.sender = sender;\n      _this.messageFromBus = message;\n    });\n  },\n  methods: {\n    sendMessage: function sendMessage() {\n      this.$bus.$emit('sendMessage', {\n        // 通过eventBus触发sendMessage事件     \n        sender: this.$options.name,\n        message: this.message\n      });\n    }\n  }\n});\n\n//# sourceURL=webpack:///./src/App.vue?./node_modules/babel-loader/lib/index.js??clonedRuleSet-2%5B0%5D.rules%5B0%5D.use%5B0%5D!./node_modules/vue-loader/lib/index.js??vue-loader-options");

/***/ }),
	});
	// The module cache
	var __webpack_module_cache__ = {
   };
	// The require function
	function __webpack_require__(moduleId) {
   
 		// Check if module is in cache
 		var cachedModule = __webpack_module_cache__[moduleId];
 		if (cachedModule !== undefined) {
   
 			return cachedModule.exports;
		}
 		// Create a new module (and put it into the cache)
 		var module = __webpack_module_cache__[moduleId] = {
   
 			// no module.id needed
 			// no module.loaded needed
 			exports: {
   }
 		};
 	
 		// Execute the module function
 		// 传入创建的模块,模块导出对象和require, 为了让函数内部可以导入和导出对象
 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
 	
 		// Return the exports of the module
		return module.exports;
 	}
})()

	// 用立即执行函数给__webpack_require__增加一些数据和工具函数
 	 	/* webpack/runtime/make namespace object */
 	(() => {
   
 		// define __esModule on exports 在导出对象上添加__esModule标记
 		__webpack_require__.r = (exports) => {
   
 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
   
 				Object.defineProperty(exports, Symbol.toStringTag, {
    value: 'Module' });
 			}
 			Object.defineProperty(exports, '__esModule', {
    value: true });
 		};
 	})();
	// startup
 	// Load entry module and return exports
 	// This entry module can't be inlined because the eval devtool is used.
 	var __webpack_exports__ = __webpack_require__(</
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值