欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

webpack笔记( 四 )- 编译结果分析

程序员文章站 2022-03-22 10:26:38
我们每一次用webpack打包以后都会生成一个dist文件夹, 下面有一个main.js文件,我们是时候关注一下webpack的编译结果main.js都写了什么我们不看main.js到底写了什么, 我们在src目录下新建一个a.js, b.js和index.js, 各自书写代码如下// a.jsconsole.log('i am a.js');module.exports = { a: 10, b: 20}// index.jsconsole.log('i am inde....

我们每一次用webpack打包以后都会生成一个dist文件夹, 下面有一个main.js文件,我们是时候关注一下webpack的编译结果main.js都写了什么

我们不看main.js到底写了什么, 我们在src目录下新建一个a.js, b.js和index.js, 各自书写代码如下

// a.js
console.log('i am a.js');
module.exports = {
    a: 10,
    b: 20
}
// index.js
console.log('i am index.js');
const result = require('./a');
console.log(result.b);

然后我们想一想, 我们能不能自己写一个dist/myMain.js出来然后实现跟webpack一样的功能呢? 然后咱再去看看webpack是怎么写的, 就很nice

OK, 咱在src下新建一个dist目录, 创建一个myMain.js, webpack做了什么呢, 他根据入口文件分析出入口文件与其他文件之间的依赖关系, 然后将这些文件合并成一个文件, 我们的入口文件就是index.js( 默认就会找index.js ), 而index.js依赖了a.js

// main.js

// 所以我们是要合并./src/index.js 和 ./src/a.js模块, 合并的结果里就是一个普普通通的JS代码, 不再存在任何的模块化代码

// 我们将模块都放进一个对象里这么来写

const modules = {

    './src/a.js': (module, exports, require) => {
        console.log('i am a.js');
        module.exports = {
            a: 10,
            b: 20
        }
    },

    './src/index.js': (module, exports, require) => {
        console.log('i am index.js');
        const result = require('./src/a.js'); // 引入进来的时候这个src路径会被webpack用工具替换一下, 我们就手工修改了
        console.log(result.b);
    }
}

因为webpack会用一个工具方法来找到所有通过requireimport等关键字导入导出的代码, 从而找到他们依赖的模块, 最终将这些模块放在一个对象里, 对象的key为模块的路径名, 对象value为模块文件中的JS代码, 为甚要这样写?

  1. 路径作为key值非常的合适他一定是唯一的, 一个路径对应一个js文件代码, 我们用数组也好, 用其他数据结构也好都不太合适, 当然ES6的新数据结构还是可以做到, 但是对象这么普遍可读性可维护性高啊

  2. 你知道的, 模块化代码最大的优势之一就是代码不会影响任何外部变量, 我们将每个模块化的代码锁死在一个函数里, 不同的函数作用域中变量互相独立互不影响, 这就是为什么模块文件的代码要写进一个函数的原因

  3. 我们必须将module, exports, 和require传入给每个模块函数, 当然这些由我们自己构造, 如果你考虑ES6和其他模块化规范的话, 也是要处理其他规范的, 我们这里只处理commonjs规范了

我们这个对象现在也暴露在了全局下, 就显得非常的不安全, 所以大家能够想到的最常见的就是用立即执行函数封闭作用域

(function() {

    var modules = {
        ...
    }

}())

上面这样写可以吗? 很显然是不可以的, 这个modules对象在每一次打包的时候都是不同的, 怎么能够包在里面呢? 但是有同学说, 不包在里面怎么办鸭, 那不是没办法封闭了, 你看我给你这样写

(function(modules) {

    // 这里面是不是就可以使用modules了


}({ 
    // 咱给他直接通过对象的形式写道这
   './src/a.js': (module, exports = module.exports, require) => {
        console.log('i am a.js');
        module.exports = {
            a: 10,
            b: 20
        }
    },


    './src/index.js': (module, exports = module.exports, require) => {
        console.log('i am index.js');
        const result = require('./a');
        console.log(result.b);
    } 
}))

OK, 我们在这个立即执行函数里, 必须要执行入口函数, 这个没什么好说的吧, 那怎么执行呢? 直接通过require不就执行了吗, 所以我们必须要先提供require方法出来, 然后执行index.js

(function(modules) {

    // 运行一个模块, 得到模块的导出结果, moduleId: 就是每一个函数的path
    function require(moduleId) {
        // 通过moduleId我们是一定要去传入进来的modules里面找到对应的函数执行的, 这些函数必须要接收三个参数, 我们需要构造给他们
        let module = {
            exports: {

            }
        };

       modules[moduleId](module, module.exports, require); // 这个函数一走完, module.exports就有了值了就是模块的导出结果

       return module.exports; // 最后将module.exports丢出去
    }

    require('./src/index.js');

}({
    ...
}))

我们还知道, webpack是会对模块进行缓存的, 多次引入同一个模块其实都是引入的第一个, 所以我们也要处理一下

(function(modules) {

    const moduleCache = {}; // 模块缓存池

    function require(moduleId) {

        //这里在require的时候要先看一下moduleCache中有无数据
        if(moduleCache[moduleId]) {
            return moduleCache[moduleId];
        }

       let module = {...};

       modules[moduleId](module, module.exports, require); 

       // 我们在这里要加一行代码
       moduleCache[moduleId] = module.exports;

       return module.exports; 
    }

    require('./src/index.js');

}({
    ...
}))

这个时候我们的myMain.js就写完了, 我们来执行以下这个js看看效果是不是如我们所愿

webpack笔记( 四 )- 编译结果分析

我们会发现确实执行结果是按照我们预期的执行了, 所以基本上webpack内部编译以后形成的编译结果也就是我们这样了。 这就是webpack的编译结果分析, 希望可以对你后续的学习产生帮助

本文地址:https://blog.csdn.net/weixin_44238796/article/details/107505123

相关标签: Webpack javascript