Symbol.toStringTag
Symbol.toStringTag可以定义Object.prototype.toString.call之后[object Xxx]中Xxx部分的现实值
let obj1 = {};
Object.defineProperty(obj1, Symbol.toStringTag, { value: 'Obj1' });
let obj2 = {};
Object.defineProperty(obj2, Symbol.toStringTag, { value: 'Obj2' });
console.log(Object.prototype.toString.call(obj1));//[object Obj1]
console.log(Object.prototype.toString.call(obj2));//[object Obj2]
复制代码
打包后的代码模块规范都是commonjs
key是模块的id,无论什么模块,模块id都是相对于项目根目录的相对路径
源文件无论采用哪种模块规范,在打包后文件中都是common.js
(() => {
var modules = ({
"./src/tilte.js":
((module) => {
module.exports = 'title';
})
});
var cache = {};
function require(moduleId) {
var cachedModule = cache[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
var exports = {};
(() => {
let title = require("./src/tilte.js");
console.log(title);
})();
})()
复制代码
不同模块规范间互相加载的规范
可以通过代码中模块引入和导出的关键字来确定是什么规范:
COMMONJS:
require exports module
ES MODULE:
export import
commonJs加载es模块:
index.js
let title = require('./title')
console.log(title)
console.log(title.age)
复制代码
title.js:
export default 'title_name';
export const age = 'title_age';
复制代码
在require.r方法中会对模块的exports添加一些特殊属性,例如下面的Symbol.toStringTag和__esModule:
var modules = ({
"./src/title.js":
((module, exports, require) => {
//ES 模块转换成COMM之后有哪些不一样的地方?
//exports.__esModule=true 你可以通过它来判断转换前是不是ES 模块
require.r(exports);
//ES 模块默认导出export default 会挂载到exports.default上,age会挂载到exports.age上
require.d(exports, {
"default": () => (_DEFAULT_EXPORT__),
"age": () => (age)
});
const _DEFAULT_EXPORT__ = ('title_name');
const age = 'title_age';
})
});
var cache = {};
function require(moduleId) {
var cachedModule = cache[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
require.d = (exports, definition) => {
for (var key in definition) {
if (require.o(definition, key) && !require.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
require.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
require.r = (exports) => {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
var exports = {};
let title = require("./src/title.js");
console.log(title);
console.log(title.age);
复制代码
可以理解为commonjs将es模块转换成了commonjs模块
commonjs esmodule 导出值的区别
commonjs值导出
esmodule引用导出
var modules = {
'./src/title.js': (module, exports, require) => {
//一旦webpack检测到你的代码里有export 和import关键字,它就认为这是一个ES Module
require.r(exports);
require.d(exports, {
default: () => DEFAULT_EXPORT,
age: () => age
});
var DEFAULT_EXPORT = 'title_name';
var age = 'title_age';
setTimeout(() => {
age = 'new'
}, 1000);
},
"./src/common.js":
((module, exports, require) => {
var age = { x: 1 }
exports.age = age;
setTimeout(() => {
//age = { x: 2 }
age.x = 10;
}, 1000);
})
}
let title = require("./src/common.js");
setTimeout(() => {
console.log(title.age);
}, 3000);
let title = require("./src/title.js");
setTimeout(() => {
console.log(title.age);
}, 3000);
复制代码
es模块加载commonjs模块:
index.js:
import title from './title'
console.log(title.name)
console.log(title.age)
复制代码
title.js:
module.exports = {
name: 'aaa',
age: 12
}
复制代码
打包后代码:
var modules = ({
"./src/title.js":
((module, exports, require) => {
exports.name = 'title_name';
exports.age = 'title_age';
})
});
var cache = {};
function require(moduleId) {
var cachedModule = cache[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
require.n = (module) => {
var getter = module && module.__esModule ?
() => module['default'] :
() => module
require.d(getter, {a: getter})
return getter
};
require.d = (exports, definition) => {
for (var key in definition) {
if (require.o(definition, key) && !require.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
require.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
require.r = (exports) => {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
var exports = {};
(() => {
"use strict;"
require.r(exports);
// 这里require的模块,不知道是esmodule,还是commjs
// 所以要想取得默认导出,要加判断,这个判断就在require.n里面
var _title_0__ = require("./src/title.js");
var _title_0__default = /*#__PURE__*/require.n(_title_0__);
console.log(_title_0__default().name);
console.log(_title_0__default().age);
})();
复制代码
require.d在这里暂时没有用处
异步加载
index.js:
const load = document.getElementById('load');
load.addEventListener('click', () => {
import(/* webpackChunkName: 'title' */'./title').then(result => {
console.log(result.default);
});
});
复制代码
title.js:
module.exports = 'title';
复制代码
index.js被转换成:
const load = document.getElementById('load');
load.addEventListener('click', () => {
//懒加载或者说动态加载title这个代码块,返回一个promise,然后再加载此模块,得到模块exports,然后打印就可以了
//代码的主是一块代码,每个代码块里面会有若干个模块
//通过 require.e("title")动态加载title代码块,通过 jsonp加载title.main.js文件,取得对应的代码块,
//然后把代码块里的模块定义合并到当前文件的modules里
//然后通过require加载"./src/title.js"模块,得到返回值,不管原来是comonjs还是es,都会转成es
require.e("title").then(() => require("./src/title.js")).then(result => {
console.log(result);
});
});
复制代码
简化后的代码:
//模块定义空的
var modules = ({});
var cache = {};
function require(moduleId) {
var cachedModule = cache[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
//chunkId title ,promises=[]
//已经安装好的或者说加载好的代码块
//key代码块的名字,入口默认就是main
//值0表示已经就绪 加载完成
var installedChunks = {
main: 0,
//title: [resolve, reject,promise]
//title: 0
}
require.l = (url) => {
var script = document.createElement('script');
script.src = url;
document.head.appendChild(script);
}
require.e = (chunkId) => {
let installedChunkData;
let promise = new Promise((resolve, reject) => {
installedChunkData = installedChunks[chunkId] = [resolve, reject]
});
installedChunkData[2] = promise
var url = chunkId + '.main.js'
require.l(url);
return promise;
}
var webpackJsonpCallback = (data) => {
let [chunkIds, moreModules] = data;
//把返回的模块定义合并到当前的模块定义对象modules里
for (let moduleId in moreModules) {
modules[moduleId] = moreModules[moduleId];
}
for (let i = 0; i < chunkIds.length; i++) {
let chunkId = chunkIds[i];
let resolve = installedChunks[chunkId][0];
installedChunks[chunkId] = 0;
resolve();//调用promise的resolve方法让promise成功
}
}
var chunkLoadingGlobal = window["webpackChunk_2_bundle"] = [];
chunkLoadingGlobal.push = webpackJsonpCallback;
const load = document.getElementById('load');
load.addEventListener('click', () => {
//懒加载或者说动态加载title这个代码块,返回一个promise,然后再加载此模块,得到模块exports,然后打印就可以了
//代码的主是一块代码,每个代码块里面会有若干个模块
//通过 require.e("title")动态加载title代码块,通过 jsonp加载title.main.js文件,取得对应的代码块,
//然后把代码块里的模块定义合并到当前文件的modules里
//然后通过require加载"./src/title.js"模块,得到返回值,不管原来是comonjs还是es,都会转成es
require.e("title").then(() => require("./src/title.js")).then(result => {
console.log(result);
});
});
复制代码
打包后title被单独打包了出来
执行过程:
1、按钮点击时,执行require.e(‘title’),这个方法会给这个模块创建一个promise,存到全局变量installedChunks中,存储结构为kv键值对:
title: [resolve, reject, promise]
key是模块的名称,值是这个模块promise相关的一些方法或对象
2、然后通过require.l方法加载,这个方法就是向页面插入script,script执行时,会向window[“webpackChunk_2_bundle”]中添加这个异步模块的相关信息,window[“webpackChunk_2_bundle”]是一个全局数组,它的push方法被改写过
var chunkLoadingGlobal = window["webpackChunk_2_bundle"] = [];
chunkLoadingGlobal.push = webpackJsonpCallback;
复制代码
因此到此会继续执行webpackJsonpCallback方法
3、在webpackJsonpCallback方法中,是可以拿到模块的名称和具体代码的,然后通过这个名称可以找到这个模块在installedChunks中对应的promise相关的方法,执行该模块的resolve方法,并将其在installedChunks中的标志改为0,代表已经加载过
4、接下来,就会执行上述promise订阅的方法:
() => require("./src/title.js")
复制代码
5、通过require方法中modules[moduleId](module, module.exports, require)的执行,模块内代码开始执行,exports将作为resolve的参数,title就可以console打印出来了
完整代码:
(() => {
var modules = ({});
var cache = {};
function require(moduleId) {
var cachedModule = cache[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = cache[moduleId] = {
exports: {}
};
modules[moduleId](module, module.exports, require);
return module.exports;
}
require.m = modules;
(() => {
var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);
var leafPrototypes;
require.t = function (value, mode) {
if (mode & 1) value = this(value);
if (mode & 8) return value;
if (typeof value === 'object' && value) {
if ((mode & 4) && value.__esModule) return value;
if ((mode & 16) && typeof value.then === 'function') return value;
}
var ns = Object.create(null);
require.r(ns);
var def = {};
leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];
for (var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) {
Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));
}
def['default'] = () => (value);
require.d(ns, def);
return ns;
};
})();
(() => {
require.d = (exports, definition) => {
for (var key in definition) {
if (require.o(definition, key) && !require.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
})();
(() => {
require.f = {};
require.e = (chunkId) => {
return Promise.all(Object.keys(require.f).reduce((promises, key) => {
require.f[key](chunkId, promises);
return promises;
}, []));
};
})();
(() => {
require.u = (chunkId) => {
return "" + chunkId + ".main.js";
};
})();
(() => {
require.g = (function () {
if (typeof globalThis === 'object') return globalThis;
try {
return this || new Function('return this')();
} catch (e) {
if (typeof window === 'object') return window;
}
})();
})();
(() => {
require.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
})();
(() => {
var inProgress = {};
var dataWebpackPrefix = "2.bundle:";
require.l = (url, done, key, chunkId) => {
if (inProgress[url]) { inProgress[url].push(done); return; }
var script, needAttach;
if (key !== undefined) {
var scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
var s = scripts[i];
if (s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; }
}
}
if (!script) {
needAttach = true;
script = document.createElement('script');
script.charset = 'utf-8';
script.timeout = 120;
if (require.nc) {
script.setAttribute("nonce", require.nc);
}
script.setAttribute("data-webpack", dataWebpackPrefix + key);
script.src = url;
}
inProgress[url] = [done];
var onScriptComplete = (prev, event) => {
script.onerror = script.onload = null;
clearTimeout(timeout);
var doneFns = inProgress[url];
delete inProgress[url];
script.parentNode && script.parentNode.removeChild(script);
doneFns && doneFns.forEach((fn) => (fn(event)));
if (prev) return prev(event);
}
;
var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);
script.onerror = onScriptComplete.bind(null, script.onerror);
script.onload = onScriptComplete.bind(null, script.onload);
needAttach && document.head.appendChild(script);
};
})();
(() => {
require.r = (exports) => {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
})();
(() => {
var scriptUrl;
if (require.g.importScripts) scriptUrl = require.g.location + "";
var document = require.g.document;
if (!scriptUrl && document) {
if (document.currentScript)
scriptUrl = document.currentScript.src
if (!scriptUrl) {
var scripts = document.getElementsByTagName("script");
if (scripts.length) scriptUrl = scripts[scripts.length - 1].src
}
}
if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/?.*$/, "").replace(//[^/]+$/, "/");
require.p = scriptUrl;
})();
(() => {
var installedChunks = {
"main": 0
};
require.f.j = (chunkId, promises) => {
var installedChunkData = require.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
if (installedChunkData !== 0) {
if (installedChunkData) {
promises.push(installedChunkData[2]);
} else {
if (true) {
var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));
promises.push(installedChunkData[2] = promise);
var url = require.p + require.u(chunkId);
var error = new Error();
var loadingEnded = (event) => {
if (require.o(installedChunks, chunkId)) {
installedChunkData = installedChunks[chunkId];
if (installedChunkData !== 0) installedChunks[chunkId] = undefined;
if (installedChunkData) {
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;
installedChunkData[1](error);
}
}
};
require.l(url, loadingEnded, "chunk-" + chunkId, chunkId);
} else installedChunks[chunkId] = 0;
}
}
};
var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
var [chunkIds, moreModules, runtime] = data;
var moduleId, chunkId, i = 0;
if (chunkIds.some((id) => (installedChunks[id] !== 0))) {
for (moduleId in moreModules) {
if (require.o(moreModules, moduleId)) {
require.m[moduleId] = moreModules[moduleId];
}
}
if (runtime) var result = runtime(require);
}
if (parentChunkLoadingFunction) parentChunkLoadingFunction(data);
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if (require.o(installedChunks, chunkId) && installedChunks[chunkId]) {
installedChunks[chunkId][0]();
}
installedChunks[chunkIds[i]] = 0;
}
}
var chunkLoadingGlobal = self["webpackChunk_2_bundle"] = self["webpackChunk_2_bundle"] || [];
chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));
})();
const load = document.getElementById('load');
load.addEventListener('click', () => {
require.e("title").then(require.t.bind(require, "./src/title.js", 23)).then(result => {
console.log(result.default);
});
});
})()
;
复制代码