如何实现一个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的$(document).ready()和onload的加载顺序
May 26 Javascript
jquery实现TAB选项卡鼠标经过带延迟效果的方法
Jul 27 Javascript
JavaScript原型及原型链终极详解
Jan 04 Javascript
javascript检查某个元素在数组中的索引值
Mar 30 Javascript
Javascript获取图片原始宽度和高度的方法详解
Sep 20 Javascript
基于slideout.js实现移动端侧边栏滑动特效
Nov 28 Javascript
jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)
Dec 22 Javascript
JS简单获取当前日期时间的方法(如:2017-03-29 11:41:10 星期四)
Mar 29 Javascript
详解JavaScript中关于this指向的4种情况
Apr 18 Javascript
vue element-ui读取pdf文件的方法
Nov 26 Javascript
JavaScript前端实现压缩图片功能
Mar 06 Javascript
vue下canvas裁剪图片实例讲解
Apr 16 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中截取字符串支持utf-8
2007/01/18 PHP
根据中文裁减字符串函数的php代码
2013/12/03 PHP
php求正负数数组中连续元素最大值示例
2014/04/11 PHP
PHP PDOStatement::closeCursor讲解
2019/01/30 PHP
php获取微信openid方法总结
2019/10/10 PHP
Javascript调试工具(下载)
2007/01/09 Javascript
JavaScript 异步调用框架 (Part 4 - 链式调用)
2009/08/04 Javascript
轻轻松松学JS调试(不下载任何工具)
2010/04/14 Javascript
再论Javascript的类继承
2011/03/05 Javascript
用html5 js实现点击一个按钮达到浏览器全屏效果
2014/05/28 Javascript
nodejs 提示‘xxx’ 不是内部或外部命令解决方法
2014/11/20 NodeJs
JavaScript中的分号插入机制详细介绍
2015/02/11 Javascript
JavaScript多线程详解
2015/08/12 Javascript
JS+CSS实现简单的二级下拉导航菜单效果
2015/09/21 Javascript
Webpack 实现 Node.js 代码热替换
2015/10/22 Javascript
JS实现的颜色实时渐变效果完整实例
2016/03/25 Javascript
three.js实现3D视野缩放效果
2017/11/16 Javascript
使用sessionStorage解决vuex在页面刷新后数据被清除的问题
2018/04/13 Javascript
nodejs用gulp管理前端文件方法
2018/06/24 NodeJs
vue+axios全局添加请求头和参数操作
2020/07/24 Javascript
全局安装 Vue cli3 和 继续使用 Vue-cli2.x操作
2020/09/08 Javascript
Python 3 实现定义跨模块的全局变量和使用教程
2019/07/07 Python
使用pandas实现连续数据的离散化处理方式(分箱操作)
2019/11/22 Python
python实现按关键字筛选日志文件
2019/12/24 Python
浅谈Selenium 控制浏览器的常用方法
2020/12/04 Python
Shopee马来西亚:随拍即卖,最佳行动电商拍卖平台
2017/06/05 全球购物
美国最大的半成品净菜电商:Blue Apron(蓝围裙)
2018/04/27 全球购物
澳大利亚优质的家居用品和生活方式公司:Bed Bath N’ Table
2019/04/16 全球购物
Scotch Porter官方网站:男士美容产品
2020/08/31 全球购物
网络工程师面试(三木通信技术有限公司)
2013/06/05 面试题
运动会稿件100字
2014/02/21 职场文书
护士节策划方案
2014/05/19 职场文书
高二学年自我鉴定范文(2篇)
2014/09/26 职场文书
小学教师个人总结
2015/02/05 职场文书
2015年环保局工作总结
2015/05/22 职场文书
七年级之开学家长寄语35句
2019/09/05 职场文书