当前位置: 首页 > Web前端 > HTML5

webpack打包原理

时间:2023-04-05 18:34:14 HTML5

前言本文主要介绍webpack打包原理。webpack项目还是比较复杂的,现在已经到了webpack5版本。但是webpack打包的原理并不是很复杂。本文所有内容基于webpack0.1版本,第一个可以在github上找到的webapck版本;基本概念chunk:webpack打包生成的文件是chunkmodule:module,可以是common.js模块,也可以是es6模块,也可以是amd的模块chunkId:chunkId的标识moduleId:模块require的标识。ensure:commonjs规范中按需加载代码的方法,webpack实现了该方法;其他按需加载的方法包括amd和es6中的import方法;require.ensure(dependencies,callback)参考文章:https://github.com/webpack/do...这里我只讲解require.ensure函数的第一个参数,在执行第二个参数(回调函数)之前,已经加载了第一个参数的依赖,但加载后不会执行。这与amd规范不同。amd规范在依赖加载完成后执行;一个例子。以下所有内容均基于webpack0.1版本。如果使用其他版本,打包结果类似,不影响对webpack打包原理的理解。目录结构:代码webpack.config.jsvarpath=require("path");module.exports={mode:'development',entry:path.resolve(__dirname,'./index.js'),output:{path:path.resolve(__dirname,'dist'),文件名:'[name].js'}}index.jsvara=require("./src/a");varb=require("./src/b");要求。ensure([],function(require){require("./src/b").xyz();vard=require("./src/d");});a.js//模块ab。js//modulebc.js//modulecd.js//moduled打包代码:main.js(function(modules){//webpackBootstrap//模块缓存varinstalledModules={};//存放加载和加载的对象加载块//“0”表示“已经加载”//数组表示“加载”,数组包含回调varinstalledChunks={0:0};//require函数functionrequire(moduleId){//检查模块是否在缓存if(installedModules[moduleId])返回installedModules[moduleId].exports;//创建一个新模块(并将其放入缓存)varmodule=installedModules[moduleId]={exports:{},id:moduleId,loaded:false};//执行模块函数modules[moduleId].call(null,module,module.exports,require);//将模块标记为已加载module.loaded=true;//返回模块的导出returnmodule.exports;}//这个文件只包含入口块。//附加块的块加载函数require.e=functionrequireEnsure(chunkId,callback){//“0”是“已经加载”的信号if(installedChunks[chunkId]===0)returncallback.call(空,需要);//数组表示“当前正在加载”。if(installedChunks[chunkId]!==undefined){installedChunks[chunkId].push(回调);}else{//开始加载块installedChunks[chunkId]=[callback];varhead=document.getElementsByTagName('head')[0];varscript=document.createElement('脚本');script.type='文本/javascript';script.charset='utf-8';script.src=modules.c+""+chunkId+".js";head.appendChild(脚本);}};//暴露模块对象(__webpack_modules__)require.modules=modules;//暴露模块缓存require.cache=installedModules;//为块加载安装JSONP回调window["webpackJsonp"]=functionwebpackJsonpCallback(chunkIds,moreModules){//将“moreModules”添加到模块对象,//然后将所有“chunkIds”标记为已加载并触发回调varmoduleId,chunkId,回调=[];while(chunkIds.length){chunkId=chunkIds.shift();if(installedChunks[chunkId])callbacks.push.apply(callbacks,installedChunks[chunkId]);installedChunks[chunkId]=0;}for(moreModules中的moduleId){modules[moduleId]=moreModules[moduleId];}while(callbacks.length)callbacks.shift().call(null,require);};//加载入口模块并返回出口returnrequire(0);})/******************************************************************************/({//__webpack_public_path__c:"",0:function(module,exports,require){vara=require(1);varb=require(2);require.e/*nsure*/(1,function(require){require(2).xyz();vard=require(3);});},1:function(module,exports,require){//模块a},2:function(module,exports,require){//模块b}})1.jswebpackJsonp([1],{/***/3:/***/function(module,exports,require){exports.lzy=123;//模块d/***/}})两个文件一共130多行代码。看完这些代码,你就可以基本了解webpack打包的原理了。分析一下打包后的文件:打包后生成了2个chunk,1.js和main.js,其中1.js是异步加载的chunk;chunk分析main.js是一个自执行函数,自执行函数的参数是一个对象;对象的key为moduleId,对象的value为函数,对应A模块;webpack将所有的模块打包成一个函数,可以看到moduleId为0、1、2依次对应index.js、a.js、b.js;d.js对应的模块呢,不用着急,请看异步1.jschunk,里面包含了d.js,说明d.js是异步加载的;由于webpack将每个文件打包成一个模块,我们分析几个关键函数require(模块加载),模块异步加载require.e,加载成功后执行的回调函数webpackJsonprequire方法很简单,不用几行代码,应该是可以理解,传入moduleId,根据moduleId找到对应的模块,调用require.e方法。这个方法可以说,你可以看到如何通过插入脚本来异步加载文件来避免重复加载。比如引入了d.js。require.ensure如何避免重复加载?有了问题,可以理解installedChunks[chunkId]有三种状态:installedChunks[chunkId]==0;说明脚本已经加载完毕,在webpackJsonp的方法installedChunks[chunkId]中有设置installedChunks[chunkId]为0的操作!==undefined,表示脚本正在加载但尚未执行。这时候在数组中加入一个回调函数;installedChunks[chunkId]===undefined,表示第一次加载,需要创建脚本导入脚本,为异步chunkwebpackJsonp脚本加载成功后,执行回调函数;可以看出主要逻辑是执行回调函数,为什么要这样命名:webpackJsnop,我觉得真的很像jsnop的回调函数,require.ensure的回调是在webpackJsonp函数中执行的,jsonp的回调函数接收服务器返回的数据。webpack打包的原理说完了。如果还是不明白,就从头开始运行上面的代码;