webpack探索(一)

171 阅读2分钟

webpack 将文件打包成什么

CommonJs 下的打包结果

  • webpack 的打包结果是一个立即执行函数IIFE。这个IIFE接收一个modules对象作为参数,modules对象的key是依赖路径,value是经过处理后的依赖脚本。
    (funcition(modules) {
      // ...
    }({
      'src/index.js': (function(){ 
        // ...
      }),
      'src/hello.js': (function(){ 
        // ...
      }),
      // ...
    }))
    
  • 打包结果中定义了一个模块加载函数_webpack_require_
  • 首先使用 _webpack_require_去加载入口文件
  • 加载函数 _webpack_require_ 中会使用一个闭包变量installedModules, 用来保存已经加载过的依赖,避免重复加载

ES下的打包结果

  • ES 下的打包结果大致和CommonJs相同,只是其中多了一句 _webpack_require_.r(_webpack_exports_) 这个方法的作用是给模块的exports对象打上ES模块化规范的标记。
  • 这个标记用来区分Commonjs 和 ES规范混用的情况

按需加载下的打包结果

首先,回顾一下webpack触发按需加载的方式。通过import()方法指定要单独打包的依赖文件,import方法会返回一个promise对象用来处理依赖加载后的回调

import('./hello').then(sayHello => {
  console.log(sayHello('lala~'))
})

打包之后文件内容相较于之前有比较大的变化,其中最核心的是多了一个_webpack_require_.ewebpackJsonp

  • _webpack_require_.e_初始化了一个promise数组,用来插入script脚本
  • webpackJsonp 会挂载在全局对象上,进行模块安装

webpack的基本工作原理

  • 读取webpack.conf.js 或从shell语句中获得配置参数;
  • 将所需的插件实例化,在webpack的事件流中注册钩子回调,这样在指定的阶段插件就能发挥作用,更改产出结果;
  • 同时根据配置制定的入口文件,从入口文件开始遍历收集依赖。这个过程如下:
    • 先通过loader对文件进行编译
    • 编译好的内容再通过acon解析生成AST
    • 然后分析文件依赖关系,并将不同模块化语法替换成_webpack_require_,即使用webpack自己的模块化加载器
  • 产出结果,打包到配置指定的文件目录。

抽象语法树AST

抽象语法数,是对源代码的一种抽象表示。通过树状的形式表示编程代码的语法结构,树上的每一个节点都是源码中的一种结构和表达。

AST为代码分析提供基础。webpack将代码转换成AST就是为了开发者方便提取模块文件中的信息。

js处理AST的工具

  1. 通过esprima把源码转化成AST
   let esprima = require('esprima');
   let code = 'function ast(){}';
   let ast = esprima.parse(code);
   console.log(ast);
  1. 通过estraverse 遍历AST的节点,并做修改
estraverse.traverse(ast, {
   enter(node) {
     console.log('enter', node.type)
     if (node.type == 'Indentifier') {
       node.name += 'enter';
     }
   },
   leave(node) {
     console.log('leave', node.type)
     if (node.type == 'Indentifier') {
       node.name += 'leave';
     }
   }
 })
  1. 通过 escodegen 将AST重新生成源码
   let result = escodegen.generate(ast)

借助babel工具也可以对AST进行操作

具体参考 babel官方文档