如何实现一个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 相关文章推荐
javascript 放大镜 v1.0 基于Yui2 实现的放大镜效果
Mar 08 Javascript
javascript日期格式化示例分享
Mar 05 Javascript
JS实现网页背景颜色与select框中颜色同时变化的方法
Feb 27 Javascript
学习javascript面向对象 理解javascript原型和原型链
Jan 04 Javascript
javascript html5摇一摇功能的实现
Apr 19 Javascript
轻松学习Javascript闭包
Mar 01 Javascript
详解React开发中使用require.ensure()按需加载ES6组件
May 12 Javascript
vue init失败简单解决方法(终极版)
Dec 22 Javascript
JavaScript原型对象原理与应用分析
Dec 27 Javascript
关于ligerui子页面关闭后,父页面刷新,重新加载的方法
Sep 27 Javascript
详解Angular Karma测试的持续集成实践
Nov 15 Javascript
vue3.0+vue-router+element-plus初实践
Dec 02 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
S900/ ETON E1-XM 收音机
2021/03/02 无线电
邮箱正则表达式实现代码(针对php)
2013/06/21 PHP
PHP第三方登录―QQ登录实现方法
2017/02/06 PHP
centos下file_put_contents()无法写入文件的原因及解决方法
2017/04/01 PHP
PHP处理bmp格式图片的方法分析
2017/07/04 PHP
discuz论坛更换域名,详细文件修改步骤
2020/12/09 PHP
表单内同名元素的控制
2006/11/22 Javascript
通过JAVASCRIPT读取ASP设定的COOKIE
2006/11/24 Javascript
基于jQuery图片平滑连续滚动插件
2009/04/27 Javascript
JavaScript中的集合及效率
2010/01/08 Javascript
控制台报错object is not a function的解决方法
2014/08/24 Javascript
jQuery 浮动导航菜单适合购物商品类型的网站
2014/09/09 Javascript
jQuery中:has选择器用法实例
2014/12/30 Javascript
jQuery复制表单元素附源码分享效果演示
2015/09/30 Javascript
页面内容排序插件jSort使用方法
2015/10/10 Javascript
JavaScript SHA-256加密算法详细代码
2016/10/06 Javascript
JS实现登录页密码的显示和隐藏功能
2017/12/06 Javascript
详解Axios统一错误处理与后置
2018/09/26 Javascript
解决eclipse中没有js代码提示的问题
2018/10/10 Javascript
微信小程序实现简单评论功能
2018/11/28 Javascript
通过实例学习React中事件节流防抖
2019/06/17 Javascript
小程序Request的另类用法详解
2019/08/09 Javascript
taro 实现购物车逻辑的实例代码
2020/06/05 Javascript
JavaScript实现消消乐的源代码
2021/01/12 Javascript
使用Python制作获取网站目录的图形化程序
2015/05/04 Python
详解Python中for循环是如何工作的
2017/06/30 Python
Python 中包/模块的 `import` 操作代码
2019/04/22 Python
python 随机生成10位数密码的实现代码
2019/06/27 Python
Python OpenCV读取中文路径图像的方法
2020/07/02 Python
python使用建议与技巧分享(一)
2020/08/17 Python
python3中数组逆序输出方法
2020/12/01 Python
CSS教程:CSS3圆角属性
2009/04/02 HTML / CSS
荷兰皇家航空公司官方网站:KLM Royal Dutch Airlines
2017/12/07 全球购物
课程改革实施方案
2014/03/16 职场文书
2015年行风建设工作总结
2015/05/15 职场文书
2015小学毕业班工作总结
2015/07/21 职场文书