如何实现一个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 相关文章推荐
ExtJS 2.0实用简明教程 之获得ExtJS
Apr 29 Javascript
JavaScript 基础知识 被自己遗忘的
Oct 15 Javascript
javascript Keycode对照表
Oct 24 Javascript
chrome原生方法之数组
Nov 30 Javascript
jQuery 下拉列表 二级联动插件分享
Mar 29 Javascript
jquery1.10给新增元素绑定事件的方法
Mar 06 Javascript
javascript冒泡排序小结
Apr 10 Javascript
jQuery Checkbox 全选 反选的简单实例
Nov 29 Javascript
js实现贪吃蛇小游戏(容易理解)
Jan 22 Javascript
AngularJS学习笔记之表单验证功能实例详解
Jul 06 Javascript
快速解决vue-cli不能初始化webpack模板的问题
Mar 20 Javascript
ES6 Promise对象的含义和基本用法分析
Jun 14 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程序中的常见漏洞进行攻击(下)
2006/10/09 PHP
在VS2008中编译MYSQL5.1.48的方法
2010/07/03 PHP
PHP根据IP判断地区名信息的示例代码
2014/03/03 PHP
php正则匹配html中带class的div并选取其中内容的方法
2015/01/13 PHP
ExtJS Store的数据访问与更新问题
2010/04/28 Javascript
jquery时间下拉框小例子
2013/04/15 Javascript
js实现图片轮换效果代码
2013/04/16 Javascript
jQuery 获取URL的GET参数值的小例子
2013/04/18 Javascript
jQuery中:hidden选择器用法实例
2014/12/30 Javascript
浅谈类似于(function(){}).call()的js语句
2015/03/30 Javascript
jQuery中ajax的load()与post()方法实例详解
2016/01/05 Javascript
jquery ajax双击div可直接修改div中的内容
2016/03/04 Javascript
JS实现保留n位小数的四舍五入问题示例
2016/08/03 Javascript
微信小程序 欢迎界面开发的实例详解
2016/11/30 Javascript
jquery实现多次上传同一张图片
2017/01/09 Javascript
jquery replace方法去空格
2017/05/08 jQuery
Node.js五大应用性能技巧小结(必须收藏)
2017/08/09 Javascript
vue 实现剪裁图片并上传服务器功能
2018/03/01 Javascript
详解angular2.x创建项目入门指令
2018/10/11 Javascript
解决layer弹出层的内容页点击按钮跳转到新的页面问题
2019/09/14 Javascript
js单线程的本质 Event Loop解析
2019/10/29 Javascript
利用Python的Twisted框架实现webshell密码扫描器的教程
2015/04/16 Python
Python 40行代码实现人脸识别功能
2017/04/02 Python
css3设置box-pack和box-align让div里面的元素垂直居中
2014/09/01 HTML / CSS
CSS3中各种颜色属性的使用教程
2016/05/17 HTML / CSS
世界上最好的精品店:Shoptiques
2018/02/05 全球购物
英国游戏机和游戏购物网站:365games.co.uk
2018/06/18 全球购物
一套Delphi的笔试题二
2013/05/11 面试题
材料加工硕士生求职信
2013/10/10 职场文书
大学生家政服务项目创业计划书
2014/01/30 职场文书
仓库文员岗位职责
2014/04/06 职场文书
初中班主任教育随笔
2015/08/15 职场文书
python tqdm用法及实例详解
2021/06/16 Python
Vue项目打包、合并及压缩优化网页响应速度
2021/07/07 Vue.js
苹果的回收机器人可以通过拆解iPhone获取大量的金和铜并外公布了环境保护最新进展
2022/04/21 数码科技
Java结构型设计模式之组合模式详解
2022/09/23 Java/Android