如何实现一个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 相关文章推荐
解读IE和firefox下JScript和HREF的执行顺序
Jan 12 Javascript
用showModalDialog弹出页面后,提交表单总是弹出一个新窗口
Jul 18 Javascript
js prototype截取字符串函数
Apr 01 Javascript
jquery实现多级下拉菜单的实例代码
Oct 02 Javascript
javascript通过navigator.userAgent识别各种浏览器
Oct 25 Javascript
JavaScript实现自动弹出窗口并自动关闭窗口的方法
Aug 06 Javascript
Bootstrap导航条可点击和鼠标悬停显示下拉菜单的实现代码
Jun 23 Javascript
基于angular实现三级联动的生日插件
May 12 Javascript
Node.js v8.0.0正式发布!看看带来了哪些主要新特性
Jun 02 Javascript
jQuery ajax动态生成table功能示例
Jun 14 jQuery
详解JS获取HTML DOM元素的8种方法
Jun 17 Javascript
JS实现移动端可折叠导航菜单(现代都市风)
Jul 07 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)
2010/09/04 PHP
PHP mcrypt可逆加密算法分析
2011/07/19 PHP
解析二进制流接口应用实例 pack、unpack、ord 函数使用方法
2013/06/18 PHP
ThinkPHP多表联合查询的常用方法
2020/03/24 PHP
利用Homestead快速运行一个Laravel项目的方法详解
2017/11/14 PHP
jquery ajax执行后台方法
2010/03/18 Javascript
EasySlider 基于jQuery功能强大简单易用的滑动门插件
2010/06/11 Javascript
三级下拉菜单的js实现代码
2011/05/23 Javascript
JavaScript基础知识之数据类型
2012/08/06 Javascript
利用fecha进行JS日期处理
2016/11/21 Javascript
javascript判断firebug是否开启的方法
2016/11/23 Javascript
JS实现鼠标移上去显示图片或微信二维码
2016/12/14 Javascript
vue中七牛插件使用的实例代码
2017/07/28 Javascript
新版小程序登录授权的方法
2018/12/12 Javascript
JS无限级导航菜单实现方法
2019/01/05 Javascript
react-native滑动吸顶效果的实现过程
2019/06/03 Javascript
Python判断直线和矩形是否相交的方法
2015/07/14 Python
python 调用c语言函数的方法
2017/09/29 Python
Python基于socket实现简单的即时通讯功能示例
2018/01/16 Python
Python中XlsxWriter模块简介与用法分析
2018/04/24 Python
解决tensorflow模型参数保存和加载的问题
2018/07/26 Python
Python PyAutoGUI模块控制鼠标和键盘实现自动化任务详解
2018/09/04 Python
详解重置Django migration的常见方式
2019/02/15 Python
元组列表字典(莫烦python基础)
2019/04/03 Python
python多线程http压力测试脚本
2019/06/25 Python
python lambda函数及三个常用的高阶函数
2020/02/05 Python
python 动态绘制爱心的示例
2020/09/27 Python
CSS3过渡transition效果实例介绍
2016/05/03 HTML / CSS
美国豪华时尚女性精品店:Kirna Zabête
2018/01/11 全球购物
客服端调用EJB对象的几个基本步骤
2012/01/15 面试题
2014的自我评价
2014/01/13 职场文书
计算机网络工程专业职业生涯规划书
2014/03/10 职场文书
2014年党支部学习材料
2014/05/19 职场文书
财会专业大学生求职信
2014/09/26 职场文书
财务总监岗位职责
2015/02/03 职场文书
关于童年的读书笔记
2015/06/26 职场文书