前言
在阅读本文之前需要对以下概念有相当清晰认知: 请前往 模块化
- 1、模块化是什么?
- 2、有哪些模块化标准?
webpack是什么?webpack是一款模块加载器兼打包工具。
- 作为一款模块加载器,它能把所有的资源文件(JS、JSX、CSS、CoffeeScript、Less、Sass、Image等)都作为模块来使用和处理。
- 作为一个模块打包工具,主要功能是打包资源文件并整合到一个包中,我们在开发时,只需要引用一个包文件,就能加载预先设计好的模块功能,同时允许以指定模块化规范输出。
那么作为一款模块加载器。在浏览器运行时,我想弄明白以下几个问题:
- 1、webpack如何实现对各种不同JS模块规范的兼容处理?
- 2、webpack如何实现对js类型文件之外的模块化处理的?
作为一个模块打包工具,在浏览器运行时,我想知道:
- 1、webpack将打包资源文件并整合到一个包中之后,是如何按指定模块化规范输出的?
本文以打包结果为导向,对比我们的源码,探讨在浏览器端运行时,webpack输出的文件是如何处理js文件,css文件、图片、字体等,以及按需加载,以达到模块化的管理。
[一] 初始化webpack项目
1.1 新建一个文件夹webpack-demo,安装依赖。
npm init -y
npm install webpack webpack-cli -D
复制代码
1.2 配置打包的输入(entry)与输出(output)
新建webpack.config.js
module.exports = {
entry:'./src/index.js', // 输入输出
output: {
path: path.resolve(__dirname, "./dist"), // 打包后的目录
filename:"bundle.js"
}
}
复制代码
1.3 配置打包命令
在package.json增加
{
scripts:{
"build": "webpack --config webpack.config.js"
}
}
复制代码
1.4 新建入口文件
在src/index.js
随便写点什么
const foo = function(){
console.log('this is foo')
}
export default foo
复制代码
1.5 执行打包命令 npm run build
可以在根目录下找到dist/bundle.js
【二】分析bundle.js
将bundle.js copy 过来删除部分注释之后大致内容如下:
(function(modules) { // webpackBootstrap
// 1. 定义一个模块缓存对象
var installedModules = {};
// 2. require 函数的实现
function __webpack_require__(moduleId) {
// 2.1 首先会检查模块缓存
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 2.2 缓存不存在时,创建并缓存一个新的模块对象,
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// 2.3 调用模块函数
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 2.4 标记模块为已加载
module.l = true;
// 2.5 返回输出模块
return module.exports;
}
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// define getter function for harmony exports
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};
// define __esModule on exports
__webpack_require__.r = function(exports) {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
// create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
__webpack_require__.t = function(value, mode) {
if(mode & 1) value = __webpack_require__(value);
if(mode & 8) return value;
if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, 'default', { enumerable: true, value: value });
if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
return ns;
};
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
// 3. 加载入口文件
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})({
"./src/index.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\nvar foo = function foo() {\n console.log(\"this is foo\");\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (foo);\n\n//# sourceURL=webpack:///./src/index.js?");
})
})
复制代码
2.1 整体结构
webpack 作为模块加载器,打包结果bundle.js
中的内容分为两部分,运行时代码和我们的源代码。原理是通过IIFE作为启动器,自执行函数体是webpack管理各个模块代码在浏览器端运行时所需的代码。自执行函数的参数就是我们的源代码。
(function(modules) { // webpackBootstrap
statements
// 此处 require 入口模块
})(
// modules对象集合
{
path1:function(){ /*xxx.js源码*/ },
path2:function(){ /*xxx.js源码*/ }
}
)
复制代码
简单来理解,webpack模块化机制是通过自执行函数(IIFE)启动,然后通过webpack自定义的 exports 和 require 来实现的各个依赖模块化管理。
2.2 require的实现
__webpack_require__函数 主要实现逻辑如下
- 2.2.1 首先会检查模块缓存,如果有则返回缓存模块的export对象,即module.exports
- 2.2.2 缓存不存在时,创建一个新的模块对象并放入缓存
- 2.2.3 调用模块函数,同时将新的模块对象和__webpack_require__作为参数传入
- 2.2.4 标记模块为已加载
- 2.2.5 返回输出模块 exports对象
webpack_require 挂载一些方法和属性
- | 注释 | 描述 |
---|---|---|
webpack_require.m | // expose the modules object | 暴露传进来的modules对象 |
webpack_require.c | // expose the module cache | 暴露已经缓存起来的模块 installedModules(可能还未加载完) |
webpack_require.d | // define getter function for harmony exports | 将具体操作绑定到属性a的getter方法上 |
webpack_require.r | // define __esModule on exports | 用于给__webpack_exports__添加一个 __esModule 为 true 的属性,表示这是一个 ES6 module |
webpack_require.t | // | |
webpack_require.n | // getDefaultExport function for compatibility with non-harmony modules | 用于判断__webpack_exports__是否为es模块,当__esModule为true的时候,标识__webpack_exports__为es模块,那么module[default]默认返回export default对应的变量,否则返回直接返回export |
webpack_require.o | // Object.prototype.hasOwnProperty.call | 判断是否自有属性 |
webpack_require.p | // __webpack_public_path__ | Webpack 配置中的 publicPath,用于加载被分割出去的异步代码 |
webpack_require.s | // Load entry module and return exports | 保存了启动模块路径 |
2.3 export的实现(Es6 module)
2.3.1 源文件
// ========== 源文件 ========== start
// src/module-es6.js
export let moduleValue = "moduleValue"
export default function () {
console.log("this is es6-module")
}
// src/index.js
import moduleDefault, { moduleValue } from "./module-es6.js";
// ========== 源文件 ========== end
复制代码
2.3.2 编译打包结果
// ========== 编译打包结果 ========== start
'./src/index.js':
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _module_es6_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( \"./src/module-es6.js\");\n\nconsole.log(_module_es6_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n//# sourceURL=webpack:///./src/index.js?");
}),
'./src/module-es6.js':
/*! exports provided: moduleValue, default */
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"moduleValue\", function() { return moduleValue; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return f; });\nvar moduleValue = \"moduleValue\";\n\nfunction f() {\n console.log(\"this is es6-module\");\n}\n\n//# sourceURL=webpack:///./src/module-es6.js?");
})
// ========== 编译打包结果 ========== end
复制代码
2.3.3 分析打包结果
// 2.3.3.1 先从module-es6.js 导出对象来分析(即 导出过程__webpack_require__函数的返回值 module.exports)
{
default: f(), // 对应 export default 关键字
moduleValue: 'moduleValue', // 对应 export 关键字
__esModule:true // 用于标识 es6模块
}
// index.js 中通过import引入es6模块
// 2.3.3.2 import 实际上通过__webpack_require__函数引入
var _module_es6_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( \"./src/module-es6.js\")
// 2.3.3.3 对应变量的使用也转化为导出对象的属性访问
_module_es6_js__WEBPACK_IMPORTED_MODULE_0__['default']
_module_es6_js__WEBPACK_IMPORTED_MODULE_0__['moduleValue']
复制代码
2.4 export的实现(commonJs module)
2.4.1 源文件
// module-commonjs.js
exports.moduleValue1 = "moduleValue1";
exports.moduleValue2 = "moduleValue2";
// src.js
import moduleDefault, { moduleValue1, moduleValue2 } from "./module-commonjs.js";
console.log(moduleDefault);
console.log(moduleValue1, moduleValue2);
复制代码
2.4.2 编译打包结果
// ========== 编译打包结果 ========== start
"./src/index.js":
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _module_commonjs_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./module-commonjs.js */ \"./src/module-commonjs.js\");\n/* harmony import */ var _module_commonjs_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_module_commonjs_js__WEBPACK_IMPORTED_MODULE_0__);\n\nconsole.log(_module_commonjs_js__WEBPACK_IMPORTED_MODULE_0___default.a);\nconsole.log(_module_commonjs_js__WEBPACK_IMPORTED_MODULE_0__[\"moduleValue1\"], _module_commonjs_js__WEBPACK_IMPORTED_MODULE_0__[\"moduleValue2\"]);\n\n//# sourceURL=webpack:///./src/index.js?");
}),
"./src/module-commonjs.js":
(function(module, exports) {
eval("// module-commonjs.js\nexports.moduleValue1 = \"moduleValue1\";\nexports.moduleValue2 = \"moduleValue2\";\n\n//# sourceURL=webpack:///./src/module-commonjs.js?");
})
// ========== 编译打包结果 ========== end
复制代码
2.4.3 分析
// ========== 分析 ========== start
// 2.4.3.1、先从结果来看module-commonjs.js 导出对象(即 __webpack_require__函数的返回值 module.exports)
{
moduleValue1: 'moduleValue1',
moduleValue2: 'moduleValue2'
}
// index.js 中通过import引入common js模块
// 2.4.3.2 对应变量的使用也转化为 导出对象的属性访问
_module_commonjs_js__WEBPACK_IMPORTED_MODULE_0___default.a // 通过__webpack_require__.n 获取默认模块
_module_es6_js__WEBPACK_IMPORTED_MODULE_0__['moduleValue1']
_module_es6_js__WEBPACK_IMPORTED_MODULE_0__['moduleValue2']
// ========== 分析 ========== end
复制代码
当然模块化标准不只是 CommoJs 和 ES Harmony,还有AMD、CMD等,由于篇幅原因就不在展开
【三】实现按需加载(引入异步模块)原理
3.1 引入异步模块module-dynamic
举个简单例子
// module-dynamic.js
export default function (msg) {
console.log(msg);
}
// index.js
import("./module-dynamic").then((show) => {
// 执行 show 函数
show("Webpack");
});
复制代码
3.2 执行打包命令 npm run build
重新构建后会输出两个文件,分别是执行入口文件 bundle.js
和异步加载文件 0.bundle.js
。
3.2.1 0.bundle.js
内容如下
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0],{
"./src/module-dynamic.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function (msg) {\n console.log(msg);\n});\n\n//# sourceURL=webpack:///./src/module-dynamic.js?"); })}]);
复制代码
3.2.2 重新构建之后bundle.js 内容如下
(function(){
/***
* webpackJsonp 用于从异步加载的文件中安装模块。
* 把 webpackJsonp 挂载到全局是为了方便在其它文件中调用。
*
* @param chunkIds 异步加载的文件中存放的需要安装的模块对应的 Chunk ID
* @param moreModules 异步加载的文件中存放的需要安装的模块列表
*/
// install a JSONP callback for chunk loading
function webpackJsonpCallback(data) {
var chunkIds = data[0];
var moreModules = data[1];
// add "moreModules" to the modules object,
// then flag all "chunkIds" as loaded and fire callback
// 收集模块,将所有“chunkIds”标记为已加载
var moduleId, chunkId, i = 0, resolves = [];
for(;i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0;
}
// 将异步模块内容插入到主文件模块对象 modules 中,以便后续直接使用
for(moduleId in moreModules) {
if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
if(parentJsonpFunction) parentJsonpFunction(data);
while(resolves.length) {
resolves.shift()();
}
};
// object to store loaded and loading chunks
// undefined = chunk not loaded, null = chunk preloaded/prefetched
// Promise = chunk loading, 0 = chunk loaded
var installedChunks = {
"main": 0
};
// script path function
function jsonpScriptSrc(chunkId) {
return __webpack_require__.p + "" + chunkId + ".bundle." + "f26a295c3de9e9af4369" + ".js"
}
/**
* 用于加载被分割出去的,需要异步加载的 Chunk 对应的文件
* @param chunkId 需要异步加载的 Chunk 对应的 ID
* @returns {Promise}
*/
__webpack_require__.e = function requireEnsure(chunkId) {
var promises = [];
// JSONP chunk loading for javascript
// 去检查 chunkId 对应的 Chunk 是否安装成功,安装成功时才会存在于 installedChunks 中
var installedChunkData = installedChunks[chunkId];
if(installedChunkData !== 0) { // 0 means "already installed".
// a Promise means "currently loading".
// installedChunkData 不为空且不为0表示该 Chunk 正在网络加载中
if(installedChunkData) {
promises.push(installedChunkData[2]);
} else {
// setup Promise in chunk cache
var promise = new Promise(function(resolve, reject) {
installedChunkData = installedChunks[chunkId] = [resolve, reject];
});
promises.push(installedChunkData[2] = promise);
// start chunk loading
var script = document.createElement('script');
var onScriptComplete;
script.charset = 'utf-8';
script.timeout = 120;
if (__webpack_require__.nc) {
script.setAttribute("nonce", __webpack_require__.nc);
}
script.src = jsonpScriptSrc(chunkId);
// create error before stack unwound to get useful stacktrace later
var error = new Error();
onScriptComplete = function (event) {
// avoid mem leaks in IE.
// 防止内存泄露
script.onerror = script.onload = null;
clearTimeout(timeout);
var chunk = installedChunks[chunkId];
if(chunk !== 0) {
if(chunk) {
var errorType = event && (event.type === 'load' ? 'missing' : event.type);
var realSrc = event && event.target && event.target.src;
error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
error.name = 'ChunkLoadError';
error.type = errorType;
error.request = realSrc;
chunk[1](error);
}
installedChunks[chunkId] = undefined;
}
};
// 设置最长加载时间
var timeout = setTimeout(function(){
onScriptComplete({ type: 'timeout', target: script });
}, 120000);
script.onerror = script.onload = onScriptComplete;
document.head.appendChild(script);
}
}
return Promise.all(promises);
};
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
jsonpArray.push = webpackJsonpCallback;
jsonpArray = jsonpArray.slice();
for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
var parentJsonpFunction = oldJsonpFunction;
"./src/index.js":
(function(module, exports, __webpack_require__) {
eval("__webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ./module-dynamic */ \"./src/module-dynamic.js\")).then(function (show) {\n // 执行 show 函数\n show(\"Webpack\");\n});\n\n//# sourceURL=webpack:///./src/index.js?");
})
})
复制代码
对比发现,旧的bundle.js 和引入异步的 bundle.js 非常相似,区别在于:
- 1、定义了一个对象 installedChunks 作用是缓存动态模块。
- 2、定义了一个辅佐函数jsonScriptSrc(),作用是根据chunkId 生产动态模块的URL
- 2、多了两个核心方法,一个
__webpack_require__.e
函数用于加载被分割出去的,需要异步加载的chunk对应的文件,并返回一个promise对象,另一个webpackJsonCallback
函数用于处理异步模块的安装; - 3、定义了一个
webpackJsonp=[]
全局变量,用于存储需要动态导入的模块。 - 4、重写window.webpackJsonp数组的push方法为
webpackJsonCallback
,当执行webpackJsonp.push时,即通过webpackJsonCallback
来实现异步文件加载完成之后模块的安装。
3.2.3 异步模块实现原理
从index.js
打包结果来分析
-
index.js 通过编译之后,import函数实际通过调用__webpack_require__.e 并传入需要异步加载模块的chunkId(通过
jsonpScriptSrc
函数映射到实际文件加载路径),动态创建script,注册script onload事件,最后返回一个promise对象 -
待异步模块加载完成之后,执行刚加载完成的脚本, 而
window[“webpackJsonp”].push
实际指向的是webpackJsonpCallback 函数,完成以下三个事情:
- 收集模块,将所有“chunkIds”标记为已加载
- 将刚加载异步模块内容插入到主文件模块对象 modules 中,以便后续直接使用
- rosolve 更新上一步返回的promise对象的状态
- 然后通过
__webpack_require__
访问到更新之后主文件模块对象 modules,获取到异步模块,继续执行后续代码逻辑
当然可以通过魔法注释方式指定输出文件名等
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackExports: ["default", "named"] */
'./module-dynamic.js'
);
复制代码
此時,0.bundle.xxx.js
將被替换为 my-chunk-name.bundle.xxx.js
。
【四】webpack 如何处理非js文件
4.1 处理css
webpack 本身不具备处理除了js 以外的文件能力,css 文件需要通过插件、loader加载处理。有两种方案
- 将css直接打包进js。之后通过js 动态创建style标签插入页面
- 一般我们都不会把大段的css放进页面中,会将其放在独立的文件中,然后在我们的页面通过link中引用。
4.1.1 借助 style-loader 插入页面
安装依赖:style-loader css-loader
// 1、配置webpack .config.js
// 2、执行npm run build
// 3、查看打包结果 bunld.js
"./node_modules/css-loader/dist/cjs.js!./src/css/main.css":
(function(module, __webpack_exports__, __webpack_require__) {
// ... css-loader加载main.css处理细节(包含css代码中可能又引入其它css文件)
}),
"./node_modules/css-loader/dist/runtime/api.js":
(function(module, exports, __webpack_require__) {
// ... 省略 css-loader 运行时
}),
"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":
(function(module, exports, __webpack_require__) {
// ... 省略 style-loader 运行时
}),
"./src/css/main.css":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__); var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(\"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\"); var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__); var _node_modules_css_loader_dist_cjs_js_main_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(\"./node_modules/css-loader/dist/cjs.js!./src/css/main.css\"); var options = {};options.insert = \"head\";options.singleton = false;var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_main_css__WEBPACK_IMPORTED_MODULE_1__[\"default\"], options); __webpack_exports__[\"default\"] = (_node_modules_css_loader_dist_cjs_js_main_css__WEBPACK_IMPORTED_MODULE_1__[\"default\"].locals || {});");
})
// 4、分析打包结果 loader执行顺序自右向左
// 4.1 通过 css-loader 运行时api 加载main.css文件得到符合CommonJS 规范的对象
// 4.1 通过 style-loader 运行时api 将上一步得到css模块对象通过style标签动态插入html中
复制代码
- css-loader 的作用是将 css 转换成 commonjs 对象,也就是样式代码会被放到 js 里面去了。
- style-loader 插入样式是一个动态的过程,查看打包后的 html 源码并不会看到 html 有 style 样式的。style-loader 是代码运行时动态的创建 style 标签,然后将 css style 插入到 style 标签里面去
4.1.2 借助mini-css-extract-plugin 提取出单独的css文件
安装依赖:mini-css-extract-plugin css-loader
// 1、 配置webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"], // 从右向左解析原则
}
]
},
plugins: [
new MiniCssExtractPlugin({
// 这里的配置和webpackOptions.output中的配置相似
// 即可以通过在名字前加路径,来决定打包后的文件存在的路径
filename: "css/[name].[hash].css",
chunkFilename: "css/[id].[hash].css",
})
]
}
// 2、index.js 引入main.css
import './main.css'
// 3、执行npm run build 分析打包文件
// 3.1 打包结果中增加一个 css/main.xxx.css 文件
// 3.2 index.html 头部通过link链接该路径下的css文件
复制代码
4.2 处理图片、字体
wepack处理图片、字体方案是通过url-loader
,file-loader
完成模块化转换的。大致原理如下:
借助url-loader
,通过修改 webpack 配置让小于 10k 的图片或者字体文件在构建阶段自动转化为base64。如果图片较多,会发很多http请求,会降低页面性能。url-loader
会将引入的图片编码,生成dataURl。相当于把图片数据翻译成一串字符。再把这串字符打包到文件中,最终只需要引入这个文件就能访问图片了。当然,如果图片较大,编码会消耗性能。因此url-loader提供了一个limit参数,小于limit字节的文件会被转为DataURl,大于limit的还会使用file-loader
进行copy。
4.2.1 使用url-loader转base64编码
"./src/images/不你没懂.png":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"data:image/png;base64,......省略base64编码\");\n)})
复制代码
4.2.1 使用file-loader复制一份
file-loader
本身并不会对文件内容进行任何转换,只是复制一份文件内容,并根据配置为他生成一个唯一的文件名。大致输出结果如下:
"./src/images/不你没懂.png":
(function(module, __webpack_exports__, __webpack_require__) {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (__webpack_require__.p + \"img/不你没懂.adb0f5d9.png\");\n\n//# sourceURL=webpack:///./src/images/%E4%B8%8D%E4%BD%A0%E6%B2%A1%E6%87%82.png?");
})
复制代码
通过与__webpack_require__.p
拼接访问到实际文件路径
【五】补充——引入非模块
上面介绍了 webpack 打包视一切资源为模块的情况,但实际使用场景中,仍有一些不支持模块化规范的模块通过全局变量来使用,但我们希望它可以作为模块内容导出。
5.1 举个例子
// hello.js
var Hello = window.Hello = function(){
console.log('hello from global Hello() function');
};
// index.js
var Hello = require('./hello.js');
Hello();
复制代码
打包执行之后可以看到,由于hello.js
并没有导出模块,导致入口文件index.js
在引用的时候报错,找不到Hello这个函数
5.2 使用exports-loader
将某些全局变量作为模块内容导出
npm install exports-loader@1 --save-dev
import Hello from "exports-loader?exports=Hello!./hello.js";
复制代码
5.3 查看打包结果
"./node_modules/exports-loader/dist/cjs.js?exports=Hello!./src/hello.js":
(function(module, __webpack_exports__, __webpack_require__) {
//.. 省略部分打包内容
__webpack_require__.d(__webpack_exports__, \"Hello\", function() { return Hello; }
})
复制代码
【六】 输出指定模块化规范
webpack 提供了这几个字段来满足不同模块化方案输出
output.library
: 指定导出的模块名。支持string或者object,它的值被如何使用会根据output.libraryTarget的取值不同而不同output.libraryTarget
: 支持string,作用是控制 webpack 打包的内容是如何暴露的,默认为var
暴露方式 | 值 |
---|---|
暴露一个变量 | var、assign |
通过对象属性暴露 | this、window、global、commonjs |
模块定义系统 | commonjs2、amd、umd |
output.libraryExport
: 指定模块的某个属性作为导出对象