如何实现一个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 select下拉框操作的一些说明
Apr 02 Javascript
浅谈Javascript中深复制
Dec 01 Javascript
jquery使用each方法遍历json格式数据实例
May 18 Javascript
jQuery实现鼠标点击弹出渐变层的方法
Jul 09 Javascript
原生js实现查询天气小应用
Dec 09 Javascript
ajax与json 获取数据并在前台使用简单实例
Jan 19 Javascript
json数据处理及数据绑定
Jan 25 Javascript
封装运动框架实战左右与上下滑动的焦点轮播图(实例)
Oct 17 Javascript
JavaScript体验异步更好的解决办法
Jan 08 Javascript
详解vue中v-model和v-bind绑定数据的异同
Aug 10 Javascript
Openlayers绘制地图标注
Sep 28 Javascript
JS画布动态实现黑客帝国背景效果
Nov 08 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下清空字符串中的HTML标签的代码
2010/09/06 PHP
PHP中break及continue两个流程控制指令区别分析
2011/04/18 PHP
php中大括号作用介绍
2012/03/22 PHP
php从数组中随机抽取一些元素的代码
2012/11/05 PHP
php json与xml序列化/反序列化
2013/10/28 PHP
php中有关合并某一字段键值相同的数组合并的改进
2015/03/10 PHP
在Mac OS上编译安装Nginx+PHP+MariaDB开发环境的教程
2016/02/23 PHP
php实现base64图片上传方式实例代码
2017/02/22 PHP
Stop SQL Server
2007/06/21 Javascript
javaScript checkbox 全选/反选及批量删除
2010/04/28 Javascript
JavaScript中setInterval的用法总结
2013/11/20 Javascript
jQuery实现进度条效果代码
2015/12/17 Javascript
Angularjs material 实现搜索框功能
2016/03/08 Javascript
JavaScript调试的多个必备小Tips
2017/01/15 Javascript
js实现3D图片展示效果
2017/03/09 Javascript
浅谈angular.js跨域post解决方案
2017/08/30 Javascript
JavaScript实现短信倒计时60s
2017/10/09 Javascript
Sublime Text新建.vue模板并高亮(图文教程)
2017/10/26 Javascript
vue-lazyload图片延迟加载插件的实例讲解
2018/02/09 Javascript
react-native聊天室|RN版聊天App仿微信实例|RN仿微信界面
2019/11/12 Javascript
如何封装Vue Element的table表格组件
2021/02/06 Vue.js
Python实现向服务器请求压缩数据及解压缩数据的方法示例
2017/06/09 Python
Django 自定义分页器的实现代码
2019/11/24 Python
python字符串判断密码强弱
2020/03/18 Python
Python不支持 i ++ 语法的原因解析
2020/07/22 Python
Python自定义sorted排序实现方法详解
2020/09/18 Python
django inspectdb 操作已有数据库数据的使用步骤
2021/02/07 Python
使用CSS3实现多列布局与多背景的技巧
2016/02/29 HTML / CSS
澳大利亚免息网上购物:Shop Zero
2016/09/17 全球购物
.NET面试题:什么是反射
2016/09/30 面试题
校园联欢晚会主持词
2014/03/17 职场文书
志愿者活动总结
2014/04/28 职场文书
校园演讲稿汇总
2014/05/21 职场文书
2014年客房部工作总结
2014/11/22 职场文书
优秀教师申报材料
2014/12/16 职场文书
JavaScript中的LHS和RHS分析详情
2022/04/06 Javascript