如何实现一个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事件写法实现代码
Jan 07 Javascript
jquery创建并行对象或者合并对象的实现代码
Oct 10 Javascript
jQuery中事件对象e的事件冒泡用法示例介绍
Apr 25 Javascript
jQuery获取标签文本内容和html内容的方法
Mar 27 Javascript
前端js弹出框组件使用方法
Aug 24 Javascript
Vue-Router实现页面正在加载特效方法示例
Feb 12 Javascript
如何将 jQuery 从你的 Bootstrap 项目中移除(取而代之使用Vue.js)
Jul 17 jQuery
JS模拟超市简易收银台小程序代码解析
Aug 18 Javascript
vue做网页开场视频的实例代码
Oct 20 Javascript
微信小程序跨页面数据传递事件响应实现过程解析
Dec 19 Javascript
js实现幻灯片轮播图
Aug 14 Javascript
JavaScript中的几种继承方法示例
Dec 06 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 mysqli查询语句返回值类型实例分析
2016/06/29 PHP
PHP基于堆栈实现的高级计算器功能示例
2017/09/15 PHP
php web环境和命令行环境下查找php.ini的位置
2019/07/17 PHP
Javascript----文件操作
2007/01/18 Javascript
两个select之间option的互相添加操作(jquery实现)
2009/11/12 Javascript
JS 控件事件小结
2012/10/31 Javascript
jQuery中DOM树操作之复制元素的方法
2015/01/23 Javascript
javascript产生随机数方法汇总
2016/01/25 Javascript
理解javascript定时器中的单线程
2016/02/23 Javascript
bootstrap和jQuery.Gantt的css冲突 如何解决
2016/05/29 Javascript
html中鼠标滚轮事件onmousewheel的处理方法
2016/11/11 Javascript
详解微信小程序 页面跳转 传递参数
2016/12/08 Javascript
js常用DOM方法详解
2017/02/04 Javascript
JavaScript设计模式之工厂模式和抽象工厂模式定义与用法分析
2018/07/26 Javascript
[03:49]DOTA2 2015国际邀请赛中国区预选赛第二日现场百态
2015/05/27 DOTA
如何用python写一个简单的词法分析器
2018/12/18 Python
Python正则匹配判断手机号是否合法的方法
2020/12/09 Python
基于pytorch的保存和加载模型参数的方法
2019/08/17 Python
如何将anaconda安装配置的mmdetection环境离线拷贝到另一台电脑
2020/10/15 Python
python反爬虫方法的优缺点分析
2020/11/25 Python
详解python的变量缓存机制
2021/01/24 Python
纯css3制作的火影忍者写轮眼开眼至轮回眼及进化过程实例
2014/11/11 HTML / CSS
瑞典廉价机票预订网站:Seat24
2018/06/19 全球购物
Hanro官网:奢华男士和女士内衣、睡衣和家居服
2018/10/25 全球购物
浙大网新C/C++面试解惑
2015/05/27 面试题
Java文件和目录(IO)操作
2014/08/26 面试题
UNIX文件系统分类
2014/11/11 面试题
高中生校园生活自我评价
2013/09/19 职场文书
艺术专业大学生自我评价
2013/09/22 职场文书
成功的酒店创业计划书
2013/12/27 职场文书
酒店管理毕业生自我鉴定
2014/03/02 职场文书
函授毕业生自我鉴定范文
2014/03/25 职场文书
初中优秀班集体申报材料
2014/05/01 职场文书
连锁超市项目计划书
2014/09/15 职场文书
2019年度开业庆典祝福语大全!
2019/07/05 职场文书
pycharm安装深度学习pytorch的d2l包失败问题解决
2022/03/25 Python