如何实现一个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重写Cognos右键菜单的实现代码
Apr 11 Javascript
IE下window.onresize 多次调用与死循环bug处理方法介绍
Nov 12 Javascript
jQuery移动web开发中的页面初始化与加载事件
Dec 03 Javascript
Angularjs 滚动加载更多数据
Mar 17 Javascript
BootStrap.css 在手机端滑动时右侧出现空白的原因及解决办法
Jun 07 Javascript
利用jQuery来动态为属性添加或者删除属性的简单方法
Dec 02 Javascript
JavaScript hasOwnProperty() 函数实例详解
Aug 04 Javascript
webpack常用配置总览(小结)
Nov 18 Javascript
使用vue实现一个电子签名组件的示例代码
Jan 06 Javascript
序列化模块json代码实例详解
Mar 03 Javascript
js函数和this用法实例分析
Mar 13 Javascript
代码块高亮可复制显示js插件highlight.js+clipboard.js整合
Feb 15 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 什么是PEAR?
2009/03/19 PHP
PHP获取MAC地址的具体实例
2013/12/13 PHP
php页面,mysql数据库转utf-8乱码,utf-8编码问题总结
2015/08/27 PHP
jquery+json实现的搜索加分页效果
2010/03/31 Javascript
关于UTF-8的客户端用AJAX方式获取GB2312的服务器端乱码问题的解决办法
2010/11/30 Javascript
利用jq让你的div居中的好方法分享
2013/11/21 Javascript
js数组的基本操作(很全自己整理的)
2014/10/16 Javascript
javascript中的previousSibling和nextSibling的正确用法
2015/09/16 Javascript
浅谈JQuery+ajax+jsonp 跨域访问
2016/06/25 Javascript
jquery 抽奖小程序实现代码
2016/10/12 Javascript
谈谈因Vue.js引发关于getter和setter的思考
2016/12/02 Javascript
纯javaScript、jQuery实现个性化图片轮播【推荐】
2017/01/08 Javascript
使用vs code开发Nodejs程序的使用方法
2017/09/21 NodeJs
vue中进入详情页记住滚动位置的方法(keep-alive)
2018/09/21 Javascript
python实现迭代法求方程组的根过程解析
2019/11/25 Javascript
jquery检测上传文件大小示例
2020/04/26 jQuery
jquery自定义组件实例详解
2020/12/31 jQuery
[05:48]DOTA2英雄梦之声vol21 屠夫
2014/06/20 DOTA
[54:47]Liquid vs VP Supermajor决赛 BO 第五场 6.10
2018/07/05 DOTA
全面了解django的缓存机制及使用方法
2019/07/22 Python
使用 python pyautogui实现鼠标键盘控制功能
2019/08/04 Python
Python3多线程版TCP端口扫描器
2019/08/31 Python
余弦相似性计算及python代码实现过程解析
2019/09/18 Python
django xadmin中form_layout添加字段显示方式
2020/03/30 Python
python随机模块random的22种函数(小结)
2020/05/15 Python
python 自定义异常和主动抛出异常(raise)的操作
2020/12/11 Python
CSS3网格的三个新特性详解
2014/04/04 HTML / CSS
经典c++面试题五
2014/12/17 面试题
中专毕业自我鉴定
2013/10/16 职场文书
装修致歉信
2014/01/15 职场文书
初中三好学生自我鉴定
2014/04/07 职场文书
机关会计岗位职责
2014/04/08 职场文书
师德先进个人事迹材料
2014/12/19 职场文书
《活见鬼》教学反思
2016/02/24 职场文书
话题作文之成长
2019/12/09 职场文书
Python代码实现双链表
2022/05/25 Python