如何实现一个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中匿名函数,函数直接量和闭包
May 08 Javascript
用javascript实现点击链接弹出"图片另存为"而不是直接打开
Aug 15 Javascript
前后台交互过程中json格式如何解析以及如何生成
Dec 26 Javascript
使用script的src实现跨域和类似ajax效果
Nov 10 Javascript
jquery实现顶部向右伸缩的导航区域代码
Sep 02 Javascript
Bootstrap入门书籍之(四)菜单、按钮及导航
Feb 17 Javascript
js实现3D图片环展示效果
Mar 09 Javascript
使用webpack-dev-server处理跨域请求的方法
Apr 18 Javascript
详解vuex中action何时完成以及如何正确调用dispatch的思考
Jan 21 Javascript
vue实现随机验证码功能的实例代码
Apr 30 Javascript
vue+element使用动态加载路由方式实现三级菜单页面显示的操作
Aug 04 Javascript
javascript操作向表格中动态加载数据
Aug 27 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
phpmyadmin配置文件现在需要绝密的短密码(blowfish_secret)的2种解决方法
2014/05/07 PHP
PHP GD库生成图像的几个函数总结
2014/11/19 PHP
用php守护另一个php进程的例子
2015/02/13 PHP
php封装的数据库函数与用法示例【参考thinkPHP】
2016/11/08 PHP
PHP框架Laravel中使用UUID实现数据分表操作示例
2018/05/30 PHP
js玩一玩WSH吧
2007/02/23 Javascript
如何快速的呈现我们的网页的技巧整理
2007/07/01 Javascript
JavaScript 加号(+)运算符号
2009/12/06 Javascript
JavaScript基本编码模式小结
2012/05/23 Javascript
js 中的switch表达式使用示例
2020/06/03 Javascript
JavaScript 开发工具webstrom使用指南
2014/12/09 Javascript
window.onload绑定多个事件的两种解决方案
2016/05/15 Javascript
js小数计算小数点后显示多位小数的实现方法
2016/05/30 Javascript
bootstrap按钮插件(Button)使用方法解析
2017/01/13 Javascript
详解vue数据渲染出现闪烁问题
2017/06/29 Javascript
js学习心得_一个简单的动画库封装tween.js
2017/07/14 Javascript
基于Cookie常用操作以及属性介绍
2017/09/07 Javascript
mongoose更新对象的两种方法示例比较
2017/12/19 Javascript
vue中各选项及钩子函数执行顺序详解
2018/08/25 Javascript
手把手15分钟搭一个企业级脚手架
2019/09/16 Javascript
[01:04:01]2014 DOTA2华西杯精英邀请赛5 24 DK VS VG
2014/05/25 DOTA
[03:43]2014DOTA2西雅图国际邀请赛 newbee战队巡礼
2014/07/07 DOTA
[04:03]2014DOTA2西雅图国际邀请赛 LGD战队巡礼
2014/07/07 DOTA
python 编程之twisted详解及简单实例
2017/01/28 Python
Python利用BeautifulSoup解析Html的方法示例
2017/07/30 Python
Python模拟鼠标点击实现方法(将通过实例自动化模拟在360浏览器中自动搜索python)
2017/08/23 Python
python 批量修改/替换数据的实例
2018/07/25 Python
Python代码实现删除一个list里面重复元素的方法
2019/04/02 Python
python读取指定字节长度的文本方法
2019/08/27 Python
调整Jupyter notebook的启动目录操作
2020/04/10 Python
学生档案自我鉴定
2013/10/07 职场文书
生物科学系大学生的自我评价
2013/12/20 职场文书
2014年环境卫生工作总结
2014/11/24 职场文书
文明单位申报材料
2014/12/23 职场文书
爱心捐书倡议书
2015/04/27 职场文书
2015年市场部工作总结
2015/04/30 职场文书