如何实现一个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 API学Jquery 之三 筛选
Apr 09 Javascript
JavaScript高级程序设计 读书笔记之九 本地对象Array
Feb 27 Javascript
jquery搜索框效果实现方法
Jan 16 Javascript
简述Matlab中size()函数的用法
Mar 20 Javascript
手机端js和html5刮刮卡效果
Sep 29 Javascript
JavaScript调试的多个必备小Tips
Jan 15 Javascript
JS实现图片预览的两种方式
Jun 27 Javascript
浅谈react受控组件与非受控组件(小结)
Feb 09 Javascript
vue 点击按钮增加一行的方法
Sep 07 Javascript
vue项目设置scrollTop不起作用(总结)
Dec 21 Javascript
详解vue 不同环境配置不同的打包命令
Apr 07 Javascript
vue双击事件2.0事件监听(点击-双击-鼠标事件)和事件修饰符操作
Jul 27 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
学习discuz php 引入文件的方法DISCUZ_ROOT
2009/06/21 PHP
PHP开发中常用的三个表单验证函数使用小结
2010/03/03 PHP
基于PHP CURL用法的深入分析
2013/06/09 PHP
类似GMAIL的Ajax信息反馈显示
2010/02/16 Javascript
JavaScript类和继承 prototype属性
2010/09/03 Javascript
Javascript/Jquery——简单定时器的多种实现方法
2013/07/03 Javascript
jquery实现加载等待效果示例
2013/09/25 Javascript
jquery中的on方法使用介绍
2013/12/29 Javascript
dedecms页面如何获取会员状态的实例代码
2016/03/15 Javascript
使用jQuery制作浮动工具栏的实例分享
2016/05/13 Javascript
使用AngularJS对表单提交内容进行验证的操作方法
2017/07/12 Javascript
vue基于Element构建自定义树的示例代码
2017/09/19 Javascript
vue检测对象和数组的变化分析
2018/06/30 Javascript
微信小程序使用canvas的画图操作示例
2019/01/18 Javascript
vue中根据时间戳判断对应的时间(今天 昨天 前天)
2019/12/20 Javascript
微信小程序实现锚点跳转
2020/11/23 Javascript
[01:50]2014DOTA2西雅图邀请赛 专访欢乐周宝龙
2014/07/08 DOTA
[01:42]辉夜杯战队访谈宣传片—FANTUAN
2015/12/25 DOTA
[50:02]完美世界DOTA2联赛PWL S2 Magma vs FTD 第三场 11.29
2020/12/03 DOTA
python实现基于两张图片生成圆角图标效果的方法
2015/03/26 Python
详解Python程序与服务器连接的WSGI接口
2015/04/29 Python
python实现清屏的方法
2015/04/30 Python
详解python发送各类邮件的主要方法
2016/12/22 Python
python之从文件读取数据到list的实例讲解
2018/04/19 Python
win10系统下Anaconda3安装配置方法图文教程
2018/09/19 Python
python3.8.3安装教程及环境配置的详细教程(64-bit)
2020/11/28 Python
Lookfantastic挪威官网:英国知名美妆购物网站
2017/07/26 全球购物
交通专业个人自荐信格式
2013/09/23 职场文书
俄语翻译实习生的自我评价分享
2013/11/06 职场文书
心理学专业毕业生推荐信范文
2013/11/21 职场文书
小学教师听课制度
2014/02/01 职场文书
大学校务公开实施方案
2014/03/31 职场文书
抽样调查项目计划书
2014/04/24 职场文书
党支部鉴定意见
2015/06/02 职场文书
Unity连接MySQL并读取表格数据的实现代码
2021/06/20 MySQL
详解Java分布式事务的 6 种解决方案
2021/06/26 Java/Android