webpack原理解析(一)实现一个简单的webpack

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: Webpack 是当下最热门的前端资源模块化管理和打包工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。webpack如此强大,其内部机制到底是怎样的,今天我们来一探究竟。

准备工作


新建一个目录,执行npm init -y 初始化一个package.json文件


新建src下index.js,作为入口文件,export.js作为依赖的模块


新建webpack.config.js,这里就按照webpack4的基础配置写一下


新建bundle.js作为入口进行webpack构建 创建lib下webpack.js


根据配置读取入口文件


bundle.js:


//读取到webpackd 的配置文件
const options = require("./webpack.config.js")
const Webpack = require("./lib/webpack.js")
new Webpack(options).run()


webpack.js 这里写了一个Webpack类,bundle.js实例化这个类,将参数(引入的基础配置的内容)传进来并执行run方法


module.exports = class Webpack {
    constructor(options){
        console.log(options)
    }
    run(){
        console.log("hello")
    }
}


此时执行node bundle.js :



已经可以根据基础配置读取到入口模块了


分析出依赖模块


我这里写的index.js是这样:


import { sayHi } from './export.js'
sayHi("hfj")
console.log("hello webpack index")


此时需要借助node的核心模块-- filesystem来读到内容


const conts = fs.readFileSync(entryFile,'utf-8')


接下来借助@babel/parser(记得自己安装和引入。。)把内容抽象成语法树


const ast = parser.parse(conts, {
            sourceType: "module"
          });


打印下ast.program.body



看type,哪里是依赖模块,哪里是表达式,已经很明显了,接下来通过@babel/traverse来提取依赖模块:


const dependencies = {}
          traverse(ast,{
            ImportDeclaration({node}){
                const newPath = "./" + path.join(
                    path.dirname(entryFile),
                    node.source.value
                    )
                dependencies[node.source.value] = newPath
                console.log(dependencies)
            }
        })


注意下,这里做了一个处理,将依赖模块以对象的形式放到了dependencies里。


编译内容


接下来,通过@babel/core将ast处理成内容


const {code} = transformFromAst(ast,null,{
            presets: ["@babel/preset-env"]
        })
        console.log(code)


打印的code如下:



内容成功拿到了!


遍历依赖模块


以上是对入口文件进行的编译,接下来还要看入口模块的依赖模块有哪些,分别按照刚才的方法进行内容分析,最后做一个汇总(根据dependence来判断的是否还有依赖模块):


run(){
       const info = this.analysis(this.entry)
        this.modulesArr.push(info)
        for(let i=0;i<this.modulesArr.length;i++) {
            const item = this.modulesArr[i]
            const { dependencies } = item;
            if(dependencies) {
                for(let j in dependencies){
                    this.modulesArr.push(this.analysis(dependencies[j]))
                }
            }
        }
        // console.log(this.modules)
        //数组结构转换
        const obj = {}
        this.modulesArr.forEach((item) => {
            obj[item.entryFile] = {
                dependencies:item.dependencies,
                code:item.code
            }
        })
    }


最终是将modulesArr转化成了对象,方便后续处理


输出浏览器可执行的js代码


此处划重点! webpack最终是将模块化的js打包成一个chunk,打包后的js到dist(webpack基础配置里设置的output)


首先用了fs.writeFileSync将生成的bundle写入filePath(filePath之前有在this.output保存) 接下来就是处理obj(上一步骤中的汇总后的模块)了:


核心是用eval()来执行obj中的code,同时需要处理requrie和export require和export通过形参传入,之后执行自己写的reqire和export require处理了模块路径,exports是一个对象,看源码--


const bundle = `(function(graph){
            function require(moduleId){
                function localRequire(relativePath){
                   return require(graph[moduleId].dependencies[relativePath]) 
                }
                var exports = {};
                (function(require,exports,code){
                    eval(code)
                })(localRequire,exports,graph[moduleId].code)
                return exports;
            }
            require('${this.entry}')
        })(${newCode})`


执行node bundle.js,在dist文件夹下输出如下js:


(function(graph){
            function require(moduleId){
                function localRequire(relativePath){
                   return require(graph[moduleId].dependencies[relativePath]) 
                }
                var exports = {};
                (function(require,exports,code){
                    eval(code)
                })(localRequire,exports,graph[moduleId].code)
                return exports;
            }
            require('./src/index.js')
        })({"./src/index.js":{"dependencies":{"./export.js":"./src\\export.js"},"code":"\"use strict\";\n\nvar _export = require(\"./export.js\");\n\n(0, _export.sayHi)(\"hfj\");\nconsole.log(\"hello webpack index\");"},"./src\\export.js":{"dependencies":{},"code":"\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.sayHi = void 0;\n\nvar sayHi = function sayHi(params) {\n  console.log(\"hello ~\" + params);\n};\n\nexports.sayHi = sayHi;"}})


在浏览器执行:输出:



完成~


总结


webpack通过入口文件逐层遍历到模块依赖,进行代码分析、代码转换,最终生成可在浏览器运行的打包后的代码


本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。


以上实现了一个简易版本的webpack


下一篇:webpack原理解析(二)loader和plugin机制


如有兴趣,欢迎进一步交流探讨~


附github地址:github.com/manli-tongh…


参考:Webpack官网



相关文章
|
18天前
|
存储 缓存 算法
HashMap深度解析:从原理到实战
HashMap,作为Java集合框架中的一个核心组件,以其高效的键值对存储和检索机制,在软件开发中扮演着举足轻重的角色。作为一名资深的AI工程师,深入理解HashMap的原理、历史、业务场景以及实战应用,对于提升数据处理和算法实现的效率至关重要。本文将通过手绘结构图、流程图,结合Java代码示例,全方位解析HashMap,帮助读者从理论到实践全面掌握这一关键技术。
63 13
|
2月前
|
运维 持续交付 云计算
深入解析云计算中的微服务架构:原理、优势与实践
深入解析云计算中的微服务架构:原理、优势与实践
73 1
|
3月前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
64 3
|
4天前
|
存储 物联网 大数据
探索阿里云 Flink 物化表:原理、优势与应用场景全解析
阿里云Flink的物化表是流批一体化平台中的关键特性,支持低延迟实时更新、灵活查询性能、无缝流批处理和高容错性。它广泛应用于电商、物联网和金融等领域,助力企业高效处理实时数据,提升业务决策能力。实践案例表明,物化表显著提高了交易欺诈损失率的控制和信贷审批效率,推动企业在数字化转型中取得竞争优势。
36 14
|
13天前
|
网络协议 安全 网络安全
探索网络模型与协议:从OSI到HTTPs的原理解析
OSI七层网络模型和TCP/IP四层模型是理解和设计计算机网络的框架。OSI模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,而TCP/IP模型则简化为链路层、网络层、传输层和 HTTPS协议基于HTTP并通过TLS/SSL加密数据,确保安全传输。其连接过程涉及TCP三次握手、SSL证书验证、对称密钥交换等步骤,以保障通信的安全性和完整性。数字信封技术使用非对称加密和数字证书确保数据的机密性和身份认证。 浏览器通过Https访问网站的过程包括输入网址、DNS解析、建立TCP连接、发送HTTPS请求、接收响应、验证证书和解析网页内容等步骤,确保用户与服务器之间的安全通信。
60 1
|
2月前
|
运维 持续交付 虚拟化
深入解析Docker容器化技术的核心原理
深入解析Docker容器化技术的核心原理
55 1
|
2月前
|
监控 前端开发 JavaScript
Webpack 中 HMR 插件的工作原理
【10月更文挑战第23天】可以进一步深入探讨 HMR 工作原理的具体细节、不同场景下的应用案例,以及与其他相关技术的结合应用等方面的内容。通过全面、系统地了解 HMR 插件的工作原理,能够更好地利用这一功能,为项目的成功开发提供有力保障。同时,要不断关注技术的发展动态,以便及时掌握最新的 HMR 技术和最佳实践。
|
2月前
|
缓存 前端开发 JavaScript
Webpack 动态加载的原理
【10月更文挑战第23天】Webpack 动态加载通过巧妙的机制和策略,实现了模块的按需加载和高效运行,提升了应用程序的性能和用户体验。同时,它也为前端开发提供了更大的灵活性和可扩展性,适应了不断变化的业务需求和技术发展。
|
2月前
|
存储 供应链 算法
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
60 0
|
2月前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
63 1

推荐镜像

更多