文章目录
1、分析编译结果的目的 ?
对webpack的编译结果
的认识对分析编译过程
有帮助,理解了编译过程对后面使用webpack的加载器和插件
的理解有帮助;
2、自己尝试手写dist/main.js
自己尝试手写dist/main.js
webpack的作用:根据入口文件./src/index.js 分析文件的依赖关系,然后把依赖的模块合并成一个文件
my-main.js :
//合并两个模块
// ./src/a.js
// ./src/index.js
(function (modules) {
var moduleExports = {}; //用于缓存模块的导出结果
//require函数相当于是运行一个模块,得到模块导出结果
function __webpack_require(moduleId) { //moduleId就是模块的路径
if (moduleExports[moduleId]) {
//检查是否有缓存
return moduleExports[moduleId];
}
var func = modules[moduleId]; //得到该模块对应的函数
var module = {
exports: {}
}
func(module, module.exports, __webpack_require); //运行模块
var result = module.exports; //得到模块导出的结果
moduleExports[moduleId] = result; //缓存起来
return result;
}
//执行入口模块
return __webpack_require("./src/index.js"); //require函数相当于是运行一个模块,得到模块导出结果
})({ //该对象保存了所有的模块,以及模块对应的代码
"./src/a.js": function (module, exports) {
eval("console.log(\"module a\")\nmodule.exports = \"a\";\n //# sourceURL=webpack:///./src/a.js")
},
"./src/index.js": function (module, exports, __webpack_require) {
eval("console.log(\"index module\")\nvar a = __webpack_require(\"./src/a.js\")\na.abc();\nconsole.log(a)\n //# sourceURL=webpack:///./src/index.js")
}
});
3、手写思路
合并./src/index.js
和 ./src/a.js
两个模块,合并的结果里就是原始的js代码
,不存在任何模块化的内容
---------------step1-----------------------------------------
1、避免全局变量污染,避免模块化代码(module,exports,require
)找不到
问题:
- 合并前的模块化代码(
module,exports,require
)怎么办?—传参 - 模块化的模块里不会有 全局变量污染,合并后怎么
确保也不会污染全局变量
? --模块里的内容放在函数里
方案:
我们可以如下,写成对象的模式
, 定义一个变量modules 是一个对象
,该对象保存了所有的模块,以及模块对应的代码
var modules = {
"./src/a.js ": function(module,exports,require){
console.log('a 模块');
module.exports = 'a';
},
"./src/index.js ": function(module,exports,require){
// var a = require('./a');
var a = require('./src/a.js'); //用统一的路径书写方式,require('./src/a.js)的时候,就找到对应的函数去执行
console.log(a);
console.log('index模块');
},
}
模块的路径
作为对象里的属性名
,因为每个模块的路径是唯一的,所以通过这个唯一的属性名就可以找到一个唯一的模块对象的属性值
可以写成一个函数,把模块里的代码放到函数环境
里,- 在函数里用到了
module
,在普通的js里是没有module的
,我们可以使用函数的参数 给传进来(commonjs里还可能用到exports.a =1;我们把exports
也传进来)
这样每一个模块里面的代码就没有污染全局变量!!!
总结:
因为每个模块对应一个唯一的路径
,所以我们把路径用作属性名
,属性值是函数(模块里的代码放在函数里)
./src/index.js 和 ./src/a.j
s 这两个模块,运行一个模块,就相当于运行一个函数,就是执行函数里的模块代码
---------------step2-----------------------------------------
2、对象modules交给立即执行函数来处理
问题3:那对象modules交给谁来处理呢? –给立即执行函数来处理
方案:我们可以写一个立即执行函数
,因为我们尽量的避免污染全局变量
,所以把模块modules给立即执行函数
,来处理里面的模块
var modules = {
"./src/a.js ": function(){ //...}
}
(function(modules){})(modules) //把modules 给立即执行函数,来执行来处理里面的所有模块
问题4:var modules = {} 然后传参给立即执行函数,会有全局变量污染
,
方案:为了不污染全局变量,直接把modules对应的内容,作为字面量传
给立即执行函数
(function(modules){ })({
"./src/a.js ": function(){ //...}
}) //把modules 给立即执行函数,来执行来处理里面的所有模块
这样就不会污染全局变量,同时又把模块构建好了,所有的模块对保存在对象里{模块的路径:模块的函数},然后把这个对象给立即执行函数
---------------step3-----------------------------------------
3、手写执行函数,执行入口模块
立即执行函数里面要执行入口模块 require('./src/index.js)
--require(’./src/a.js)就是找到./src/a.js
对应的函数去执行
require函数相当于运行一个模块,并得到模块的导出结果,这个require函数要自己写
(function(modules){
function webpack_require(moduleId){ //moduleId就是模块的路径,为了避免require 和node环境的require重名,改用webpack_require
var func = modules[moduleId]; //得到模块对应的函数
var module = {
exports:{
}
}
var exports = module.exports;
func(module,exports,webpack_require); //执行模块对应的函数,传入参数
var result = modules.exports; //得到模块导出的结果
return result;
}
//执行入口模块
return __webpack_require("./src/index.js"); //require函数相当于是运行一个模块,得到模块导出结果
})({
"./src/a.js ": function(){ //...}
}) //把modules 给立即执行函数,来执行来处理里面的所有模块
---------------step4-----------------------------------------
4、加载模块缓存问题
问题:加载模块缓存问题?就是一个模块加载了多次怎么办?
(function(modules){
var moduleExports = {
// "./src/a.js":'a' //这种方式缓存结果
};//1-----------------用于缓存模块的导出结果
function webpack_require(moduleId){ //moduleId就是模块的路径,为了避免require 和node环境的require重名,改用webpack_require
if(moduleExports[moduleId]){
//2-------------- 检查是否有缓存
return moduleExports[moduleId];
}
//3-----------如果没有
var func = modules[modules]; //得到模块对应的函数
var module = {
exports:{
}
}
var exports = module.exports;
func(module,exports,webpack_require); //执行模块对应的函数,传入参数
var result = modules.exports; //得到模块导出的结果
moduleExports[moduleId] = result; //4-----------缓存结果
return result;
}
//执行入口模块
return __webpack_require("./src/index.js"); //require函数相当于是运行一个模块,得到模块导出结果
})({
"./src/a.js ": function(module,exports,require){
console.log('a 模块');
module.exports = 'a';
},
"./src/index.js ": function(module,exports,require){
var a = require('./src/a.js');
console.log(a);
console.log('index模块');
},
})
---------------step5-----------------------------------------
5、模块里的代码放到eval()里,方便调试
问题:为什么模块合并后,把模块里的代码放到对应函数里的eval()
里面?
这和浏览器有关,最终运行的是合并后的代码,如果报错了只能指示到打包文件里,为了更好的调试,查找报错位置,放在eval()里。
eval()里的代码是放到另一个环境执行的,如果报错,就看不到其他代码的干扰了
"./src/a.js": function (module, exports) {
eval("console.log(\"module a\")\nmodule.exports = \"a\";\n //# sourceURL=webpack:///./src/a.js")
},
//# sourceURL= webpack:///./src/a.js
告诉浏览器触发调试的时候,显示的错误位置路径是./src/a.js