前端面试宝典---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.mzph.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…