如何实现一个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实现冻结表格行和列
Apr 29 Javascript
javascript实现倒计时并弹窗提示特效
Jun 05 Javascript
JavaScript中实现map功能代码分享
Jun 11 Javascript
ClearTimeout消除闪动实例代码
Feb 29 Javascript
vue-cli webpack 开发环境跨域详解
May 18 Javascript
禁止弹窗中蒙层底部页面跟随滚动的几种方法
Dec 07 Javascript
JS中用EL表达式获取上下文参数值的方法
Mar 28 Javascript
Vue SSR 组件加载问题
May 02 Javascript
JS验证输入的是否是数字及保留几位小数问题
May 09 Javascript
JS实现横向轮播图(中级版)
Jan 18 Javascript
解决vue $http的get和post请求跨域问题
Jun 07 Vue.js
详解Vue的列表渲染
Nov 20 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
一个没有MYSQL数据库支持的简易留言本的编写
2006/10/09 PHP
用PHP程序实现支持页面后退的两种方法
2008/06/30 PHP
php debug 安装技巧
2011/04/30 PHP
PHP第一季视频教程(李炎恢+php100 不断更新)
2011/05/29 PHP
下拉列表多级联动dropDownList示例代码
2013/06/27 PHP
PHP ignore_user_abort函数详细介绍和使用实例
2014/07/15 PHP
php+MySQL判断update语句是否执行成功的方法
2014/08/28 PHP
php上传图片获取路径及给表单字段赋值的方法
2016/01/23 PHP
jquery autocomplete自动完成插件的的使用方法
2010/08/07 Javascript
JS图片浏览组件PhotoLook的公开属性方法介绍和进阶实例代码
2010/11/09 Javascript
javascript管中窥豹 形参与实参浅析
2011/12/17 Javascript
js捕获鼠标右键菜单中的粘帖事件实现代码
2013/04/01 Javascript
JavaScript判断密码强度(自写代码)
2013/09/06 Javascript
js获得当前系统日期时间的方法
2015/05/06 Javascript
jQuery实现鼠标滑过链接控制图片的滑动展开与隐藏效果
2015/10/28 Javascript
Spring MVC中Ajax实现二级联动的简单实例
2016/07/06 Javascript
JavaScript利用Date实现简单的倒计时实例
2017/01/12 Javascript
Vue.js 插件开发详解
2017/03/29 Javascript
VUE 配置vue-devtools调试工具及安装方法
2018/09/30 Javascript
ES6入门教程之变量的解构赋值详解
2019/04/13 Javascript
Vue项目环境搭建详细总结
2019/09/26 Javascript
python实现可将字符转换成大写的tcp服务器实例
2015/04/29 Python
Python基于opencv调用摄像头获取个人图片的实现方法
2019/02/21 Python
查看python安装路径及pip安装的包列表及路径
2019/04/03 Python
解决Django后台ManyToManyField显示成Object的问题
2019/08/09 Python
解决Django提交表单报错:CSRF token missing or incorrect的问题
2020/03/13 Python
Python自动化之UnitTest框架实战记录
2020/09/08 Python
浅谈Python xlwings 读取Excel文件的正确姿势
2021/02/26 Python
HTML5跳转小程序wx-open-launch-weapp的示例代码
2020/07/16 HTML / CSS
微软英国官方网站:Microsoft英国
2016/10/15 全球购物
搞笑获奖感言
2014/01/30 职场文书
工作过失检讨书
2014/02/23 职场文书
部门年终奖分配方案
2014/05/07 职场文书
项目合作意向书模板
2014/07/29 职场文书
银行授权委托书格式
2014/10/10 职场文书
格列佛游记读书笔记
2015/06/30 职场文书