如何实现一个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 相关文章推荐
对字符串进行HTML编码和解码的JavaScript函数
Feb 01 Javascript
浅析JavaScript中的CSS属性及命名规范
Nov 28 Javascript
上传文件返回的json数据会被提示下载问题解决方案
Dec 03 Javascript
node.js [superAgent] 请求使用示例
Mar 13 Javascript
javaScript中Math()函数注意事项
Jun 18 Javascript
jQuery实现可编辑的表格实例讲解(2)
Sep 17 Javascript
jQuery遮罩层实现方法实例详解(附遮罩层插件)
Dec 08 Javascript
jquery mobile实现可折叠的导航按钮
Mar 11 Javascript
关于Vue单页面骨架屏实践记录
Dec 13 Javascript
angular-tree-component的使用详解
Jul 30 Javascript
JavaScript前端开发时数值运算的小技巧
Jul 28 Javascript
在vue中使用image-webpack-loader实例
Nov 12 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
安装APACHE
2007/01/15 PHP
解析php时间戳与日期的转换
2013/06/06 PHP
使用PHP求两个文件的相对路径
2013/06/20 PHP
stripos函数知识点实例分享
2019/02/11 PHP
TNC vs RR BO3 第一场 2.14
2021/03/10 DOTA
validator验证控件使用代码
2010/11/23 Javascript
javascript之querySelector和querySelectorAll使用介绍
2011/12/20 Javascript
用jquery实现输入框获取焦点消失文字
2013/04/27 Javascript
完美解决IE低版本不支持call与apply的问题
2013/12/05 Javascript
优化javascript的执行效率一些方法总结
2013/12/25 Javascript
原生javascript获取元素样式
2014/12/31 Javascript
原生js结合html5制作小飞龙的简易跳球
2015/03/30 Javascript
jQuery实现的淡入淡出二级菜单效果代码
2015/09/15 Javascript
原生javascript实现匀速运动动画效果
2016/02/26 Javascript
学习使用bootstrap的modal和carousel
2016/12/09 Javascript
JavaScript控制输入框中只能输入中文、数字和英文的方法【基于正则实现】
2017/03/03 Javascript
jQuery中table数据的值拷贝和拆分
2017/03/19 Javascript
微信小程序中遇到的iOS兼容性问题小结
2018/11/14 Javascript
新手简单了解vue
2019/05/29 Javascript
JS实现简单打字测试
2020/06/24 Javascript
在Mac OS上搭建Python的开发环境
2015/12/24 Python
unittest+coverage单元测试代码覆盖操作实例详解
2018/04/04 Python
详谈python中冒号与逗号的区别
2018/04/18 Python
Python实现的查询mysql数据库并通过邮件发送信息功能
2018/05/17 Python
Python for循环生成列表的实例
2018/06/15 Python
django 数据库 get_or_create函数返回值是tuple的问题
2020/05/15 Python
纯css3实现宠物小鸡实例代码
2018/10/08 HTML / CSS
伦敦哈德森鞋:Hudson Shoes
2018/02/06 全球购物
Etam艾格英国官网:法国著名女装品牌
2019/04/15 全球购物
Linux如何命名文件--使用文件名时应注意
2012/01/22 面试题
自我鉴定的范文
2013/10/03 职场文书
高二地理教学反思
2014/01/24 职场文书
喷漆工的岗位职责
2014/03/17 职场文书
简单租房协议书
2014/10/21 职场文书
九年级化学教学反思
2016/02/22 职场文书
浅谈css清除浮动(clearfix和clear)的用法
2023/05/21 HTML / CSS