如何实现一个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的树控件实现代码(asp.net+json)
Jul 11 Javascript
JS面向对象编程 for Cookie
Sep 19 Javascript
js multiple全选与取消全选实现代码
Dec 04 Javascript
JavaScript中的alert()函数使用技巧详解
Dec 29 Javascript
JavaScript解八皇后问题的方法总结
Jun 12 Javascript
基于rem的移动端响应式适配方案(详解)
Jul 07 Javascript
详解vue+css3做交互特效的方法
Nov 20 Javascript
webpack4 处理SCSS的方法示例
Sep 03 Javascript
jQuery选择器选中最后一个元素,倒数第二个元素操作示例
Dec 10 jQuery
echarts多条折线图动态分层的实现方法
May 24 Javascript
解决layui表格的表头不滚动的问题
Sep 04 Javascript
JavaScript canvas实现雪花随机动态飘落
Feb 08 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 Memcached应用实现代码
2010/02/08 PHP
深入Apache与Nginx的优缺点比较详解
2013/06/17 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(四)
2014/06/23 PHP
Discuz7.2版的faq.php SQL注入漏洞分析
2014/08/06 PHP
使用php转义输出HTML到JavaScript
2015/03/27 PHP
PHP基于双向链表与排序操作实现的会员排名功能示例
2017/12/26 PHP
PHP+Oracle本地开发环境搭建方法详解
2019/04/01 PHP
javascript 贪吃蛇实现代码
2008/11/22 Javascript
JavaScript Cookie显示用户上次访问的时间和次数
2009/12/08 Javascript
jquery.post用法示例代码
2014/01/03 Javascript
js显示当前日期时间和星期几
2015/10/22 Javascript
JavaScript中的时间处理小结
2016/02/24 Javascript
Javascript将双字节字符转换成单字节字符并计算长度
2016/06/22 Javascript
Node.js Sequelize如何实现数据库的读写分离
2016/10/23 Javascript
15个非常实用的JavaScript代码片段
2016/12/18 Javascript
Vue源码学习之初始化模块init.js解析
2017/11/02 Javascript
详解各版本React路由的跳转的方法
2018/05/10 Javascript
详解如何创建并发布一个 vue 组件
2018/11/08 Javascript
JS实现的A*寻路算法详解
2018/12/14 Javascript
微信小程序动态添加和删除组件的现实
2020/02/28 Javascript
[04:26]2014DOTA2国际邀请赛-Newbee顺利进入胜者组决赛 独家专访战神7
2014/07/19 DOTA
Python实现根据IP地址和子网掩码算出网段的方法
2015/07/30 Python
react+django清除浏览器缓存的几种方法小结
2019/07/17 Python
django 数据库连接模块解析及简单长连接改造方法
2019/08/29 Python
Django对接支付宝实现支付宝充值金币功能示例
2019/12/17 Python
python 项目目录结构设置
2020/02/14 Python
基于python实现上传文件到OSS代码实例
2020/05/09 Python
基于K.image_data_format() == 'channels_first' 的理解
2020/06/29 Python
python 实现IP子网计算
2021/02/18 Python
世界各地的旅游、观光和活动:Isango!
2019/10/29 全球购物
Juice Beauty官网:有机美容产品,护肤与化妆品
2020/06/13 全球购物
婚宴父母致辞
2015/07/27 职场文书
九年级历史教学反思
2016/02/19 职场文书
MongoDB balancer的使用详解
2021/04/30 MongoDB
python实现剪贴板的操作
2021/07/01 Python
Python安装使用Scrapy框架
2022/04/12 Python