如何实现一个webpack模块解析器


Posted in Javascript onOctober 24, 2018

最近在学习 webpack源码,由于源码比较复杂,就先梳理了一下整体流程,就参考官网的例子,手写一个最基本的 webpack 模块解析器。

代码很少,github地址:手写webpack模块解析器

整体流程分析

1、读取入口文件。

2、将内容转换成 ast 语法树。

3、深度遍历语法树,找到所有的依赖,并加入到一个数组中。

4、将 ast 代码转换回可执行的 js 代码。

5、编写 require 函数,根据入口文件,自动执行完所有的依赖。

6、输出运行结果。

createAsset

// 读取内容并提取它的依赖关系
function createAsset(filename) {
 // 以字符串的形式读取文件
 const content = fs.readFileSync(filename, "utf-8");

 // 转换字符串为ast抽象语法树
 const ast = babylon.parse(content, {
  sourceType: "module"
 });

 const dependencies = [];

 // 遍历抽象语法树
 traverse(ast, {
  // 每当遍历到import语法的时候
  ImportDeclaration: ({ node }) => {
   // 把依赖的模块加入到数组中
   dependencies.push(node.source.value);
  }
 });

 const id = ID++;

 // 转换为浏览器可运行的代码
 const { code } = babel.transformFromAstSync(ast, null, {
  presets: ["@babel/preset-env"]
 });

 return {
  id,
  filename,
  dependencies,
  code
 };
}

createGraph

// 从入口开始,分析所有依赖项,形成依赖图,采用深度优先遍历
function createGraph(entry) {
 const mainAsset = createAsset(entry);

 // 定义一个保存依赖项的数组
 const queue = [mainAsset];

 for (const asset of queue) {
  const dirname = path.dirname(asset.filename);

  // 定义一个保存子依赖项的属性
  asset.mapping = {};

  asset.dependencies.forEach(relativePath => {
   const absolutePath = path.join(dirname, relativePath);

   const child = createAsset(absolutePath);

   // 给子依赖项赋值
   asset.mapping[relativePath] = child.id;

   // 将子依赖也加入队列中,循环处理
   queue.push(child);
  });
 }
 return queue;
}

bundle

// 根据生成的依赖关系图,生成浏览器可执行文件
function bundle(graph) {
 let modules = "";

 // 把每个模块中的代码放在一个function作用域内
 graph.forEach(mod => {
  modules += `${mod.id}:[
   function (require, module, exports){
    ${mod.code}
   },
   ${JSON.stringify(mod.mapping)},
  ],`;
 });

 // require, module, exports 不能直接在浏览器中使用,这里模拟了模块加载,执行,导出操作。
 const result = `
  (function(modules){
   // 创建一个require()函数: 它接受一个 模块ID 并在我们之前构建的模块对象查找它.
   function require(id){
    const [fn, mapping] = modules[id];

    function localRequire(relativePath){
     // 根据mapping的路径,找到对应的模块id
     return require(mapping[relativePath]);
    }

    const module = {exports:{}};

    // 执行转换后的代码,并输出内容。
    fn(localRequire,module,module.exports);

    return module.exports;
   }

   // 执行入口文件
   require(0);

  })({${modules}})
 `;

 return result;
}

执行解析

const graph = createGraph("./entry.js");
const result = bundle(graph);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
HTML5+setCutomValidity()函数验证表单实例分享
Apr 24 Javascript
浅谈jQuery构造函数分析
May 11 Javascript
JS基于Ajax实现的网页Loading效果代码
Oct 27 Javascript
js实现iframe框架取值的方法(兼容IE,firefox,chrome等)
Nov 26 Javascript
jQuery内容过滤选择器用法示例
Sep 09 Javascript
JS中作用域和变量提升(hoisting)的深入理解
Oct 31 Javascript
JavaScript函数节流和函数防抖之间的区别
Feb 15 Javascript
Angular 4.x 动态创建表单实例
Apr 25 Javascript
深入理解node.js http模块
Jan 24 Javascript
手写Node静态资源服务器的实现方法
Mar 20 Javascript
vue.js的双向数据绑定Object.defineProperty方法的神奇之处
Jan 18 Javascript
使用Vue.js和MJML创建响应式电子邮件
Mar 23 Vue.js
vue项目中使用Svg的方法
Oct 24 #Javascript
js中获取URL参数的共用方法getRequest()方法实例详解
Oct 24 #Javascript
小程序云开发初探(小结)
Oct 24 #Javascript
vue-cli V3.0版本的使用详解
Oct 24 #Javascript
vue+axios 前端实现登录拦截的两种方式(路由拦截、http拦截)
Oct 24 #Javascript
vue 属性拦截实现双向绑定的实例代码
Oct 24 #Javascript
深入理解JavaScript的值传递和引用传递
Oct 24 #Javascript
You might like
解析php二分法查找数组是否包含某一元素
2013/05/23 PHP
19个Android常用工具类汇总
2014/12/30 PHP
PHP传值到不同页面的三种常见方式及php和html之间传值问题
2015/11/19 PHP
PHP Primary script unknown 解决方法总结
2019/08/22 PHP
Javascript与jQuery方法的隐藏与显示
2015/01/19 Javascript
jQuery对指定元素中指定字符串进行替换的方法
2015/03/17 Javascript
AngularJS 实现按需异步加载实例代码
2015/10/18 Javascript
全面解析JavaScript中的valueOf与toString方法(推荐)
2016/06/14 Javascript
jQuery 实现图片的依次加载图片功能
2017/07/06 jQuery
如何快速解决JS或Jquery ajax异步跨域的问题
2018/01/08 jQuery
浅析Vue中method与computed的区别
2018/03/06 Javascript
vue input标签通用指令校验的实现
2019/11/05 Javascript
[49:15]DOTA2-DPC中国联赛 正赛 CDEC vs XG BO3 第二场 1月19日
2021/03/11 DOTA
基于wxpython实现的windows GUI程序实例
2015/05/30 Python
Python写的一个简单监控系统
2015/06/19 Python
使用paramiko远程执行命令、下发文件的实例
2017/10/01 Python
python实现简易内存监控
2018/06/21 Python
pygame实现俄罗斯方块游戏
2018/06/26 Python
Python多进程fork()函数详解
2019/02/22 Python
python pandas模块基础学习详解
2019/07/03 Python
python numpy中cumsum的用法详解
2019/10/17 Python
Python队列、进程间通信、线程案例
2019/10/25 Python
tensorflow 实现从checkpoint中获取graph信息
2020/02/10 Python
记录模型训练时loss值的变化情况
2020/06/16 Python
Python使用pyexecjs代码案例解析
2020/07/13 Python
Python+OpenCV检测灯光亮点的实现方法
2020/11/02 Python
python制作微博图片爬取工具
2021/01/16 Python
CSS3自定义滚动条样式的示例代码
2017/08/21 HTML / CSS
中国入世承诺
2014/04/01 职场文书
小学生放飞梦想演讲稿
2014/08/26 职场文书
四风问题个人剖析材料
2014/10/07 职场文书
出纳工作检讨书范文
2014/12/27 职场文书
大学生入党群众意见书
2015/06/02 职场文书
用python开发一款操作MySQL的小工具
2021/05/12 Python
浅谈Python数学建模之整数规划
2021/06/23 Python
Java基础之this关键字的使用
2021/06/30 Java/Android