如何实现一个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 相关文章推荐
document.onreadystatechange事件的用法分析
Oct 17 Javascript
从零开始学习jQuery (十一) 实战表单验证与自动完成提示插件
Feb 23 Javascript
js为数字添加逗号并格式化数字的代码
Aug 23 Javascript
jquery实现select选中行、列合计示例
Apr 25 Javascript
常用的jQuery前端技巧收集
Dec 24 Javascript
小发现之浅谈location.search与location.hash的问题
Jun 23 Javascript
React Native模块之Permissions权限申请的实例相机
Sep 28 Javascript
VueJs监听window.resize方法示例
Jan 17 Javascript
使用mpvue搭建一个初始小程序及项目配置方法
Dec 03 Javascript
vue实现动态按钮功能
May 13 Javascript
sortable+element 实现表格行拖拽的方法示例
Jun 07 Javascript
微信小程序之导航滑块视图容器功能的实现代码(简单两步)
Jun 19 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验证码类 附调用方法
2016/06/24 PHP
laravel框架之数据库查出来的对象实现转化为数组
2019/10/23 PHP
JavaScript高级程序设计 阅读笔记(四) ECMAScript中的类型转换
2012/02/27 Javascript
javascript进行四舍五入方法汇总
2014/12/16 Javascript
JavaScript通过prototype给对象定义属性用法实例
2015/03/23 Javascript
JavaScript改变CSS样式的方法汇总
2015/05/07 Javascript
js同源策略详解
2015/05/21 Javascript
MVC Ajax Helper或Jquery异步加载部分视图
2015/11/29 Javascript
基于BootStrap Metronic开发框架经验小结【三】下拉列表Select2插件的使用
2016/05/12 Javascript
web 前端常用组件之Layer弹出层组件
2016/09/22 Javascript
Vue.js 2.0中select级联下拉框实例
2017/03/06 Javascript
javascript观察者模式实现自动刷新效果
2017/09/05 Javascript
vue登录页面cookie的使用及页面跳转代码
2019/07/10 Javascript
layui数据表格跨行自动合并的例子
2019/09/02 Javascript
Vue.js组件使用props传递数据的方法
2019/10/19 Javascript
JS实现json数组排序操作实例分析
2019/10/28 Javascript
[05:13]TI4 中国战队 机场出征!!
2014/07/07 DOTA
python的正则表达式re模块的常用方法
2013/03/09 Python
python魔法方法-属性访问控制详解
2016/07/25 Python
利用python程序帮大家清理windows垃圾
2017/01/15 Python
python使用fork实现守护进程的方法
2017/11/16 Python
对python3 urllib包与http包的使用详解
2018/05/10 Python
Python continue继续循环用法总结
2018/06/10 Python
Python猴子补丁知识点总结
2020/01/05 Python
彻底搞懂 python 中文乱码问题(深入分析)
2020/02/28 Python
python如何调用java类
2020/07/05 Python
3D动画《斗罗大陆》上线当日播放过亿
2021/03/16 国漫
美体小铺印度官网:The Body Shop印度
2019/10/17 全球购物
大学生自我评价范文分享
2014/02/21 职场文书
教师党员岗位承诺书
2014/05/29 职场文书
身边的榜样活动方案
2014/08/20 职场文书
活动总结格式
2014/08/30 职场文书
党章培训心得体会
2014/09/04 职场文书
承德避暑山庄导游词
2015/02/03 职场文书
担保书怎么写 ?
2019/04/22 职场文书
Pytorch GPU内存占用很高,但是利用率很低如何解决
2021/06/01 Python