前端面試寶典---webpack原理解析,并有簡化版源碼

前言

先看一下webpack打包后的bundle.js,前邊的直接掃一眼就過,可以發現這個立即執行函數的形參就是一個,key為引入文件路徑,value為該模塊代碼的函數。
所以比較重要的就是通過webpack的配置文件中的entry的入口文件,遞歸去生成這個modules,并把代碼中require變成__webpack_require__。

(function (modules) { // webpackBootstrap// The module cachevar installedModules = {}// The require functionfunction __webpack_require__ (moduleId) {// Check if module is in cacheif (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: {}}// Execute the module functionmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__)// Flag the module as loadedmodule.l = true// Return the exports of the modulereturn 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 valueif ((mode & 4) && typeof value === 'object' && value && value.__esModule) return valuevar 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 exportsreturn __webpack_require__(__webpack_require__.s = "./src/app.js")})({"./src/app.js":(function (module, __webpack_exports__, __webpack_require__) {"use strict"__webpack_require__.r(__webpack_exports__)var _module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./module */ "./src/module.js")var _module__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_module__WEBPACK_IMPORTED_MODULE_0__)console.log("Hello World")}),"./src/module.js":(function (module, exports) {module.exports = {name: 'module',description: 'module description',version: '1.0.0',dependencies: {'module-a': '1.0.0','module-b': '1.0.0',},devDependencies: {'module-c': '1.0.0','module-d': '1.0.0',},}})});
//# sourceMappingURL=bundle.js.map

實現思路

項目配置如下
在這里插入圖片描述

  1. 讀取webpack.config.js文件

  2. 對入口文件實現編譯生成依賴對象modules
    2.1 根據入口文件遞歸去獲取依賴及其代碼,并通過ast抽象語法書,對require替換成__webpack_require__
    2.2 復制webpack打包生成打的bundles.js 將其改造成模板文件(bundlejsTemplate.ejs),通過ejs,把modules 插入模板中,生成代碼

  3. 將替換后的模板代碼生成到webpack.config.js配置的output路徑下

具體實現

index.js

#! /usr/bin/env node
/*
* 實現 webpack 打包功能
* 1. 配置文件的讀取 webpack.config.js
*
* 2. 實現入口文件的編譯,然后生成依賴對象 modules
*
*
* */
// console.log('jdpack打包功能');
// console.log(process.cwd()); // 打印當前命令所處的目錄/*
* 1. 配置文件的讀取 webpack.config.js
* */
const Compiler = require('../lib/Compiler.js');const path = require('path');
const configPath = path.resolve(process.cwd(), 'webpack.config.js');
const configObj = require(configPath);// console.log(configObj); // 配置文件對象/*
* 2. 實現入口文件的編譯,然后生成依賴對象 modules
* */
const compile = new Compiler(configObj);
compile.run();
// console.log(compile.modules); // 模塊依賴對象

Compiler.js (最重要的實現都在這個類里)

/*
* 編譯我們的代碼,生成 打包后的內容
* 1. 根據配置文件 entry 入口文件,讀取入口文件對象的代碼
* 2. 生成依賴對象
* 3. 傳遞給 webpack的 自執行的匿名函數
*
* */
const fs = require('fs');
const ejs = require('ejs');
const path = require('path');/*
* 導入ast相關的模塊
* */
const {parse} = require('@babel/parser');
const generator = require('@babel/generator').default;
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');class Compiler {/** 配置文件* */constructor(config) {this.config = config;// 模塊依賴對象,保存了代碼里面的所有的模塊依賴關系 key 依賴模塊的路徑 value 依賴模塊對應代碼的函數this.modules = {};}run() {// 1. 根據配置文件的入口文件生成依賴對象this.buildModules(this.config.entry);// 編譯后,生成 bundle.jsthis.generatorBundlejs();}/** moduleId 依賴模塊的路徑** 如果在 代碼里面有 require其他的模塊代碼* 1. 先生成模塊依賴對象* 2. 將代碼里面的 require 替換為 __webpack_require__ ast 實現* 3. 將依賴模塊的路徑加載入口文件的目錄** */buildModules(moduleId) {let code = this.getCode(moduleId);let {deps, newCode} = this.parseModule(code);// console.log(deps, newCode);this.modules[moduleId] = newCode;// 針對 deps 里面再次做處理,引入依賴的文件里面還有可能 requiredeps.forEach(item => {this.buildModules(item);})}/** path 依賴模塊的路徑* */getCode(modulePath) {return fs.readFileSync(modulePath, 'utf8');}/** 將代碼里面的依賴做替換* */parseModule(code) {let mainRootPath = path.dirname(this.config.entry);// 存儲了代碼里面所有的依賴路徑let deps = [];const ast = parse(code);/** 1. 對 require 節點做處理,替換 __webpack_require__* */traverse(ast, {CallExpression(NodePath) {let node = NodePath.node;if (node.callee.name === 'require') {node.callee.name = '__webpack_require__';// 2. 對依賴路徑做替換let depPath = node.arguments[0].value;depPath = '.\\' + path.join(mainRootPath, depPath);depPath = depPath.replace(/\\/g, '/');// 利用語法樹將 require 里面依賴路徑做修改node.arguments[0] = t.stringLiteral(depPath);deps.push(depPath);}}});let newCode = generator(ast).code;// console.log(newCode);return {deps, newCode};}/** 先根據生成的入口文件的依賴對象,生成打包文件。然后在 分析入口文件里面的內容,如果有其他的 require 進行再次生成依賴對象,在生成打包的文件* */generatorBundlejs() {/** 使用 ejs 根據依賴對象,生成打包后的 bundle.js 文件* 1. 讀取模板*** */let bundlePath = path.resolve(__dirname, 'bundlejsTemplate.ejs');let bundleTemplate = fs.readFileSync(bundlePath, 'utf-8');/** 2. 使用 ejs 做模板的替換* */let renderCode = ejs.render(bundleTemplate, {moduleId: this.config.entry, modules: this.modules});/** 3. 將打包后的內容根據 webpack.config.js 里面的 output 進行保存* */let outputPath = this.config.output.path;// 判斷打包后的輸出目錄是否存在,如果不存在,則先創建目錄if (!fs.existsSync(outputPath)) {fs.mkdirSync(outputPath);}let outputFilePath = path.resolve(outputPath, this.config.output.filename);fs.writeFileSync(outputFilePath, renderCode);}}module.exports = Compiler;

bundlejsTemplate.ejs

(function(modules) { // webpackBootstrap
// The module cache
var installedModules = {};// The require function
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: {}
};// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);// Flag the module as loaded
module.l = true;// Return the exports of the module
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 = "<%- moduleId %>");
})
/************************************************************************/
({
<% for(let key in modules) { %>"<%- key %>": (function(module, exports, __webpack_require__) {<%- modules[key] %>}),
<% } %>});

package.json

{"name": "jdpack","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"bin": {"mypack": "./bin/index.js"},"keywords": [],"author": "","license": "ISC","dependencies": {"@babel/generator": "^7.18.13","@babel/parser": "^7.18.13","@babel/traverse": "^7.18.13","@babel/types": "^7.18.13","ejs": "^3.1.8"}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/79261.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/79261.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/79261.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

面試的各種類型

面試是用人單位選拔人才的重要環節&#xff0c;常見的面試類型有結構化面試、半結構化面試、非結構化面試和壓力面試&#xff0c;每種類型都有其特點和應對策略。 一、結構化面試 特點&#xff1a; 標準化流程 面試流程固定&#xff0c;考官會按照預先設計好的問題清單依次向…

vue3定義全局防抖指令

文章目錄 代碼參數講解 在寫項目時&#xff0c;總會有要進行防抖節流的時候&#xff0c;如果寫一個debounce函數的話 用起來代碼總會是有點長的&#xff0c;因此想到了用一個全局指令進行輸入框的防抖&#xff0c;畢竟全局指令使用時只要v-xxx就行了&#xff0c;非常方便 代碼…

WebDeveloper 流量分析、sudo提權,靶場通關WP

一、信息收集 1、主機探測 arp-scan -l netdiscover -i eth0 -r 192.168.33.0/24 nmap -sP 192.168.66.0/24 2、端口掃描 nmap -sS -sV 192.168.66.141 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0) 80/tcp op…

某化工廠運維升級:智和信通運維平臺實現工業交換機智能管理

隨著某化工廠數字化轉型的加速推進&#xff0c;其生產過程對復雜網絡和IT設備的依賴程度日益加深。當前的網絡不僅承載著生產控制系統&#xff08;如DCS、PLC等&#xff09;的通信需求&#xff0c;還同時支持辦公自動化、安防監控、工業物聯網&#xff08;IoT&#xff09;等多種…

React:封裝一個編輯文章的組件

封裝一個編輯文章的組件,就要用到富文本編輯器,支持標題、內容、標簽等的編輯,并且能夠保存和取消。 首先,我需要考慮用戶的具體需求。編輯文章組件通常需要哪些功能?標題輸入、內容編輯、標簽管理、保存和取消按鈕。可能還需要自動保存草稿、驗證輸入、錯誤提示等功能。用…

數據結構與算法:圖論——并查集

先給出并查集的模板&#xff0c;還有一些leetcode算法題&#xff0c;以后遇見了相關題目再往上增加 并查集模板 整體模板C代碼如下&#xff1a; 空間復雜度&#xff1a; O(n) &#xff0c;申請一個father數組。 時間復雜度 路徑壓縮后的并查集時間復雜度在O(logn)與O(1)之間…

精品推薦-湖倉一體電商數據分析平臺實踐教程合集(視頻教程+設計文檔+完整項目代碼)

精品推薦&#xff0c;湖倉一體電商數據分析平臺實踐教程合集&#xff0c;包含視頻教程、設計文檔及完整項目代碼等資料&#xff0c;供大家學習。 1、項目背景介紹及項目架構 2、項目使用技術版本及組件搭建 3、項目數據種類與采集 4、實時業務統計指標分析一——ODS分層設計與…

Git 基本操作(一)

目錄 git add git commit git log git status git diff git 版本回退 git reset git add git add 指令為添加工作區中的文件到暫存區中。 git add file_name; //將工作區名稱為file_name的文件添加進暫存區 git add .; //將工作區中的所有文件添加進暫存區 git comm…

docker打包鏡像時提示permission denied

sudo usermod -aG docker $USER //讓當前用戶加入docker用戶組 sudo systemctl restart docker //重新啟動docker服務 newgrp docker //更新組權限 來源&#xff1a;docker命令出現permission denied的解決方法_permission denied while trying to connect…

Deepseek常用高效提問模板!

DeepSeek高效提問秘籍大放送&#xff01; 掌握這些實用提問模板&#xff0c;能讓你與DeepSeek的對話更加精準、高效&#xff01; 1. 精準闡述需求 提問時務必清晰明確地表達問題或任務。例如&#xff1a; 欠佳的提問&#xff1a;“隨便說點內容。”優化后的提問&#xff1a…

地震資料偏移成像中,多次波(多次反射波)處理

在地震資料偏移成像中&#xff0c;多次波&#xff08;多次反射波&#xff09;會降低成像質量&#xff0c;導致虛假同相軸和構造假象。處理多次波需要結合波場分離和壓制技術&#xff0c;以下是主要方法和開源算法參考&#xff1a; 1. 多次波處理的核心方法 (1) 基于波場分離的…

quickbi finebi 測評(案例講解)

quickbi & finebi 測評 國產BI中入門門檻比較低的有兩個&#xff0c;分別是quickbi和finebi。根據我的經驗通過這篇文章做一個關于這兩款BI的測評文章。 quickbi分為個人版、高級版、專業版、私有化部署四種。這篇文章以quickbi高級版為例&#xff0c;對quickbi進行分享。…

【進階】--函數棧幀的創建和銷毀詳解

目錄 一.函數棧幀的概念 二.理解函數棧幀能讓我們解決什么問題 三.相關寄存器和匯編指令知識點補充 四.函數棧幀的創建和銷毀 4.1.調用堆棧 4.2.函數棧幀的創建 4.3 函數棧幀的銷毀 一.函數棧幀的概念 --在C語言中&#xff0c;函數棧幀是指在函數調用過程中&#xff0c;…

基于大模型預測的輸尿管癌診療全流程研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與創新點 二、大模型預測輸尿管癌的原理與方法 2.1 大模型技術概述 2.2 用于輸尿管癌預測的大模型選擇 2.3 數據收集與處理 2.4 模型訓練與優化 三、術前風險預測與手術方案制定 3.1 術前風險預測指標 3.2 大模型預測…

【Machine Learning Q and AI 讀書筆記】- 03 小樣本學習

Machine Learning Q and AI 中文譯名 大模型技術30講&#xff0c;主要總結了大模型相關的技術要點&#xff0c;結合學術和工程化&#xff0c;對LLM從業者來說&#xff0c;是一份非常好的學習實踐技術地圖. 本文是Machine Learning Q and AI 讀書筆記的第3篇&#xff0c;對應原…

PETR和位置編碼

PETR和位置編碼 petr檢測網絡中有2種類型的位置編碼。 正弦編碼和petr論文提出的3D Position Embedding。transformer模塊輸入除了qkv&#xff0c;還有query_pos和key_pos。這里重點記錄下query_pos和key_pos的生成 query pos的生成 先定義reference_points, shape為(n_query…

Ubuntu搭建 Nginx以及Keepalived 實現 主備

目錄 前言1. 基本知識2. Keepalived3. 腳本配置4. Nginx前言 ?? 找工作,來萬碼優才:?? #小程序://萬碼優才/r6rqmzDaXpYkJZF 爬蟲神器,無代碼爬取,就來:bright.cn Java基本知識: java框架 零基礎從入門到精通的學習路線 附開源項目面經等(超全)【Java項目】實戰CRU…

文章記單詞 | 第56篇(六級)

一&#xff0c;單詞釋義 interview /??nt?vju?/&#xff1a; 名詞&#xff1a;面試&#xff1b;采訪&#xff1b;面談動詞&#xff1a;對… 進行面試&#xff1b;采訪&#xff1b;接見 radioactive /?re?di???kt?v/&#xff1a;形容詞&#xff1a;放射性的&#xff…

MATLAB函數調用全解析:從入門到精通

在MATLAB編程中&#xff0c;函數是代碼復用的核心單元。本文將全面解析MATLAB中各類函數的調用方法&#xff0c;包括內置函數、自定義函數、匿名函數等&#xff0c;幫助提升代碼效率&#xff01; 一、MATLAB函數概述 MATLAB函數分為以下類型&#xff1a; 內置函數&#xff1a…

哈希表筆記(二)redis

Redis哈希表實現分析 這份代碼是Redis核心數據結構之一的字典(dict)實現&#xff0c;本質上是一個哈希表的實現。Redis的字典結構被廣泛用于各種內部數據結構&#xff0c;包括Redis數據庫本身和哈希鍵類型。 核心特點 雙表設計&#xff1a;每個字典包含兩個哈希表&#xff0…