如何实现一个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 相关文章推荐
javascript实现浏览器窗口传递参数的方法
Sep 03 Javascript
AngularJS ng-repeat数组有重复值的解决方法
Oct 23 Javascript
浅谈Node.js:理解stream
Dec 08 Javascript
Angularjs为ng-click事件传递参数
Jun 15 Javascript
使用watch监听路由变化和watch监听对象的实例
Feb 24 Javascript
判断iOS、Android以及PC端的示例代码
Nov 15 Javascript
基于javascript的拖拽类封装详解
Apr 19 Javascript
微信小程序自定义弹窗滚动与页面滚动冲突的解决方法
Jul 16 Javascript
javascript实现视频弹幕效果(两个版本)
Nov 28 Javascript
JS实现电商商品展示放大镜特效
Jan 07 Javascript
javascript中的with语句学习笔记及用法
Feb 17 Javascript
vue实力踩坑之push当前页无效
Apr 10 Vue.js
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
destoon实现底部添加你是第几位访问者的方法
2014/07/15 PHP
PHP随机生成信用卡卡号的方法
2015/03/23 PHP
PHP+Mysql+jQuery文件下载次数统计实例讲解
2015/10/10 PHP
php输出文字乱码的解决方法
2019/10/04 PHP
Laravel 对某一列进行筛选然后求和sum()的例子
2019/10/10 PHP
jQuery实现带延迟效果的滑动菜单代码
2015/09/02 Javascript
基于vue2实现左滑删除功能
2017/11/28 Javascript
通过一次报错详细谈谈Point事件
2018/05/17 Javascript
微信小程序实现时间预约功能
2018/11/27 Javascript
vue路由守卫,限制前端页面访问权限的例子
2019/11/11 Javascript
Python入门教程之if语句的用法
2015/05/14 Python
Python内建模块struct实例详解
2018/02/02 Python
python表格存取的方法
2018/03/07 Python
python 读文件,然后转化为矩阵的实例
2018/04/23 Python
Python学习笔记之open()函数打开文件路径报错问题
2018/04/28 Python
Python3实现统计单词表中每个字母出现频率的方法示例
2019/01/28 Python
Python实现从SQL型数据库读写dataframe型数据的方法【基于pandas】
2019/03/18 Python
Python for循环搭配else常见问题解决
2020/02/11 Python
CSS3 中filter(滤镜)属性使用详解
2020/04/07 HTML / CSS
canvas压缩图片以及卡片制作的方法示例
2018/12/04 HTML / CSS
韩国著名的在线综合购物网站:Akmall
2016/08/07 全球购物
日本卡普空电视游戏软件公司官方购物网站:e-CAPCOM
2018/07/17 全球购物
远程学习的教学用品和家庭学习资源:Really Good Stuff
2020/04/27 全球购物
新员工培训个人的自我评价
2013/10/09 职场文书
师范教师大学生职业生涯规划范文
2014/01/05 职场文书
社区党员先进事迹
2014/01/22 职场文书
公司门卫管理制度
2014/02/01 职场文书
消防应急演练方案
2014/02/12 职场文书
学生会招新策划书
2014/02/14 职场文书
金融管理毕业生求职信
2014/03/03 职场文书
党员公开承诺书和承诺事项
2014/03/25 职场文书
乡领导班子四风问题对照检查材料
2014/09/25 职场文书
演讲开场白和结束语
2015/05/29 职场文书
2016年习总书记讲话学习心得体会
2016/01/20 职场文书
Pytest中skip和skipif的具体使用方法
2021/06/30 Python
openEuler 搭建java开发环境的详细过程
2022/06/10 Servers