jyzwf / blog

在Issues里记录技术得点滴
17 stars 3 forks source link

webpack打包后代码浅析2 #44

Open jyzwf opened 6 years ago

jyzwf commented 6 years ago

继续上文webpack打包后代码浅析1,现在我们对 show.js 作如下修改:

    // 原来的:
    /* function show(content) {
        window.document.getElementById('app').innerHTML = content
    };

    export default  */

    export function show(content) {
        window.document.getElementById('app').innerHTML = content
    };

    export function getAge(age) {
        console.log(age)
    }

    export var fileName = 'show'

现在我们是用 export直接暴露出两个方法和一个变量,我们来看看打包后的文件发生了啥变化:

(function (modules) {
    // 这一部分没有变化
})([
    // 数组的第一个元素也没有变
    (function (module, exports, __webpack_require__) {
        const show = __webpack_require__(1)
        show('webpack hello 77')
    }),

    (function (module, __webpack_exports__, __webpack_require__) {
        "use strict";

        Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
        /* harmony export (immutable) */ __webpack_exports__["show"] = show;
        /* harmony export (immutable) */ __webpack_exports__["getAge"] = getAge;
        /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fileName", function() { return fileName; });

        // 最终的 __webpack_exports__(也就是exports) 变为了如下:
        /* 
        __webpack_exports__={
            __esModule:true,
            show,
            getAge,
            fileName
        }
        */

        function show(content) {
            window.document.getElementById('app').innerHTML = content
        };

        function getAge() {
            console.log(age)
        }

        var fileName = 'show'
    })
])

可以看到,变的是数组第二个元素,这里把原来传进来的第二个参数 exports 转为了 __webpack_exports__

这里我们注意到,对于暴露一个变量来说,他是直接调用 __webpack_require__.d 方法,并在 __webpack_exports__ 上定义属性,那么为什么不直接像定义方法一样定义 变量呢? 来到 __webpack_require__.d 函数,他是先做了一个 if 判断,!__webpack_require__.o(exports, name) ,判断该属性是否已经存在,,如果不存在就进行定义,这就有个问题,难道不能重复定义覆盖吗?我们做下测试:

// show.js 改为如下:
export function show(content) {
    window.document.getElementById('app').innerHTML = content
};

export function getAge() {
    console.log(age)
}

export var fileName = 'show1'

export var fileName = 'show2'

果然,打包的时候出现了一个错误: image 同理,重复暴露方法也会报错: image

这里我还有一个疑问,就是为什么变量不能像方法一样,直接用 点的方式挂到 exports 上,而是使用defineProperty呢?

后面我又对 as 的作用做了测试:

// show.js
export function show(content) {
    window.document.getElementById('app').innerHTML = content
};

function getAge(age) {
    console.log(age)
}

var fileName1 = 'show1'

export {
    fileName1 as f1,
    getAge as age
}

然后打包,发现基本没变,就只改了两行: image

继续,我们试试 export default

// show.js
function getAge(age) {
    console.log(age)
}

export default getAge

image

前面我们都是将 exportmodule.exportsexport default分开来用,现在我们把他们结合起来用试试

// show.js
function getAge(age) {
    console.log(age)
}

export function show() {
    console.log(55)
}

export var n = 'n'

export default {
    getAge
}

image

这样子用没啥区别,如果既 exportexport default

// show.js
function getAge(age) {
    console.log(age)
}

export function show() {
    console.log(55)
}

export var n = 'n'

export default {
    getAge,
    n
}

image 好吧,他会两个地方都来一个, image

最后我们加上 module.exports,看看有啥刺激的

module.exports = function getAge(age) {
    console.log(age)
}

export function show() {
    console.log(55)
}

var n = 'n'
export default {
    n
}

image image

导出的结果: image

呦呵,确实我们不一样,不一样,还有,我的 getAge呢,来分析一波:

// __webpack_require__(1)
function (module, __webpack_exports__, __webpack_require__) {

    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", {
        value: true
    });
    /* WEBPACK VAR INJECTION */

    (function (module) { /* harmony export (immutable) */
        __webpack_exports__["show"] = show;
        module.exports = function getAge(age) {
            console.log(age)
        }

        function show() {
            console.log(55)
        }

        var n = 'n'
        __webpack_exports__["default"] = ({
            n
        });
    }.call(__webpack_exports__, __webpack_require__(2)(module)))
}

// __webpack_require__(2)
function (module, exports) {

    module.exports = function (originalModule) {
        if (!originalModule.webpackPolyfill) {
            // 以originalModule为原型创建一个新对象
            var module = Object.create(originalModule);
            // module.parent = undefined by default
            if (!module.children) module.children = [];
            Object.defineProperty(module, "loaded", {
                enumerable: true,
                get: function () {
                    return module.l; 这里是 originalModule 的 l
                }
            });
            Object.defineProperty(module, "id", {
                enumerable: true,
                get: function () {
                    return module.i;    // 这里是 originalModule 的 i
                }
            });
            Object.defineProperty(module, "exports", {
                enumerable: true,
            });
            module.webpackPolyfill = 1;
        }

        return module;
    };
}

上面,在 .call(__webpack_exports__, __webpack_require__(2)(module)) 时,先执行 __webpack_require__(2),执行后返回的 module.exports 是一个函数,接受一个参数,在这里就是 __webpack_require__(1)模块,并返回一个新模块对象: image

那为什么会报错呢?因为 writable 当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。所以报错了,我们只要作如下改动: image 结果就出来了,但是依旧没有 getAge 方法 image

所以尽量不要这么用

突然看了下上面的,还有一种单独使用 module.exports的情形,我们看看:

function getAge(age) {
    console.log(age)
}

function show() {
    console.log(55)
}

var n = 'n'

module.exports = {
    show,
    getAge,
    n
}

image image

好嘛,,简洁明了。

今天就到这里,我们还没有解决 浅析1 中的一些疑问,我们明天继续 ^_^