If you can understand how the jQuery library handles installation and use in different environments, can you also understand how other libraries run in different environments

Install jquery in WebPack, open node_module, and you can see the structure of the file. SRC is the source file of each module. After packaging and compilation, the final code is put in DIST

Different environment import processing

Jquery source code is ultimately provided to a variety of different environments to use, front-end code use mainly have the following environment

  1. Webpack environment
    • Support Commonjs/Es6Module specification
    • This environment also exists because it is packaged and run in a browser window globally-variable
  2. Run in a pure Node environment
    • Support only Commonjs specification, not ES6module specification
    • Global variable None window (In the global scope, this Is a global variable global
  3. Run directly in the browser environment (or webView, etc.) (based on <script src='xxx'></script> The form of
    • Commonjs is not supported and is used under newer browsers <script type='module'></script> , ES6Module can be supported. Otherwise, ES6Module is not supported
    • The global variables have window

The following from the closure and aspect object thought to see jquery source code, mainly for their own future can be wrapped plug-in components

Environment judgment and import

Pre-knowledge:

Little tip about typeof

Small tip1

Typeof returns undefined if a variable is declared but not assigned a value. Typeof returns undefined if a variable is not declared

Small tip2

  • CommonJs exported modules can be imported using CommonJs or ES6Module
  • However, modules exported based on the ES6Module specification can only be imported using the ES6Module specification

What does the original immediate function do?

Through the closure mechanism, directly

  1. 支那 return * * orexport factoryThe result of the function execution
  2. orDirect execution factory(), execute the function

Return results and direct execution are determined by whether the incoming Gobal + supports the ES6Module or Commonjs module specifications

  1. gobalIndicates whether it exists in the global environment window Variables, if there are,gobalis window If notgobalRepresents the global environmentthis
  2. Whether the ES6Module module specification or Commonjs module specification is supported

Use the above two aspects to distinguish between the above different environments (Node, browser, Webpack), and finally return the factory function result or execute the factory function regardless of the environment

Here are the detailed methods for determining the different environments, and the rules are in the comments

* + browser OR Hybrid -> webView * + NODE * + webpack * +... * /
(function (global, factory) {
    /* Global :typeof window! == "undefined" ? Window: this 1. Browser environment: window 2. Webpack environment: Window 3
    "use strict";
    if (typeof module= = ="object" && typeof module.exports === "object") {
        // Support CommonJS module import/export specification "NODE environment or WEBPACK environment"
        // global.document: WEBPACK environment "global->window"
        module.exports = global.document ?
        	// In the webpack environment, the factory function is executed directly, and noGlobal is true(to distinguish webpack imports from Script imports).
            factory(global.true) :
            // In other cases where there is no Window (node, etc.), we export a function and execute the function later, so that if we can pass in a window, it will work, otherwise an error will be reported
            function (w) {
                if(! w.document) {throw new Error("jQuery requires a window with a document");
                }
                return factory(w);
            };
    } else {
        // The jquery is mounted to the browser window, so there is no need to return
        //  + global:window
        factory(global);
    }
})(typeof window! = ="undefined" ? window : this.function factory(window, noGlobal) {
    "use strict";
    var version = "3.5.1 track of",
        jQuery = function (selector, context) {
           / /...
        };
	/ /...
    return jQuery;
});
Copy the code

This layer of contextual logic can be added to support the Commonjs project

The factory function takes different environments to execute

With the window, noGlobal parameter passed in, it can be used to execute in different environments

  1. noGlobalforundefinedIf you want to mount jQuery from SCRIPT, you need to mount jQuery from SCRIPTwindowon
  2. noGlobalfortrueThe CommonJS/ES6Module specification in WEBPACK is used to import jQuery objects

The detailed code is explained as follows:


(function (global, factory) {
   ...
})(typeof window! = ="undefined" ? window : this.function factory(window, noGlobal) {
    // Direct SCRIPT import
    // + window:window noGlobal:undefined
    // Import JQ based on CommonJS/ES6Module specification in WEBPACK
    // + window:window noGlobal:true
    "use strict";
    var version = "3.5.1 track of",
        jQuery = function (selector, context) {
            return newjQuery.fn.init(selector, context); }; .// To support AMD modules
    if (typeof define === "function" && define.amd) {
		define("jquery"[],function () {
			return jQuery;
		});
	}
    
    // Import JQ "execution code" && before it is exposed globally
    var _$ = window. $; jQuery.noConflict =function () {
        if (window.$ === jQuery) {
            window. $= _ $; }return jQuery;
    };

    // If you are in a browser environment, you can mount jquery directly on the window.
    $() or jQuery() -> are all internal jQuery methods
    if (typeof noGlobal === "undefined") {
        window.jQuery = window.$ = jQuery;
    }

    Module. exports=jQuery; // module.exports=jQuery; (Based on WEBPACK processing)
    Import $from 'jquery'
    return jQuery;
});
Copy the code

So that’s using closures to keep things private, to do environment differentiation, to make them available to the outside world, and the rest of the core logic and method encapsulation is all about objects

Solve the problem of coexistence of multiple libraries

JQuery imported by the Commonjs method does not have this problem because $is not mounted to the window

If $usage conflicts (e.g. Zepto also has $)

The Zepto library handles this: if $is occupied, it is no longer used

Jquery transfers $uselage in case of conflict. To put the original library existing $aside temporarily, temporary private variable _ $above, if you execute the jQuery. Of noConflict method, will return the other right to use the library before, before $who had back in use. Otherwise, it overwrites other libraries directly. You can make a new name for yourself

// Import JQ "execution code" && before it is exposed globally
    var _$ = window. $; jQuery.noConflict =function () {
        if (window.$ === jQuery) {
            window. $= _ $; }return jQuery;
    };
Copy the code

// Map over jQuery in case of overwrite
  var  _jQuery = window.jQuery,

// Map over the $ in case of overwrite
     _$ = window. $; jQuery.noConflict =function (deep) {
if (window.$ === jQuery) {
        window. $= _ $; }if (deep && window.jQuery === jQuery) {
        window.jQuery = _jQuery;
}

return jQuery;
};
Copy the code

Depth transfer, transfer the jQuery name as well

Encapsulate a library of your own

We use the methods used above, including the principle of exposing apis for different environments and co-existing libraries. To write a library for JSONP

(function () {
    /* Core method */
    function jsonp(config) {
        config == null ? config = {} : null;
        typeofconfig ! = ="object" ? config = {} : null;
        let {
            url,
            params = {},
            jsonpName = 'callback',
            success = Function.prototype
        } = config;

        // create a global function yourself
        let f_name =   `jsonpThe ${+new Date()}`  ;
        window[f_name] = function (result) {
            typeof success === "function" ? success(result) : null;
            delete window[f_name];
            document.body.removeChild(script);
        };

        / / processing the URL
        params = Qs.stringify(params);
        if (params) url +=   `${url.includes('? ')?'&':'? '}${params}`  ;
        url +=   `${url.includes('? ')?'&':'? '}${jsonpName}=${f_name}`  ;

        // Send the request
        let script = document.createElement('script');
        script.src = url;
        document.body.appendChild(script);
    }

    /* Multiple libraries coexist */
    let _$ = window.$,
        _jsonp = window.jsonp;
    jsonp.noConflict = function noConflict(deep) {
        if (window.$ === jsonp) {
            window. $= _ $; }if (deep && window.jsonp === jsonp) {
            window.jsonp = _jsonp;
        }
        return jsonp;
    };

    /* Expose API */
    if (typeof window! = ="undefined") {
        window.jsonp = window.$ = jsonp;
    }
    if (typeof module= = ="object" && typeof module.exports === "object") {
        module.exports = jsonp;
    }
})();

Copy the code

Note that the above will rely on a QS packageUse:

<script src="node_modules/qs/dist/qs.js"></script>
<script src="jsonp.js"></script>
<script>
    jsonp({
        url: 'https://www.baidu.com/sugrec'.params: {
            prod: 'pc'.wd: 'test the json'
        },
        jsonpName: 'cb'.success: result= > {
            console.log(result); }});</script>
Copy the code

Later in the series, the rest of the source code will be interpreted from an object-oriented perspective