在最终导出的dist文件中,最外层是自执行函数执行,IIFE两个形参global, factory,用于区分环境。 jquery导入相当于自执行函数执行,将内部所有内容挂载到window的某个属性上,形成一个闭包空间,用户可以通过window.$访问jQuery暴露的东西
基本骨架
监测环境
/**
* 检测环境:利用暂时性死区
* 如果当前环境中没有window,typeof检测不会报错,会返回undefined。
* 若当前环境中没有window(node环境),则global = this
* 若当前环境中有window(浏览器、webview、webpack环境),则global = window
*/
let params1 = typeof window !== 'undefined' ? window : this
/**
* jq核心代码
* commonJS的webpack环境:window = window,noGlobal = true
* 浏览器环境:window = window,noGlobal = undefined
*/
let params2 = function (window, noGlobal) { ... }
// =========================================环境区分===================================================
/**
* jquery环境区分
* 使用严格模式
*/
(function (global, factory){
"use strict"
/**
* && 检测当前环境是否支持CommonJS规范,同样利用了暂时性死区
* 若支持:则是node环境或webpack环境
* 若不支持:则是浏览器/webview环境
*/
if( typeof module === "object" && typeof module.exports === "object" ){
/**
* 支持commonJS规范
*/
module.exports = global.document ?
// 有document,则说明global是window,所以webpack环境
factory(global, true) :
// 无document, 则是node环境,导出代理函数
// 没window的情况下导出函数,函数也需要可以接收一个window,正常执行
// 纯node环境不支持jquery(node没必要操作DOM)
function (w){
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
}
}
else {
/**
* 不支持commonJS规范
* 浏览器环境,webview环境
* &&& params2回调函数执行
*/
factory( global )
}
})(params1, params2)
总结:
在自己写插件/组件时,需要让组件支持SCRIPT导入,commonJS/ES6Module,可以采用上方的方式区分。或在老版本的某些库进行重构,添加一层环境兼容。
(function (){
function xxx(){};
...
// 判断window环境
if(typeof window !== undefined) {
window.xxx = xxx;
}
// 判断commonJS规范
if(typeof module === 'object' && typeof module.exports === 'object') {
module.exports = xxx;
}
})()
jQ挂载
// 主函数
let params2 = function(window, noGlobal){
// 一些操作
...
/**
* 声明jQuery这个函数
*/
var jQuery = function (selector, context) {}
// 一些操作
...
//==========================================多库共存============================================
/**
* 解决全局属性名jQuery和$命名和别的类库冲突
* eg:Zepto向全局暴露的是$,jQuery向全局暴露的也是$,后写的$覆盖前面的$
* <script src = "js/Zepto.js"></script>
* <script src = "js/jQuery.js"></script>
* var _jQuery、_$ 保存挂载前的window.jQuery(= undefined) -->
* 和window.$(等于之前导入过模块的冲突$的代表或undefined)
* 声明转让$的函数noConflict
* 解决冲突:let jq = $.noConflict(),jq代表jquery,此时jq-->jQuery,$-->Zepto
*/
var
// 若引入不同版本jquery冲突,则此时获得的_jquery不再等于undefined,而是上一个引入的版本jquery
_jQuery = window.jQuery,
_$ = window.$;
// noConflict函数暴露给用户,让用户解决window上的命名冲突
jQuery.noConflict = function( deep ) {
if ( window.$ === jQuery ) {
// 调用noConflict方法,则jQuery一定占用了$,则把$归还给别人的$
// _$存储的是被覆盖的$存的内容
// 最后方法返回jquery,可以被用户以别的变量引用
window.$ = _$;
}
// 当一个产品导入不同版本的jquery时,不仅$冲突了,jquery也冲突了
//deep参数控制版本冲突时是否转让现有版本的jQuery名字
if ( deep && window.jQuery === jQuery ) {
//同上归还,
window.jQuery = _jQuery;
}
// 解决冲突时,自己起一个别名来代表jquery,特殊符不强占别人。let jq = $.noConflict()。
return jQuery;
};
//==========================================挂载============================================
/**
* 挂载jq到浏览器环境使用
*/
if ( typeof noGlobal === "undefined" ) {
window.jQuery = window.$ = jQuery;
}
/**
* amd环境使用
*/
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function() {
return jQuery;
});
}
/**
* & 挂载jq到commonJS使用
* module.exports = global.document ? factory(global, true) : function (w) {...}
* 即jquery.js文件的module.exports = factory(global, true)返回值 = jQuery
*/
return jQuery
}