如何实现一个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 相关文章推荐
from 表单提交返回值用post或者是get方法实现
Aug 21 Javascript
jQuery学习笔记之2个小技巧
Jan 19 Javascript
javascript框架设计之框架分类及主要功能
Jun 23 Javascript
Javascript编程中几种继承方式比较分析
Nov 28 Javascript
javascript实现图片轮播效果
Jan 20 Javascript
编写高质量JavaScript代码的基本要点
Mar 02 Javascript
浅谈Node.js轻量级Web框架Express4.x使用指南
May 03 Javascript
Express框架之connect-flash详解
May 31 Javascript
JS作用域链详解
Jun 26 Javascript
jQuery实现表格冻结顶栏效果
Aug 20 jQuery
使用jQuery给Table动态增加行、清空table的方法
Sep 05 jQuery
微信小程序实现动态显示和隐藏某个控件功能示例
Dec 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中使用crypt()实现用户身份验证的代码
2012/09/05 PHP
php中base_convert()进制数字转换函数实例
2014/11/20 PHP
关于PHP内置的字符串处理函数详解
2017/02/04 PHP
php对xml文件的增删改查操作实现方法分析
2017/05/19 PHP
javascript强大的日期函数代码分享
2013/09/04 Javascript
JavaScript表格常用操作方法汇总
2015/04/15 Javascript
JS中的THIS和WINDOW.EVENT.SRCELEMENT详解
2015/05/25 Javascript
template.js前端模板引擎使用详解
2017/10/10 Javascript
使用PreloadJS加载图片资源的基础方法详解
2020/02/03 Javascript
深入了解JavaScript词法作用域
2020/07/29 Javascript
JS中循环遍历数组的四种方式总结
2021/01/23 Javascript
[01:28]一分钟告诉你DOTA2 TI9不朽宝藏Ⅱ中有什么!
2019/07/09 DOTA
Python多进程同步Lock、Semaphore、Event实例
2014/11/21 Python
深入学习Python中的装饰器使用
2016/06/20 Python
简单了解Python中的几种函数
2017/11/03 Python
python下解压缩zip文件并删除文件的实例
2018/04/24 Python
Python正则表达式指南 推荐
2018/10/09 Python
python顺序执行多个py文件的方法
2019/06/29 Python
python使用sklearn实现决策树的方法示例
2019/09/12 Python
如何解决django-celery启动后迅速关闭
2019/10/16 Python
python给指定csv表格中的联系人群发邮件(带附件的邮件)
2019/12/31 Python
Python3爬虫关于识别点触点选验证码的实例讲解
2020/07/30 Python
python 19个值得学习的编程技巧
2020/08/15 Python
PyTorch预训练Bert模型的示例
2020/11/17 Python
python 爬虫之selenium可视化爬虫的实现
2020/12/04 Python
美国第一香水网站:Perfume.com
2017/01/23 全球购物
iHerb中文官网:维生素、保健品和健康产品
2018/11/01 全球购物
工作的心得体会
2013/12/31 职场文书
校庆接待方案
2014/03/18 职场文书
公证书标准格式
2014/04/10 职场文书
团队激励口号
2014/06/06 职场文书
2014年党员自我评议(5篇)
2014/09/12 职场文书
领导离职感言
2015/08/03 职场文书
加强党性修养心得体会
2016/01/21 职场文书
庭外和解协议书
2016/03/23 职场文书
防止web项目中的SQL注入
2021/12/06 MySQL