如何实现一个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 OffsetParent属性深入解析
Jan 13 Javascript
JavaScript中诡异的delete操作符
Mar 12 Javascript
js制作网站首页图片轮播特效代码
Aug 30 Javascript
vue使用watch 观察路由变化,重新获取内容
Mar 08 Javascript
移动端使用localResizeIMG4压缩图片
Apr 22 Javascript
vue 路由嵌套高亮问题的解决方法
May 17 Javascript
vue+iview动态渲染表格详解
Mar 19 Javascript
react-native聊天室|RN版聊天App仿微信实例|RN仿微信界面
Nov 12 Javascript
解决vue v-for src 图片路径问题 404
Nov 12 Javascript
JavaScript实现放大镜效果代码示例
Apr 29 Javascript
javascript中导出与导入实现模块化管理教程
Dec 03 Javascript
JavaScript对象访问器Getter及Setter原理解析
Dec 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程序员不应该忽略的3点
2015/10/09 PHP
浅谈PHP检查数组中是否存在某个值 in_array 函数
2016/06/13 PHP
利用PHP_XLSXWriter代替PHPExcel的方法示例
2017/07/16 PHP
PHP基于openssl实现的非对称加密操作示例
2019/01/11 PHP
解析John Resig Simple JavaScript Inheritance代码
2012/12/03 Javascript
js写的方法实现上传图片之后查看大图
2014/03/05 Javascript
JavaScript中扩展Array contains方法实例
2020/08/23 Javascript
JS简单编号生成器实现方法(附demo源码下载)
2016/04/05 Javascript
Jquery组件easyUi实现手风琴(折叠面板)示例
2016/08/23 Javascript
Angular JS 生成动态二维码的方法
2017/02/23 Javascript
JS实现二叉查找树的建立以及一些遍历方法实现
2017/04/17 Javascript
vue.js语法及常用指令
2017/10/29 Javascript
vue 组件 全局注册和局部注册的实现
2018/02/28 Javascript
socket在egg中的使用实例代码详解
2019/05/30 Javascript
Python深入学习之装饰器
2014/08/31 Python
跟老齐学Python之print详解
2014/09/28 Python
Python之PyUnit单元测试实例
2014/10/11 Python
Python封装shell命令实例分析
2015/05/05 Python
Django admin实现图书管理系统菜鸟级教程完整实例
2017/12/12 Python
Python数据结构与算法之图的基本实现及迭代器实例详解
2017/12/12 Python
Python数据可视化之画图
2019/01/15 Python
pyQT5 实现窗体之间传值的示例
2019/06/20 Python
Python使用qrcode二维码库生成二维码方法详解
2020/02/17 Python
Python3合并两个有序数组代码实例
2020/08/11 Python
Python 爬虫批量爬取网页图片保存到本地的实现代码
2020/12/24 Python
ALLSAINTS英国官网:伦敦新锐潮流品牌
2016/09/19 全球购物
古驰英国官网:GUCCI英国
2020/03/07 全球购物
中专自我鉴定范文
2013/10/16 职场文书
技校生自我鉴定
2013/12/08 职场文书
团日活动总结书
2014/05/08 职场文书
高中学生自我评价范文
2014/09/23 职场文书
建筑技术负责人岗位职责
2015/04/13 职场文书
小学生暑假安全保证书
2015/07/13 职场文书
总经理年会致辞
2015/07/29 职场文书
python实战之一步一步教你绘制小猪佩奇
2021/04/22 Python
SQL注入篇学习之盲注/宽字节注入
2022/03/03 MySQL