如何实现一个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 div 遮罩层封锁整个页面
Jul 10 Javascript
jQuery validate 中文API 附validate.js中文api手册
Jul 31 Javascript
jqGrid增加时--判断开始日期与结束日期(实例解析)
Nov 08 Javascript
jquery与prototype框架的详细对比
Nov 21 Javascript
Jquery api 速查表分享
Jan 12 Javascript
jQuery中unwrap()方法用法实例
Jan 16 Javascript
jQuery表格插件datatables用法详解
Nov 23 Javascript
实例详解display:none与visible:hidden的区别
Mar 30 Javascript
Vuejs监听vuex中值的变化的方法示例
Dec 02 Javascript
利用d3.js力导布局绘制资源拓扑图实例教程
Jan 08 Javascript
详解jquery和vue对比
Apr 16 jQuery
ES2020让代码更优美的运算符 (?.) (??)
Jan 04 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
thinkPHP商城公告功能开发问题分析
2016/12/01 PHP
PHP实现验证码校验功能
2017/11/16 PHP
Laravel框架模型的创建及模型对数据操作示例
2019/05/07 PHP
js 页面输出值
2008/11/30 Javascript
JS input文本框禁用右键和复制粘贴功能的代码
2010/04/15 Javascript
学习面向对象之面向对象的基本概念:对象和其他基本要素
2010/11/30 Javascript
自己动手开发jQuery插件教程
2011/08/25 Javascript
js调用AJAX时Get和post的乱码解决方法
2013/06/04 Javascript
js操作label给label赋值及取label的值示例
2013/11/07 Javascript
JS实现模仿微博发布效果实例代码
2013/12/16 Javascript
深入理解jQuery中live与bind方法的区别
2013/12/18 Javascript
Jquery 监视按键,按下回车键触发某方法的实现代码
2014/05/11 Javascript
判断浏览器的内核及版本号方法汇总
2015/01/05 Javascript
JavaScript插件化开发教程(五)
2015/02/01 Javascript
jquery插件orbit.js实现图片折叠轮换特效
2015/04/14 Javascript
全面详细的jQuery常见开发技巧手册
2016/02/21 Javascript
JavaScript简单实现鼠标移动切换图片的方法
2016/02/23 Javascript
原生JS实现网络彩票投注效果
2016/09/25 Javascript
AngularJS解决ng-if中的ng-model值无效的问题
2017/06/21 Javascript
详解Vue2.0 事件派发与接收
2017/09/05 Javascript
详解ES7 Decorator 入门解析
2019/02/18 Javascript
JS如何在数组指定位置插入元素
2020/03/10 Javascript
python比较两个列表大小的方法
2015/07/11 Python
Python装饰器实现几类验证功能做法实例
2017/05/18 Python
Python异常的检测和处理方法
2018/10/26 Python
Django调用百度AI接口实现人脸注册登录代码实例
2020/04/23 Python
Html5调用手机摄像头并实现人脸识别的实现
2018/12/21 HTML / CSS
La Redoute英国官网:法国时尚品牌
2017/04/27 全球购物
vue实现倒计时功能
2021/03/24 Vue.js
小学教师培训感言
2014/02/11 职场文书
大龄毕业生求职别忘职业规划
2014/03/11 职场文书
药剂专业自荐书
2014/06/20 职场文书
就业意向书
2014/07/29 职场文书
公司周年庆典标语
2014/10/07 职场文书
保外就医申请书范文
2015/08/06 职场文书
SQL解决未能删除约束问题drop constraint
2022/05/30 SQL Server