如何实现一个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 相关文章推荐
比较简单的异步加载JS文件的代码
Jul 18 Javascript
javascript nextSibling 与 getNextElement(node) 使用介绍
Oct 13 Javascript
jquery图片延迟加载 前端开发技能必备系列
Jun 18 Javascript
javascript:void(0)点击登录没反应怎么解决
Nov 13 Javascript
jQuery插件cxSelect多级联动下拉菜单实例解析
Jun 24 Javascript
JS匿名函数类生成方式实例分析
Nov 26 Javascript
JS实现求数组起始项到终止项之和的方法【基于数组扩展函数】
Jun 13 Javascript
详解node.js中的npm和webpack配置方法
Jan 21 Javascript
微信小程序实现漂亮的弹窗效果
May 26 Javascript
Vue.js中provide/inject实现响应式数据更新的方法示例
Oct 16 Javascript
node创建Vue项目步骤详解
Mar 06 Javascript
Laravel 如何在blade文件中使用Vue组件的示例代码
Jun 28 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使用json_encode对变量json编码
2014/04/07 PHP
php程序总是提示验证码输入有误解决方案
2015/01/07 PHP
php数组添加与删除单元的常用函数实例分析
2015/02/16 PHP
php源码分析之DZX1.5字符串截断函数cutstr用法
2015/06/17 PHP
让插入到 innerHTML 中的 script 跑起来的实现代码
2006/07/01 Javascript
JAVASCRIPT下判断IE与FF的比较简单的方式
2008/10/17 Javascript
input 高级限制级用法
2009/03/26 Javascript
js 上传图片预览问题
2010/12/06 Javascript
JavaScript 高级篇之函数 (四)
2012/04/07 Javascript
框架页面高度自动刷新的Javascript脚本
2013/11/01 Javascript
JS将所有对象s的属性复制给对象r(原生js+jquery)
2014/01/25 Javascript
JavaScript学习笔记之数组求和方法
2016/03/23 Javascript
关于数据与后端进行交流匹配(点亮星星)
2016/08/03 Javascript
vue登录路由验证的实现
2017/12/13 Javascript
react-router browserHistory刷新页面404问题解决方法
2017/12/29 Javascript
基于vue-element组件实现音乐播放器功能
2018/05/06 Javascript
JS使用JSON.parse(),JSON.stringify()实现对对象的深拷贝功能分析
2019/03/06 Javascript
用jQuery实现抽奖程序
2020/04/12 jQuery
浅谈python和C语言混编的几种方式(推荐)
2017/09/27 Python
Pytorch入门之mnist分类实例
2018/04/14 Python
python分数表示方式和写法
2019/06/26 Python
Python 实现将数组/矩阵转换成Image类
2020/01/09 Python
Python 如何展开嵌套的序列
2020/08/01 Python
cookies应对python反爬虫知识点详解
2020/11/25 Python
CSS3中几个新增加的盒模型属性使用教程
2016/03/01 HTML / CSS
飞利浦美国官网:Philips美国
2020/02/28 全球购物
Ego Shoes官网:英国时髦鞋类品牌
2020/10/19 全球购物
高校生生产实习自我鉴定
2013/09/21 职场文书
数控专业毕业生求职信范文
2013/09/21 职场文书
市场营销毕业生自荐信
2013/11/23 职场文书
《匆匆》教学反思
2014/02/22 职场文书
晚会主持词开场白
2014/03/17 职场文书
美术指导求职信
2014/03/17 职场文书
我的中国梦演讲稿500字
2014/08/19 职场文书
MyBatis-Plus 批量插入数据的操作方法
2021/09/25 Java/Android
SQL CASE 表达式的具体使用
2022/03/21 SQL Server