Open yinguangyao opened 3 years ago
在上篇讲 Nuxt 同构问题的时候,我有提到过 NodeJS 和 webpack 的模块化实现。今天主要来讲解 webpack 中的模块化。
如果你有观察过 webpack 转换后的代码,一定会发现,不管是 import 还是 require 都会被转换成 __webpack_require__ 这种形式。
webpack 自己实现了一套模块化的规范,使用 __webpack_require__ 来导入模块,将其挂载到 module.exports 上面,有点儿类似 CommonJS 的模块化规范。
某天晚上,我的 QQ 群有个童鞋问了这么一个问题:
我也比较好奇为什么 require 引入的图片还需要在后面加个 default 呢?为什么 import 引入的却不需要?是否和 file-loader 处理图片文件有关?
file-loader
带着这个疑问,于是我写了一个简单的 DEMO 来验证了一下,代码如下:
在执行了 webpack 命令后,可以看到编译后的精简代码是这样的:
首先,我们可以看出来这个编译后的 js 文件就是一个立即执行函数,他接收了当前文件引入的外部模块作为一个参数,所有的外部模块被放到了一个对象当中,以当前 src 目录下的绝对路径作为 key 值,value 这是一个方法,这个方法注入了 webpack_require 和 webpack_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 被编译后的代码:
__webpack_exports__["default"]
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__ 的实现。
__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 来导入的话会怎么样呢?
module.exports
default
require
/***/ "./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 也是可以导入的。
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"img\", {\n src: _logo_png__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n })
实际上,如果你在 NodeJS 里面使用过一些 npm 上面第三方的模块,会发现导入的时候都是要求我们使用 require('').default 的,比如大名鼎鼎的 node-xlsx:
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 来导入它。
export default
require.default
前言
在上篇讲 Nuxt 同构问题的时候,我有提到过 NodeJS 和 webpack 的模块化实现。今天主要来讲解 webpack 中的模块化。
如果你有观察过 webpack 转换后的代码,一定会发现,不管是 import 还是 require 都会被转换成 __webpack_require__ 这种形式。
webpack 自己实现了一套模块化的规范,使用 __webpack_require__ 来导入模块,将其挂载到 module.exports 上面,有点儿类似 CommonJS 的模块化规范。
一个来自 QQ 群的提问
某天晚上,我的 QQ 群有个童鞋问了这么一个问题:
我也比较好奇为什么 require 引入的图片还需要在后面加个 default 呢?为什么 import 引入的却不需要?是否和
file-loader
处理图片文件有关?带着这个疑问,于是我写了一个简单的 DEMO 来验证了一下,代码如下:
在执行了 webpack 命令后,可以看到编译后的精简代码是这样的:
webpack 模块源码分析
首先,我们可以看出来这个编译后的 js 文件就是一个立即执行函数,他接收了当前文件引入的外部模块作为一个参数,所有的外部模块被放到了一个对象当中,以当前 src 目录下的绝对路径作为 key 值,value 这是一个方法,这个方法注入了 webpack_require 和 webpack_exports 作为参数,简单来说就是类似于:
我们可以明显看到,这个图片在导出的时候,实际上是在
__webpack_exports__["default"]
里面的,那么在使用 require 引入的时候又是什么样的呢? 我们来看一下index.tsx
被编译后的代码:很明显可以看到,这里在引入 logo 这个图片的时候,是直接使用
__webpack_require__
来导入的,我们前面看到过__webpack_require__
的实现。这里只是返回了
module.exports
,并没有default
,所以如果是直接用require
来引入图片的话,那就肯定不会生效。 如果我们使用 import 来导入的话会怎么样呢?我们明显可以看到,虽然导入的时候也没有带上一个
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
:相信看了前面的分析后,你也能猜到这是为什么了吧?
node-xlsx
是直接使用export default
导出的,而 webpack 为了抹平这种差异,导致我们不得不使用require.default
来导入它。