如何实现一个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的Tabs内容轮换效果实现代码,几行搞定
Feb 12 Javascript
javascript解三阶幻方(九宫格)
Apr 22 Javascript
JS中获取函数调用链所有参数的方法
May 07 Javascript
页面get请求 中文参数方法乱码问题的快速解决方法
May 31 Javascript
Javascript小技能总结(推荐)
Jun 02 Javascript
Angular.js实现多个checkbox只能选择一个的方法示例
Feb 24 Javascript
ES6新特性三: Generator(生成器)函数详解
Apr 21 Javascript
JS自动生成动态HTML验证码页面
Jun 14 Javascript
关于在vue-cli中使用微信自动登录和分享的实例
Jun 22 Javascript
Node.js+jade+mongodb+mongoose实现爬虫分离入库与生成静态文件的方法
Sep 20 Javascript
详解layui中的树形关于取值传值问题
Jan 16 Javascript
浅谈Vue 自动化部署打包上线
Jun 14 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获取一个变量的名字的方法
2014/09/05 PHP
PHP过滤黑名单关键字的方法
2014/12/01 PHP
编写PHP脚本过滤用户上传的图片
2015/07/03 PHP
微信支付开发交易通知实例
2016/07/12 PHP
ajax+php实现无刷新验证手机号的实例
2017/12/22 PHP
js获取和设置属性的方法
2014/02/20 Javascript
JQuery EasyUI 数字格式化处理示例
2014/05/05 Javascript
js处理表格对table进行修饰
2014/05/26 Javascript
使用原生JS实现弹出层特效
2014/12/22 Javascript
jQuery中:last-child选择器用法实例
2014/12/31 Javascript
js动态生成form 并用ajax方式提交的实现方法
2016/09/09 Javascript
通过jquery实现页面的动画效果(实例代码)
2016/09/18 Javascript
微信小程序 SocketIO 实例讲解
2016/10/13 Javascript
JS实现侧边栏鼠标经过弹出框+缓冲效果
2017/03/29 Javascript
Node.js对MongoDB数据库实现模糊查询的方法
2017/05/03 Javascript
基于JavaScript 性能优化技巧心得(分享)
2017/12/11 Javascript
微信小程序 setData 对 data数据影响问题
2019/04/18 Javascript
小程序云开发获取不到数据库记录的解决方法
2019/05/18 Javascript
[01:33:59]真人秀《加油 DOTA》 第六期
2014/09/09 DOTA
Python中利用函数装饰器实现备忘功能
2015/03/30 Python
Python中每次处理一个字符的5种方法
2015/05/21 Python
python实现关键词提取的示例讲解
2018/04/28 Python
对Python Pexpect 模块的使用说明详解
2019/02/14 Python
利用python读取YUV文件 转RGB 8bit/10bit通用
2019/12/09 Python
Window系统下Python如何安装OpenCV库
2020/03/05 Python
python代码实现图书管理系统
2020/11/30 Python
pycharm + django跨域无提示的解决方法
2020/12/06 Python
ProBikeKit新西兰:自行车套件,跑步和铁人三项装备
2017/04/05 全球购物
微软台湾官方网站:Microsoft台湾
2018/08/15 全球购物
毕业生如何写自我鉴定
2014/03/15 职场文书
门前三包责任书
2014/04/15 职场文书
产品销售计划书
2014/05/04 职场文书
2014年小学德育工作总结
2014/12/05 职场文书
党员转正介绍人意见
2015/06/03 职场文书
谁动了我的奶酪读书笔记
2015/06/30 职场文书
二胎满月酒致辞
2015/07/29 职场文书