如何实现一个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 相关文章推荐
读jQuery之七 判断点击了鼠标哪个键的代码
Jun 21 Javascript
javascript笔记 String类replace函数的一些事
Sep 22 Javascript
热点新闻滚动特效的js代码
Aug 17 Javascript
Javascript检查图片大小不要让大图片撑破页面
Nov 04 Javascript
IE中鼠标经过option触发mouseout的解决方法
Jan 29 Javascript
基于Jquery实现万圣节快乐特效
Nov 01 Javascript
Bootstrap企业网站实战项目4
Oct 14 Javascript
layer弹出层中H5播放器全屏出错的解决方法
Feb 21 Javascript
vue数据操作之点击事件实现num加减功能示例
Jan 19 Javascript
解决vue动态下拉菜单 有数据未反应的问题
Aug 06 Javascript
JS removeAttribute()方法实现删除元素的某个属性
Jan 11 Javascript
Vue3中toRef与toRefs的区别
Mar 24 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
apache+mysql+php+ssl服务器之完全安装攻略
2006/09/05 PHP
UCenter Home二次开发指南
2009/05/28 PHP
PHP 服务器配置(使用Apache及IIS两种方法)
2009/06/01 PHP
PHP5函数小全(分享)
2013/06/06 PHP
一个PHP实现的轻量级简单爬虫
2015/07/08 PHP
PHP实现无限分类的实现方法
2016/11/14 PHP
谈谈从phpinfo中能获取哪些值得注意的信息
2017/03/28 PHP
Laravel框架中Blade模板的用法示例
2017/08/30 PHP
laravel如何开启跨域功能示例详解
2017/08/31 PHP
一个不错的应用,用于提交获取文章内容,不推荐用
2007/03/03 Javascript
基于jquery的页面划词搜索JS
2010/09/14 Javascript
jquery操作select option 的代码小结
2011/06/21 Javascript
JS页面延迟执行一些方法(整理)
2013/11/11 Javascript
超链接的禁用属性Disabled使用示例
2014/07/31 Javascript
jQuery实现仿淘宝带有指示条的图片转动切换效果完整实例
2015/03/04 Javascript
Node.js程序中的本地文件操作用法小结
2016/03/06 Javascript
基于JavaScript实现 网页切出 网站title变化代码
2016/04/03 Javascript
js手机号4位显示空格,银行卡每4位显示空格效果
2017/03/23 Javascript
详解angular 中的自定义指令之详解API
2017/06/20 Javascript
vue2 router 动态传参,多个参数的实例
2017/11/10 Javascript
Nodejs让异步变成同步的方法
2019/03/02 NodeJs
在vue中使用setInterval的方法示例
2019/04/16 Javascript
vue发送websocket请求和http post请求的实例代码
2019/07/11 Javascript
python获取mp3文件信息的方法
2015/06/15 Python
Python登录系统界面实现详解
2019/06/25 Python
pandas读取CSV文件时查看修改各列的数据类型格式
2019/07/07 Python
Python中拆分字符串的操作方法
2019/07/23 Python
Django values()和value_list()的使用
2020/03/31 Python
Python实现爬取并分析电商评论
2020/06/19 Python
全球最大的在线旅游公司:Expedia
2017/11/16 全球购物
投标邀请书范文
2014/01/31 职场文书
六年级学生评语
2014/04/22 职场文书
大学生英语演讲稿
2014/04/24 职场文书
2015年体育教师个人工作总结
2015/05/12 职场文书
唐山大地震的观后感
2015/06/05 职场文书
python plt.plot bar 如何设置绘图尺寸大小
2021/06/01 Python