由一個jQuery問題引發(fā)的一連串思考

在開發(fā)Electron程序時无切,在引入jQueryBootstrap后荡短,控制臺報錯:

Uncaught TypeError: Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.
    at Object.jQueryDetection (bootstrap.min.js:6)
    at bootstrap.min.js:6
    at bootstrap.min.js:6
    at bootstrap.min.js:6

而反復(fù)查看代碼沒發(fā)現(xiàn)問題丐枉,引用的順序沒有問題:

<script src="js/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>

后在網(wǎng)上找到了一些方法,解決了我的疑惑掘托。

方法一

在引入jQuery之前瘦锹,添加如下代碼:

<script>
    if (typeof module === 'object') {window.module = module; module = undefined;}
</script>

方法二

使用require方式引入jquery

window.$ = window.jQuery = require('./js/jquery.min.js')

雖然問題是解決了,但是是什么引發(fā)了這個問題呢闪盔?

經(jīng)過深入了解弯院,發(fā)現(xiàn)原來electron里面還是和瀏覽器有區(qū)別的。在electron里面使用的是CommonJS標(biāo)準(zhǔn)泪掀,這也是后端服務(wù)器采用的標(biāo)準(zhǔn)听绳,而在jQuery中有如下代碼:

if ( typeof module === "object" && typeof module.exports === "object" ) {
  // set jQuery in `module`
} else {
  // set jQuery in `window`
}

判斷如果在CommonJS中,jQuery就綁定到module下异赫,否者就綁定到window下椅挣,我們經(jīng)常使用的$就是在window的下。

Bootstrap是一個(首發(fā)共眾號)前端工具塔拳,它依賴jQuery鼠证,直接從window下有沒有jQuery/$來判斷有沒有jQuery,而在這里就行(正義的程序猿)不通了靠抑,因為jQuery是在module下的量九,所以就有了如上的兩種方法,個人更傾向于第二種方法。

有沒有其他方法荠列?类浪!

方法三

因為electron中集成了node相關(guān)的東西,可以把這個集成關(guān)閉或者把相關(guān)對象綁定到新的對象上:

const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false    // 重點是這個
    }
  })

上面這種方式弯予,window對象下就沒有module了戚宦。

當(dāng)然我們也可以把綁定的對象替換掉,這樣也能使用node相關(guān)的API

<head> 
<script> 
    window.nodeRequire = require; 
    delete window.require;
    delete window.exports;
  delete window.module; 
</script> 
<script type="text/javascript" src="jquery.js"></script> 
</head>

這只是一種解決思路锈嫩,不建議大家這么做受楼。

既然都談到CommonJS了,有必要進一步了解前端的幾個規(guī)范呼寸。

CommonJs艳汽,AMD和CMD

  • CommonJSnodejs也就是服務(wù)器端廣泛使用的模塊化機制,模塊必須通過module.exports 導(dǎo)出對外的變量或接口对雪,通過require() 來導(dǎo)入其他(首發(fā)公zhong號)模塊的(正義的程序猿河狐,歡迎關(guān)注)輸出到當(dāng)前模塊作用域中,此方式是同步的
  • AMDRequireJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出瑟捣,提前執(zhí)行(異步加載:依賴先執(zhí)行)+延遲執(zhí)行
  • CMDSeaJS 在推廣過程中對模塊定義的規(guī)范化產(chǎn)出馋艺,延遲執(zhí)行(運行到需加載,根據(jù)順序執(zhí)行)

CommonJS

CommonJS模塊加載是同步的迈套,后面的模塊必須等前面的加載完成才能執(zhí)行捐祠。

定義模塊:

// 模塊 a.js
const name = 'foo'

module.exports = {
    name,
    text: 'bar'
}

加載模塊:

// 模塊 b.js
// 引用核心模塊或者第三方包模塊,不需要寫完整路徑
const path = require('path');
// 引用自定義模塊可以省略.js
const { name, text } = require('./a');

console.log(name, text);
// 輸出 foo bar

CommonJS模塊的加載(首發(fā)公zhong號)機制是桑李,輸入的是被輸出的值的拷貝踱蛀。也就是說,一旦輸出一個值贵白,模塊內(nèi)部的變化就影響不到這個值率拒。(正義的程序猿)看如下例子:

// b.js
let age = 1;
setTimeout(() => {
  age = 18;
}, 10);
module.exports = {
  age
}
// a.js
const b = require('./b');
console.log(b.age);
setTimeout(() => {
  console.log(b.age);
  console.log(require('./b').age);
}, 100);

// 執(zhí)行:node a.js
// 執(zhí)行結(jié)果:
// 1
// 1
// 1

還有,CommonJS模塊重復(fù)引入的模塊并不會重復(fù)執(zhí)行禁荒,再次獲取模塊只會獲得之前獲取到的模塊的緩存猬膨。

AMD

全稱:Asynchronous Module Definition,中文翻譯:異步模塊定義呛伴,javascript原生不支持這種規(guī)范勃痴,使用AMD規(guī)范進行頁面開發(fā)需要用到對應(yīng)的庫函數(shù),鼎鼎大名的就是RequireJS磷蜀,

定義模塊:

// 獨立模塊
define(function(){
    ...
    return {
        //返回接口
    }
})

// 非獨立模塊
define(['foo','bar'],function(foo, bar){
    ...
    return {
        //返回接口
    }
})

獨立模塊即定義的模塊不依賴其他模塊召耘,非獨立模塊就是會依賴其他模塊。我們定義的模塊依賴foobar褐隆,第二個參數(shù)是一個函數(shù)污它,僅當(dāng)依賴加載成功后才會調(diào)用,函數(shù)的參數(shù)與前面的依賴數(shù)組一一對應(yīng),函數(shù)必須返回一個對象衫贬,給其他模塊調(diào)用德澈。

加載模塊:

AMD加載模塊也是用require,由于是異步的所以只能用回調(diào)函數(shù)的方式:

require(['foo','bar'], function(foo,bar){
    ...
})

回調(diào)函數(shù)中才能使用依賴的模塊固惯,這里是在定義的時候加載模塊了梆造,相當(dāng)于把依賴前置。

require()里面可以配置一些參數(shù):

require.config({
    paths: {
        "backbone": "vendor/backbone",
        "underscore": "vendor/underscore"
    },
    shim: {
        "backbone": {
            deps: [ "underscore" ],
            exports: "Backbone"
        },
        "underscore": {
            exports: "_"
        }
    }
});
  • paths: 指定模塊的位置葬毫,可以是文件路徑镇辉,也可以是一個網(wǎng)址
  • shim: 有些庫不兼容AMD的寫法,可以配置shim來解決贴捡,可以理解成“墊片”忽肛,幫助require.js加載非AMD規(guī)范的庫

CMD

Common Module Definition通用模塊定義,這個規(guī)范也是國內(nèi)發(fā)展起來的烂斋,有個瀏覽器的實現(xiàn)SeaJS屹逛,CMDAMD要解決的問題都是一樣的,只不過在模塊定義方式和模塊加載(可以說運行汛骂、解析)時機上有所不同罕模。

和其他規(guī)范一樣,CMD中也是一個模塊一個文件:

define(function(require, exports, module) {

  // 模塊代碼

});

require是可以把其他模塊導(dǎo)入進來的一個參數(shù);而exports是可以把模塊內(nèi)的一些屬性和方法導(dǎo)出的;module 是一個對象帘瞭,上面存儲了與當(dāng)前模塊相關(guān)聯(lián)的一些屬性和方法淑掌。

AMD是依賴關(guān)系前置,在定義模塊的時候就要聲明其依賴的模塊图张,CMD是按需加載依賴锋拖,只有在用到某個模塊的時候再去require诈悍,看看CMD的例子:

// model1.js
define(function (require, exports, module) {
    console.log('model1 entry');
    exports.getHello = function () {
        return 'model1';
    }
});

// model2.js
define(function (require, exports, module) {
    console.log('model2 entry');
    exports.getHello = function () {
        return 'model2';
    }
});

// main.js
define(function(require, exports, module) {
    var model1 = require('./model1'); //在需要時聲明
    console.log(model1.getHello());
    var model2 = require('./model2'); //在需要時聲明
    console.log(model2.getHello());
});

<script src="sea.js"></script>
<script>
    seajs.use('./main.js')
</script>

CommonJS主要用于服務(wù)端祸轮,模塊文件都存在本地硬盤,所以加載起來比較快侥钳,不用考慮用異步加載适袜,但是在瀏覽器中,受限于網(wǎng)絡(luò)原因舷夺,更合理的方案應(yīng)該是異步加載苦酱。為了方便模塊化開發(fā),這時武林中出現(xiàn)了AMDCMD兩大門派给猾,分別代表了兩種不同的思想疫萤,一是依賴前置,二是按需加載依賴敢伸,各有利弊扯饶,這兩大門派都像爭奪武林盟主,按理說是要比武論高下的,但半路殺出個程咬金尾序,選舉委員會直接自己推了一個規(guī)范:ES Module钓丰,告知天下以后要聽ES Module的,他才是大哥每币。

這小子又是誰携丁?

ES Module

ES modules(ESM)是 JavaScript 官方的標(biāo)準(zhǔn)化模塊系統(tǒng),能和CommonJS混合使用兰怠,意味著能在node環(huán)境下執(zhí)行梦鉴,因為他是標(biāo)準(zhǔn),所以未來很多瀏覽器都會支持揭保。

模塊定義:

// module.js
export function name() {
    return 'this is name';
}

export function foo() {
    return 'foooooo';
}

模塊加載:

// index.js
import { name, foo } from './module.js';
console.log(name(), foo());

加載模塊的方式還有很多種尚揣,比如:

// 加載單個模塊
import {sum} from './example.js'

// 加載多個
import {sum,multiply} from './example.js'

// 導(dǎo)入整個模塊,別創(chuàng)建一個別名
import * as example from './example.js'

ESM中模塊導(dǎo)出是值的引用掖举,看如下例子:

// b.js
export let age = 1;

setTimeout(() => {
    age = 2;
}, 10);
// a.js
import { age } from './b.js';

console.log(age);
setTimeout(() => {
    console.log(age);
    import('./b.js').then(({ age }) => {
        console.log(age);
    })
}, 100);

// 執(zhí)行結(jié)果:
// 1
// 2
// 2

import/export使用又一個限制快骗,就是不能在其他語句/表達式的內(nèi)部使用,比如if語句里面塔次,所以一般都在最底部方篮,原因是ESM使用javascript引擎靜態(tài)分析。

至此励负,ESM已經(jīng)是事實上的盟主藕溅,使用該規(guī)范無論是node環(huán)境還是瀏覽器環(huán)境都能很好兼容,而且前端也不要再引入requirejssea.js來進行模塊開發(fā)继榆。

以上是我對javascript模塊開發(fā)的一些筆記巾表,如有問題可以地下留言。

參考:

http://www.reibang.com/p/d67bc79976e6
https://blog.csdn.net/crystal6918/article/details/74906757
https://cloud.tencent.com/developer/article/1589084

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末略吨,一起剝皮案震驚了整個濱河市集币,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌翠忠,老刑警劉巖鞠苟,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異秽之,居然都是意外死亡当娱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門考榨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來跨细,“玉大人,你說我怎么就攤上這事河质〖讲眩” “怎么了申鱼?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長云头。 經(jīng)常有香客問我捐友,道長,這世上最難降的妖魔是什么溃槐? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任匣砖,我火速辦了婚禮,結(jié)果婚禮上昏滴,老公的妹妹穿的比我還像新娘猴鲫。我一直安慰自己,他們只是感情好谣殊,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布拂共。 她就那樣靜靜地躺著,像睡著了一般姻几。 火紅的嫁衣襯著肌膚如雪宜狐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天蛇捌,我揣著相機與錄音抚恒,去河邊找鬼。 笑死络拌,一個胖子當(dāng)著我的面吹牛俭驮,可吹牛的內(nèi)容都是我干的蛇尚。 我是一名探鬼主播壶笼,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼楞捂,長吁一口氣:“原來是場噩夢啊……” “哼摆马!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起厅须,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤枉氮,失蹤者是張志新(化名)和其女友劉穎身腻,沒想到半個月后雄坪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厘熟,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡屯蹦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年维哈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片登澜。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡阔挠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出脑蠕,到底是詐尸還是另有隱情购撼,我是刑警寧澤跪削,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站迂求,受9級特大地震影響碾盐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜揩局,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一毫玖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凌盯,春花似錦付枫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至县忌,卻和暖如春掂榔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背症杏。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工衅疙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸳慈。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓饱溢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親走芋。 傳聞我的和親對象是個殘疾皇子绩郎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容