如何实现一个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 相关文章推荐
Javascript Function对象扩展之延时执行函数
Jul 06 Javascript
jquery时间下拉框小例子
Apr 15 Javascript
JS实现的用来对比两个用指定分隔符分割的字符串是否相同
Sep 19 Javascript
jQuery判断指定id的对象是否存在的方法
May 22 Javascript
用jQuery获取table中行id和td值的实现代码
May 19 Javascript
js 判断一组日期是否是连续的简单实例
Jul 11 Javascript
js插件dropload上拉下滑加载数据实例解析
Jul 27 Javascript
javascript之with的使用(阿里云、淘宝使用代码分析)
Oct 11 Javascript
详解node.js平台下Express的session与cookie模块包的配置
Apr 26 Javascript
解决layui-open关闭自身窗口的问题
Sep 10 Javascript
node-red File读取好保存实例讲解
Sep 11 Javascript
vue中touch和click共存的解决方式
Jul 28 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中ob(Output Buffer 输出缓冲)函数使用方法
2007/07/21 PHP
PHP创建文件及写入数据(覆盖写入,追加写入)的方法详解
2019/02/15 PHP
Laravel 不同生产环境服务器的判断实践
2019/10/15 PHP
一个js拖拽的效果类和dom-drag.js浅析
2010/07/17 Javascript
如何确保JavaScript的执行顺序 之jQuery.html并非万能钥匙
2011/03/03 Javascript
ExtJS DOM元素操作经验分享
2013/08/28 Javascript
jQuery淡入淡出元素让其效果更为生动
2014/09/01 Javascript
js实现class样式的修改、添加及删除的方法
2015/01/20 Javascript
简单介绍JavaScript的变量和数据类型
2015/06/03 Javascript
Javascript iframe交互并兼容各种浏览器的解决方法
2016/07/12 Javascript
JavaScript 继承详解(六)
2016/10/11 Javascript
PHP获取当前页面完整URL的方法
2016/12/02 Javascript
Jquery Easyui自定义下拉框组件使用详解(21)
2020/12/31 Javascript
vue学习笔记之vue1.0和vue2.0的区别介绍
2017/05/17 Javascript
Javascript 一些需要注意的细节(必看篇)
2017/07/08 Javascript
用最简单的方法判断JavaScript中this的指向(推荐)
2017/09/04 Javascript
开发一个Parcel-vue脚手架工具(详细步骤)
2018/09/22 Javascript
关于AngularJS中几种Providers的区别总结
2020/05/17 Javascript
JavaScript 判断浏览器是否是IE
2021/02/19 Javascript
六个窍门助你提高Python运行效率
2015/06/09 Python
Python实现优先级队列结构的方法详解
2016/06/02 Python
python实现二叉查找树实例代码
2018/02/08 Python
python3实现逐字输出的方法
2019/01/23 Python
基于树莓派的语音对话机器人
2019/06/17 Python
django多种支付、并发订单处理实例代码
2019/12/13 Python
Python-jenkins模块之folder相关操作介绍
2020/05/12 Python
详解移动端HTML5音频与视频问题及解决方案
2018/08/22 HTML / CSS
美国专业级皮肤病和spa品质护肤品的高级零售网站:SkinCareRx
2017/02/06 全球购物
大一自我鉴定范文
2013/10/04 职场文书
工程部经理岗位职责
2013/12/08 职场文书
材料专业毕业生求职信
2014/02/26 职场文书
社区服务标语
2014/07/01 职场文书
施工安全保证书
2015/05/09 职场文书
盘点2020年适合农村地区创业的项目
2019/10/16 职场文书
看看如何用Python绘制小米新版天价logo
2021/04/20 Python
Nginx流量拷贝ngx_http_mirror_module模块使用方法详解
2022/04/07 Servers