webpack/babel如何处理模块

252 阅读4分钟

背景:开发webpack项目时,发现动态import导入的模块没有分包,这和webpack官方介绍的“动态导入实现动态代码分离”不一致,在排查该问题的过程中学习到了webpack/babel处理模块的方式,从而有了这篇文章。

babel是如何处理模块的

vue-cli的babel预设@vue/cli/plugin-babel/preset

背景项目是一个vue-cli项目,根目录下的babel.config.js文件中配置的babel的presets(预设)是@vue/cli-plugin-babel/preset

@vue/cli/plugin-babel/preset中包含了@babel/preset-env

babel的预设@babel/preset-env

@babel/preset-env是babel官方提供的preset(预设)

测试使用的版本是v7.25.3

@babel/core版本是v7.25.2

core-js版本是v3.37.1

预设@babel/preset-env的modules参数

modules的可选值"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false, 默认是 "auto".

image.png

modules用于配置将ES模块语法转化为另一种模块类型。

  1. 设置为false将保留ES模块

    设置为false的时候,babel会保留import()语句,不转换。

    babel会使用@babel/plugin-syntax-dynamic-import插件处理。

    以下是babel给出的一个提示:

     /repl.js: @babel/plugin-transform-dynamic-import depends on a modules
     transform plugin. Supported plugins are:
      - @babel/plugin-transform-modules-commonjs ^7.4.0
      - @babel/plugin-transform-modules-amd ^7.4.0
      - @babel/plugin-transform-modules-systemjs ^7.4.0
    
     If you are using Webpack or Rollup and thus don't want
     Babel to transpile your imports and exports, you can use
     the @babel/plugin-syntax-dynamic-import plugin and let your
     bundler handle dynamic imports.
    
  2. 设置为umd

设置umd时,会提示“动态导入只能在将ES模块转换为AMD、CommonJS或SystemJS时转换”

  1. 设置为commonjs
import("jquery").then($ => {});

Promise.resolve()  
.then(() => _interopRequireWildcard(require("jquery")))  
.then(($) => {});
  1. 设置为amd
import("jquery").then($ => {});

define(["require"], function (_require) {  
    new Promise((_resolve, _reject) =>  
    _require(  
    ["jquery"],  
    (imported) => _resolve(_interopRequireWildcard(imported)),  
    _reject  
    )  
    ).then(($) => {});  
});

插件 @babel/plugin-transform-dynamic-import

  • 设置为amd,commonjs,systemjs时,babel会使用@babel/plugin-transform-dynamic-import插件将import语句转换

此插件用于将import()语句转换成非ESM模块的格式。

如果你使用了一个打包工具(bundler),比如Webpack,Rollup或者Parcal,你可以不使用这个插件,而是让打包工具(bundler)去处理import()语句。

此插件配合@babel/plugin-transform-modules-commonjs可以将ESM模块转换成CommonJS模块;

配合@babel/plugin-transform-modules-amd可以将ESM模块转换成AMD模块;

配合@babel/plugin-transform-modules-systemjs可以将ESM模块转换成SystemJs模块。

webpack 转换模块

babel转换后,webpack会将上面各种类型(amd,commonjs,umd,esm,systemjs)的模块都统一转化成webpack的模块。

webpack官网介绍的模块

webpack2支持原生的ES6模块语法,意味着你无需额外引入babel这样的工具,就可以使用import和export。但是注意,如果使用其他的ES6+特性,仍然需要引入babel。webpack支持以下的方法:

Webpack处理ES6的import()

动态的加载模块。调用import的之处,被视为分割点,意思是,被请求的模块和它引用的所有子模块,会分割到一个单独的chunk中。

Webpack处理AMD的require

与require.ensure类似,Webpack将其对应的导入文件拆分到一个单独的bundle中,此bundle会被异步加载。

综上所述:**babel预设的modules设置为false,auto和amd时import()会分包;设置为commonjs不分包。**

Webpack处理各种模块的源码

Webpack源码lib/dependencies目录下有下面这些处理不同模块语句的js文件

webpack转换模块导入导出语句.svg

Webpack是如何处理异步加载的

Webpack是通过JSONP实现异步加载的;具体实现可以看__webpack_require__.e

Webpack如何转换AMD require和import

源码路径lib/RuntimeTemplate.js下的blockPromise方法:

image.png

AMDRequireDependency中调用blockPromise

image.png

问题项目动态import未分包的原因

在上面的“问题项目”,将modules设置为false,import()语句还是会被转化为commonjs模块, commonjs模块不支持异步加载,这就导致转换后的模块脚本丢失了动态加载的特征,最终webpack转换后的脚本也没有了动态加载,也就不分包了

经过排查,导致这个的原因是在babel中增加了下面这段配置:

'env': {
     'development': {
         'plugin': ['dynamic-import-node']
     }
}

babel插件@babel-plugin-dynamic-import-node

这个插件用于将import()语句转换成延迟的require()。

Webpack >=2 已经支持了import()转换,Webpack1你才需要使用babel-plugin- dynamic-import-webpack。

上面问题项目用的是Webpack4,所以是不需要加上这个插件的。

参考 github.com/babel/babel… blog.csdn.net/jayccx/arti…