Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

深入理解 webpack 模块 #43

Open
yinguangyao opened this issue Oct 6, 2020 · 0 comments
Open

深入理解 webpack 模块 #43

yinguangyao opened this issue Oct 6, 2020 · 0 comments

Comments

@yinguangyao
Copy link
Owner

前言

在上篇讲 Nuxt 同构问题的时候,我有提到过 NodeJS 和 webpack 的模块化实现。今天主要来讲解 webpack 中的模块化。

如果你有观察过 webpack 转换后的代码,一定会发现,不管是 import 还是 require 都会被转换成 webpack_require 这种形式。

image

webpack 自己实现了一套模块化的规范,使用 webpack_require 来导入模块,将其挂载到 module.exports 上面,有点儿类似 CommonJS 的模块化规范。

一个来自 QQ 群的提问

某天晚上,我的 QQ 群有个童鞋问了这么一个问题:

image

image

我也比较好奇为什么 require 引入的图片还需要在后面加个 default 呢?为什么 import 引入的却不需要?是否和 file-loader 处理图片文件有关?

带着这个疑问,于是我写了一个简单的 DEMO 来验证了一下,代码如下:

image

在执行了 webpack 命令后,可以看到编译后的精简代码是这样的:

image

webpack 模块源码分析

首先,我们可以看出来这个编译后的 js 文件就是一个立即执行函数,他接收了当前文件引入的外部模块作为一个参数,所有的外部模块被放到了一个对象当中,以当前 src 目录下的绝对路径作为 key 值,value 这是一个方法,这个方法注入了 webpack_requirewebpack_exports 作为参数,简单来说就是类似于:

(function(modules) {
})({
 "./src/logo.png": function(module, __webpack_exports__, __webpack_require__) {
    eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (__webpack_require__.p + \"a218f2cb12bf56dd2a68003790d1e986.png\");\n\n//# sourceURL=webpack:///./src/logo.png?");
  }
})

我们可以明显看到,这个图片在导出的时候,实际上是在 __webpack_exports__["default"] 里面的,那么在使用 require 引入的时候又是什么样的呢?
我们来看一下 index.tsx 被编译后的代码:

/***/ "./src/pages/home/index.jsx":
/*!**********************************!*\
  !*** ./src/pages/home/index.jsx ***!
  \**********************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__, \"default\", function() { return Index; }); var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(\"./node_modules/_react@16.13.1@react/index.js\"); var react__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); var _constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(\"./src/pages/home/constants.js\");\n\n\n\nvar logo = __webpack_require__( \"./src/logo.png\");\n\nfunction Index(props) {\n  return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"img\", {\n    src: logo\n  });\n}\n\n//# sourceURL=webpack:///./src/pages/home/index.jsx?");

很明显可以看到,这里在引入 logo 这个图片的时候,是直接使用 __webpack_require__ 来导入的,我们前面看到过 __webpack_require__ 的实现。

/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {},
/******/ 			hot: hotCreateModule(moduleId),
/******/ 			parents: (hotCurrentParentsTemp = hotCurrentParents, hotCurrentParents = [], hotCurrentParentsTemp),
/******/ 			children: []
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}

这里只是返回了 module.exports,并没有 default,所以如果是直接用 require 来引入图片的话,那就肯定不会生效。
如果我们使用 import 来导入的话会怎么样呢?

/***/ "./src/pages/home/index.jsx":
/*!**********************************!*\
  !*** ./src/pages/home/index.jsx ***!
  \**********************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__, \"default\", function() { return Index; }); var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(\"./node_modules/_react@16.13.1@react/index.js\"); var react__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); var _constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(\"./src/pages/home/constants.js\"); var _logo_png__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__( \"./src/logo.png\");\n\n\n\n\nvar constants = __webpack_require__( \"./src/pages/home/constants.js\");\n\nfunction Index(props) {\n  return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"img\", {\n    src: _logo_png__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n  });\n}\n\n//# sourceURL=webpack:///./src/pages/home/index.jsx?");

我们明显可以看到,虽然导入的时候也没有带上一个 default,但是 React 在创建 img 标签的时候,给它带上了一个 default,关键点在于这句 return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"img\", {\n src: _logo_png__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n }),所以直接用 import 也是可以导入的。

es module 和 commonjs

实际上,如果你在 NodeJS 里面使用过一些 npm 上面第三方的模块,会发现导入的时候都是要求我们使用 require('').default 的,比如大名鼎鼎的 node-xlsx

import xlsx from 'node-xlsx';
// Or var xlsx = require('node-xlsx').default;

const data = [[1, 2, 3], [true, false, null, 'sheetjs'], ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'], ['baz', null, 'qux']];
var buffer = xlsx.build([{name: "mySheetName", data: data}]); // Returns a buffer

相信看了前面的分析后,你也能猜到这是为什么了吧?node-xlsx 是直接使用 export default 导出的,而 webpack 为了抹平这种差异,导致我们不得不使用 require.default 来导入它。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant