如何实现一个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 相关文章推荐
js 匿名调用实现代码
Jun 19 Javascript
jQuery Ajax之load()方法
Oct 12 Javascript
使用JavaScript构建JSON格式字符串实现步骤
Mar 22 Javascript
Jquery如何实现点击时高亮显示代码
Jan 22 Javascript
javascript自定义函数参数传递为字符串格式
Jul 29 Javascript
JavaScript中判断整字类型最简洁的实现方法
Nov 08 Javascript
Bootstrap Metronic完全响应式管理模板学习笔记
Jul 08 Javascript
Bootstrap源码学习笔记之bootstrap进度条
Dec 24 Javascript
用jquery获取select标签中选中的option值及文本的示例
Jan 25 jQuery
详解关于webpack多入口热加载很慢的原因
Apr 24 Javascript
Jquery ajax书写方法代码实例解析
Jun 12 jQuery
vuex分模块后,实现获取state的值
Jul 26 Javascript
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+java实现自动新闻滚动窗口
2006/10/09 PHP
如何在smarty中增加类似foreach的功能自动加载数据
2013/06/26 PHP
php实现微信企业转账功能
2018/10/02 PHP
jQuery实现的立体文字渐变效果
2010/05/17 Javascript
JS 实现导航栏悬停效果(续2)
2013/09/24 Javascript
jquery显示隐藏input对象
2014/07/21 Javascript
利用Angular+Angular-Ui实现分页(代码加简单)
2017/03/10 Javascript
Js自定义多选框效果的实例代码
2017/07/05 Javascript
vue router嵌套路由在history模式下刷新无法渲染页面问题的解决方法
2018/01/25 Javascript
Node.js中DNS模块学习总结
2018/02/28 Javascript
浅谈angularJS2中的界面跳转方法
2018/08/31 Javascript
Vue-router 切换组件页面时进入进出动画方法
2018/09/01 Javascript
layui 数据表格复选框实现单选功能的例子
2019/09/19 Javascript
[08:42]DOTA2每周TOP10 精彩击杀集锦vol.2
2014/06/25 DOTA
零基础写python爬虫之使用Scrapy框架编写爬虫
2014/11/07 Python
详解python如何调用C/C++底层库与互相传值
2016/08/10 Python
python中MethodType方法介绍与使用示例
2017/08/03 Python
python enumerate函数的使用方法总结
2017/11/15 Python
centos 安装python3.6环境并配置虚拟环境的详细教程
2018/02/22 Python
python 删除列表里所有空格项的方法总结
2018/04/18 Python
python实现文件批量编码转换及注意事项
2019/10/14 Python
详解python中各种文件打开模式
2020/01/19 Python
python读取excel进行遍历/xlrd模块操作
2020/07/12 Python
python3中calendar返回某一时间点实例讲解
2020/11/18 Python
用python 绘制茎叶图和复合饼图
2021/02/26 Python
CSS3制作文字半透明倒影效果的两种实现方式
2014/08/08 HTML / CSS
爱尔兰灯和灯具网上商店:Lights.ie
2018/03/26 全球购物
荷兰的时尚市场:To Be Dressed
2019/05/06 全球购物
在weblogic中发布ejb需涉及到哪些配置文件
2012/01/17 面试题
土建资料员岗位职责
2014/01/04 职场文书
物业保安员岗位职责
2014/03/14 职场文书
工伤赔偿协议书范本
2014/04/15 职场文书
企业职业病防治方案
2014/05/29 职场文书
2014年社区党建工作汇报材料
2014/11/02 职场文书
给老师的感谢信
2015/01/20 职场文书
电台广播稿范文
2015/08/19 职场文书