实现一个简易版webpack

现实 webpack 的打包产物

大概长这样(只把核心代码留下来):

实现一个简版的webpack

依葫芦画瓢,实现思路分2步:

1. 分析入口文件,把所有的依赖找出来(包括所有后代的依赖)

2. 拼接出类似上面的立即执行函数

找依赖

const fs = require(‘fs‘);
const path = require(‘path‘);
const parser = require(‘@babel/parser‘);
const traverse = require(‘@babel/traverse‘).default;
const { transformFromAST } = require(‘@babel/core‘);

// 分析一个文件,转成CommonJS Module,并找出它的依赖
function readCode(filePath) {
    // 读取文件字符串
    const content = fs.readFileSync(filePath, ‘utf-8‘);
    // 语法解析成 AST
    const ast = parser(content, {
        sourceType: ‘module‘
    })
    // 获取本文件的依赖
    const dependiences = [];
    // 遍历 AST,每当触发依赖钩子,就往依赖数组添加
    traverse(ast, {
        ImportDeclaration({node}) {
            // 把对应的以来路径存起来
            dependiences.push(node.source.value)
        }
    })
    // 把 es6 转成 es5 字符串
    // 最重要的是把 esModule 的 import export,转成 es5 能认识的 commonJs写法
    const { code } = transformFromAST(ast, null, {
        presets: [‘@babel/preset-env‘]
    })
    return {
        filePath,
        code,
        dependiences
    }
}

// 广度优先算法,深入找出所有的依赖
function getAllDependencies(filePath) {
    const entryObj = readCode(filePath);
    const dependencies = [entryObj];
    for (const dependency of dependencies) {
        const curDirname = path.dirname(dependency.filePath)
        for (const relativePath dependency.dependencies) {
            const absolutePath = path.join(curDirname, relativePath);
            const child = readCode(absolutePath);
            child.relativePath = relativePath;
            dependencies.push(child);
        }
    }
    return dependencies;
}

ps: 我们用的是babel的配套工具来做语法分析和转化,但是真正的webpack用的是webassemblyjs的配套工具

拼写立即执行函数

function bundle(fileName) {
    const dependencies = getAllDependencies(fileName);
    const modulesStr = ‘‘;
    dependencies.forEach(dependency => {
        const key = dependency.relativePath || dependency.filePath;
        modulesStr += `‘${key}‘: function(module, exports, require) {
            ${ dependency.code }
        }`
    })
    return `(function(modules) {
        const installedModules = {};
        function require(id) {
            // 解决循环依赖
            if (installedModules[id]) {
                return installedModules[id].exports;
            }
            var module = installedModules[id] = {exports: {}};
            modules[id].call(module.exports, module, module.exports, require);
            return module.exports;
        }
        return require(‘${fileName}‘)
    })({${modulesStr}})`
}

原文地址:https://www.cnblogs.com/amiezhang/p/11517294.html

时间: 2024-08-02 17:55:39

实现一个简易版webpack的相关文章

依赖注入[4]: 创建一个简易版的DI框架[上篇]

本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章(<控制反转>.<基于IoC的设计模式>和< 依赖注入模式>)从纯理论的角度对依赖注入进行了深入论述,为了让读者朋友能够更好地理解.NET Core的依赖注入框架的设计思想和实现原理,我们创建了一个简易版本的DI框架,也就是我们在前面文章中多次提及的Cat.我们会上下两篇来介绍这个被称为为Cat的DI框架,上篇介绍编程模型,下篇关注设计实现.[源代码从这里下载] 目录一.DI容器的层

依赖注入[5]: 创建一个简易版的DI框架[下篇]

为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]>中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的设计和实现. 目录一.服务注册:ServiceRegistry 二.DI容器:Cat 三.扩展方法 一.服务注册:ServiceRegistry 由于作为DI容器的Cat对象总是利用预先添加到服务注册来提供对应的服务实例,所以服务注册至关重要.如下

一个简易版的Function.prototype.bind实现

重新看<JavaScript设计模式与开发实践>一书,第32页发现个简易版的Function.prototype.bind实现,非常容易理解,记录在这了. Function.prototype.bind = function (context) { var self = this; return function () { return self.apply(context, arguments); }; }; var obj = { name: 'sven' }; var func = fu

手动实现一个简易版 tomcat(yet)

https://mp.weixin.qq.com/s?__biz=MzA5MzcxNjY4Ng==&mid=2648107477&idx=1&sn=237afdcd8dc67f3a36aac8a38fcaada9&chksm=887be074bf0c69627659d3f076420ba7d477bc08405437eafe4d99ed22e6d2f9a53c80a73d87&mpshare=1&scene=1&srcid=0606qoAMsGtii

手写一个简易版Tomcat

前言 Tomcat Write MyTomcat Tomcat是非常流行的Web Server,它还是一个满足Servlet规范的容器.那么想一想,Tomcat和我们的Web应用是什么关系? 从感性上来说,我们一般需要把Web应用打成WAR包部署到Tomcat中,在我们的Web应用中,我们要指明URL被哪个类的哪个方法所处理(不论是原始的Servlet开发,还是现在流行的Spring MVC都必须指明). 由于我们的Web应用是运行在Tomcat中,那么显然,请求必定是先到达Tomcat的.To

【react】---手动封装一个简易版的redux---【韶华】

export let createStore = (reducer)=>{ //定义默认的state let state; //定义默认的action let actionTypes = "@@redux/INIT"+Math.random(); let initAction = {type:actionTypes} //将所以需要监听的函数放在这个里面 let listeners = [] //定义getState函数 let getState = ()=>state;

Vue初体验——用Vue实现简易版TodoList

最近在学习Vue,断断续续看完了官方教程的基础部分,想练一下手熟悉一下基本用法,做了一个简易版的TodoList 效果: 代码: HTML: 1 <!DOCTYPE html> 2 <html> 3 4 <head> 5 <title>TodoList</title> 6 <meta charset="utf-8"> 7 <!-- 为了让 Bootstrap 开发的网站对移动设备友好,确保适当的绘制和触屏缩放

使用 LinkedBlockingQueue 实现简易版线程池

前一阵子在做联系人的导入功能,使用POI组件解析Excel文件后获取到联系人列表,校验之后批量导入.单从技术层面来说,导入操作通常情况下是一个比较耗时的操作,而且如果联系人达到几万.几十万级别,必须拆分成为子任务来执行.综上,可以使用线程池来解决问题.技术选型上,没有采用已有的 ThreadPoolExecutor 框架,而使用了自制的简易版线程池.该简易版的线程池,其实也是一个简易版的[生产者-消费者]模型,任务的加入就像是生产的过程,任务的处理就像是消费的过程.我们在这里不去讨论方案的合理性

简易版聊天系统实现 Socket VS NIO两种实现方式

说是简单聊天系统,压根不能算是一个系统,顶多算个雏形.本文重点不在聊天系统设计和实现上,而是通过实现类似效果,展示下NIO 和Socket两种编程方式的差异性.说是Socket与NIO的编程方式,不太严谨,因为NIO的底层也是通过Socket实现的,但又想不出非常好的题目,就这样吧. 主要内容 Socket方式实现简易聊天效果 NIO方式实现简易聊天效果 两种方式的性能对比 前言 预期效果,是客户端之间进行"广播"式聊天,类似于QQ群聊天.希望以后有机会,以此简易版为基础,不断演进,演