现实 webpack 的打包产物

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

实现一个简版的webpack

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

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

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

找依赖

  1. const fs = require('fs');
  2. const path = require('path');
  3. const parser = require('@babel/parser');
  4. const traverse = require('@babel/traverse').default;
  5. const { transformFromAST } = require('@babel/core');
  6.  
  7. // 分析一个文件,转成CommonJS Module,并找出它的依赖
  8. function readCode(filePath) {
  9. // 读取文件字符串
  10. const content = fs.readFileSync(filePath, 'utf-8');
  11. // 语法解析成 AST
  12. const ast = parser(content, {
  13. sourceType: 'module'
  14. })
  15. // 获取本文件的依赖
  16. const dependiences = [];
  17. // 遍历 AST,每当触发依赖钩子,就往依赖数组添加
  18. traverse(ast, {
  19. ImportDeclaration({node}) {
  20. // 把对应的以来路径存起来
  21. dependiences.push(node.source.value)
  22. }
  23. })
  24. // 把 es6 转成 es5 字符串
  25. // 最重要的是把 esModule 的 import export,转成 es5 能认识的 commonJs写法
  26. const { code } = transformFromAST(ast, null, {
  27. presets: ['@babel/preset-env']
  28. })
  29. return {
  30. filePath,
  31. code,
  32. dependiences
  33. }
  34. }
  35.  
  36. // 广度优先算法,深入找出所有的依赖
  37. function getAllDependencies(filePath) {
  38. const entryObj = readCode(filePath);
  39. const dependencies = [entryObj];
  40. for (const dependency of dependencies) {
  41. const curDirname = path.dirname(dependency.filePath)
  42. for (const relativePath dependency.dependencies) {
  43. const absolutePath = path.join(curDirname, relativePath);
  44. const child = readCode(absolutePath);
  45. child.relativePath = relativePath;
  46. dependencies.push(child);
  47. }
  48. }
  49. return dependencies;
  50. }

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

拼写立即执行函数

  1. function bundle(fileName) {
  2. const dependencies = getAllDependencies(fileName);
  3. const modulesStr = '';
  4. dependencies.forEach(dependency => {
  5. const key = dependency.relativePath || dependency.filePath;
  6. modulesStr += `'${key}': function(module, exports, require) {
  7. ${ dependency.code }
  8. }`
  9. })
  10. return `(function(modules) {
  11. const installedModules = {};
  12. function require(id) {
  13. // 解决循环依赖
  14. if (installedModules[id]) {
  15. return installedModules[id].exports;
  16. }
  17. var module = installedModules[id] = {exports: {}};
  18. modules[id].call(module.exports, module, module.exports, require);
  19. return module.exports;
  20. }
  21. return require('${fileName}')
  22. })({${modulesStr}})`
  23. }

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

  1. .NET Core的文件系统[5]:扩展文件系统构建一个简易版“云盘”

    FileProvider构建了一个抽象文件系统,作为它的两个具体实现,PhysicalFileProvider和EmbeddedFileProvider则分别为我们构建了一个物理文件系统和程序集内嵌文 ...

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

    为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]> ...

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

    本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章(<控制反转>.<基于IoC的设计模式>和< 依赖注入模式>)从纯理论的角度 ...

  4. .NET CORE学习笔记系列(2)——依赖注入[4]: 创建一个简易版的DI框架[上篇]

    原文https://www.cnblogs.com/artech/p/net-core-di-04.html 本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章从 ...

  5. 手动实现一个简易版SpringMvc

    版权声明:本篇博客大部分代码引用于公众号:java团长,我只是在作者基础上稍微修改一些内容,内容仅供学习与参考 前言:目前mvc框架经过大浪淘沙,由最初的struts1到struts2,到目前的主流框 ...

  6. 如何实现一个简易版的 Spring - 如何实现 Setter 注入

    前言 之前在 上篇 提到过会实现一个简易版的 IoC 和 AOP,今天它终于来了...相信对于使用 Java 开发语言的朋友们都使用过或者听说过 Spring 这个开发框架,绝大部分的企业级开发中都离 ...

  7. 如何实现一个简易版的 Spring - 如何实现 Constructor 注入

    前言 本文是「如何实现一个简易版的 Spring」系列的第二篇,在 第一篇 介绍了如何实现一个基于 XML 的简单 Setter 注入,这篇来看看要如何去实现一个简单的 Constructor 注入功 ...

  8. 如何实现一个简易版的 Spring - 如何实现 @Component 注解

    前言 前面两篇文章(如何实现一个简易版的 Spring - 如何实现 Setter 注入.如何实现一个简易版的 Spring - 如何实现 Constructor 注入)介绍的都是基于 XML 配置文 ...

  9. 使用 js 和 Beacon API 实现一个简易版的前端埋点监控 npm 包

    使用 js 和 Beacon API 实现一个简易版的前端埋点监控 npm 包 前端监控,埋点,数据收集,性能监控 Beacon API https://caniuse.com/beacon 优点,请 ...

随机推荐

  1. oracle中的CURRVAL和NEXTVAL用法

    原文:https://blog.csdn.net/qianyiyiding/article/details/51592689  1.什么是sequence?其作用是什么? 在Oracle数据库中,什么 ...

  2. 记录用到的mssql的几个方法

    1.RIGHT ( character_expression , integer_expression ) 返回字符串中从右边开始指定个数的字符 character_expression 字符或二进制 ...

  3. attr()与prop()区分图

  4. stack + positioned

    stack 下套container, 发现最大的显示,小的都没显示, 把所有都套个POSITIONED, 都正常显示了.

  5. 关于阿里 iconfont 的使用步骤

    第一步: 在iconfont库中,找到你想要的图标,加入到购物车,再在购物车中将图标加入到你的项目中去    第二步: 在项目中,可以看到刚刚加入的图标,这里是你在项目中所有用到的iconfont,选 ...

  6. STM32F10x芯片类型 STM32F10X_LD STM32F10X_MD STM32F10X_HD STM32F10X_XL STM32F10X_CL

    stm32f10x.h 固件库stm32f10x.h中有如下解释 #if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) & ...

  7. oracle in和exists区别

    in和exists http://oraclemine.com/sql-exists-vs-in/ https://www.techonthenet.com/oracle/exists.php htt ...

  8. Java 进阶面试问题必备

    面向对象编程的基本理念与核心设计思想 解释下多态性(polymorphism),封装性(encapsulation),内聚(cohesion)以及耦合(coupling). 继承(Inheritanc ...

  9. Chrome快捷键统计

    Chrome快捷键: Chrome 个人常用快捷键 1 将当前网页保存为书签 Ctrl + d 2 重新加载当前网页 Ctrl + r或F5 3 打开书签管理器 Ctrl + Shift + o 4 ...

  10. 微信小程序(小游戏)后台开发

    小程序开放接口功能,目的是方便小程序接入第三方服务器,比如,商城类小程序,小游戏,需要保存订单数据,玩家信息等.那就需要服务器和数据库, 开发者对于各方关系必须要理清,那就是小程序,用户,开发者服务器 ...