一翘瓮、 模塊化簡介
隨著前端js代碼復(fù)雜度的提高,JavaScript模塊化這個(gè)概念便被提出來裤翩,前端社區(qū)也不斷地實(shí)現(xiàn)前端模塊化资盅,直到es6對其進(jìn)行了規(guī)范。
1.什么是模塊化
一個(gè)模塊就是一個(gè)文件
包 : 一堆模塊
包組成項(xiàng)目
二踊赠、 第一階段:無模塊化
JavaScript最初的作用僅僅是驗(yàn)證表單呵扛,后來會(huì)添加一些動(dòng)畫,但是這些js代碼很多在一個(gè)文件中就可以完成了筐带,所以今穿,我們只需要在html文件中添加一個(gè)script標(biāo)簽。
后來伦籍,隨著前端復(fù)雜度提高蓝晒,為了能夠提高項(xiàng)目代碼的可讀性、可擴(kuò)展性等帖鸦,我們的js文件逐漸多了起來芝薇,不再是一個(gè)js文件就可以解決的了,而是把每一個(gè)js文件當(dāng)做一個(gè)模塊作儿。那么洛二,這時(shí)的js引入方式是怎樣的呢?大概是下面這樣:
<script src="jquery_scroller.js"></script>
<script src="main.js"></script>
<script src="other1.js"></script>
<script src="other2.js"></script>
<script src="other3.js"></script>
即簡單的將所有的js文件統(tǒng)統(tǒng)放在一起攻锰。但是這些文件的順序還不能出錯(cuò)晾嘶,比如jquery需要先引入,才能引入jquery插件娶吞,才能在其他的文件中使用jquery垒迂。
1.優(yōu)點(diǎn):
相比于使用一個(gè)js文件,這種多個(gè)js文件實(shí)現(xiàn)最簡單的模塊化的思想是進(jìn)步的妒蛇。
2.缺點(diǎn):
污染全局作用域娇斑。 因?yàn)槊恳粋€(gè)模塊都是暴露在全局的策添,簡單的使用,會(huì)導(dǎo)致全局變量命名沖突毫缆,當(dāng)然乐导,我們也可以使用命名空間的方式來解決苦丁。
對于大型項(xiàng)目,各種js很多物臂,開發(fā)人員必須手動(dòng)解決模塊和代碼庫的依賴關(guān)系旺拉,后期維護(hù)成本較高。
依賴關(guān)系不明顯棵磷,不利于維護(hù)蛾狗。 比如main.js需要使用jquery,但是仪媒,從上面的文件中沉桌,我們是看不出來的,如果jquery忘記了算吩,那么就會(huì)報(bào)錯(cuò)留凭。
三、 第二階段: CommonJS規(guī)范
CommonJS就是一個(gè)JavaScript模塊化的規(guī)范偎巢,該規(guī)范最初是用在服務(wù)器端的node的蔼夜,前端的webpack也是對CommonJS原生支持的。
根據(jù)這個(gè)規(guī)范压昼,每一個(gè)文件就是一個(gè)模塊求冷,其內(nèi)部定義的變量是屬于這個(gè)模塊的,不會(huì)對外暴露窍霞,也就是說不會(huì)污染全局變量匠题。
CommonJS的核心思想就是通過 require 方法來同步加載所要依賴的其他模塊,然后通過 exports 或者 module.exports 來導(dǎo)出需要暴露的接口官撼。如下所示:
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
這里的a.js就是一個(gè)CommonJS規(guī)范的模塊了梧躺。 這里的module就代表了這個(gè)模塊,module的exports屬性就是對外暴露的接口傲绣,可以對外導(dǎo)出外部可以訪問的變量掠哥,比如這里的x和addX。
exports 是對 module.exports 的引用秃诵。比如我們可以認(rèn)為在一個(gè)模塊的頂部有這句代碼:
exports = module.exports
所以续搀,我們不能直接給exports賦值,比如number菠净、function等禁舷。
然后我們就可以在其他模塊中引入這個(gè)模塊使用了:
console.log(example.x); // 5
console.log(example.addX(1)); // 6
這里的require就會(huì)獲取到a.js所暴露的module.exports變量彪杉,然后就可以使用其暴露的x和addX了。
1.優(yōu)點(diǎn):
CommonJS規(guī)范在服務(wù)器端率先完成了JavaScript的模塊化牵咙,解決了依賴派近、全局變量污染的問題,這也是js運(yùn)行在服務(wù)器端的必要條件洁桌。
2.缺點(diǎn):
此文主要是瀏覽器端js的模塊化渴丸, 由于 CommonJS 是同步加載模塊的,在服務(wù)器端另凌,文件都是保存在硬盤上谱轨,所以同步加載沒有問題,但是對于瀏覽器端吠谢,需要將文件從服務(wù)器端請求過來土童,那么同步加載就不適用了,所以工坊,CommonJS是不適用于瀏覽器端的献汗。
四、 第三階段: AMD規(guī)范
之前提到: CommonJS規(guī)范加載模塊是同步的栅组,也就是說雀瓢,只有加載完成,才能執(zhí)行后面的操作玉掸。AMD規(guī)范則是非同步加載模塊刃麸,允許指定回調(diào)函數(shù)。由于Node.js主要用于服務(wù)器編程司浪,模塊文件一般都已經(jīng)存在于本地硬盤泊业,所以加載起來比較快,不用考慮非同步加載的方式啊易,所以CommonJS規(guī)范比較適用吁伺。但是,如果是瀏覽器環(huán)境租谈,要從服務(wù)器端加載模塊篮奄,這時(shí)就必須采用非同步模式,因此瀏覽器端一般采用AMD規(guī)范割去。而AMD規(guī)范的實(shí)現(xiàn)窟却,就是大名鼎鼎的require.js了。
AMD標(biāo)準(zhǔn)中呻逆,定義了下面兩個(gè)API:
2. define(id, [depends], callback)
即通過define來定義一個(gè)模塊夸赫,然后使用require來加載一個(gè)模塊。 并且咖城,require還支持CommonJS的模塊導(dǎo)出方式茬腿。
定義alert模塊:
var alertName = function (str) {
alert("I am " + str);
}
var alertAge = function (num) {
alert("I am " + num + " years old");
}
return {
alertName: alertName,
alertAge: alertAge
};
});
引入模塊:
alert.alertName('JohnZhu');
alert.alertAge(21);
});
但是呼奢,在使用require.js的時(shí)候,我們必須要提前加載所有的依賴切平,然后才可以使用握础,而不是需要使用時(shí)再加載。
1.優(yōu)點(diǎn):
適合在瀏覽器環(huán)境中異步加載模塊揭绑」颍可以并行加載多個(gè)模塊。
2.缺點(diǎn):
提高了開發(fā)成本他匪,并且不能按需加載,而是必須提前加載所有的依賴夸研。
五邦蜜、 第四階段:CMD規(guī)范
CMD規(guī)范是阿里的玉伯提出來的,實(shí)現(xiàn)js庫為sea.js亥至。 它和requirejs非常類似悼沈,即一個(gè)js文件就是一個(gè)模塊,但是CMD的加載方式更加優(yōu)秀姐扮,是通過按需加載的方式絮供,而不是必須在模塊開始就加載所有的依賴。如下:
var $ = require('jquery');
var Spinning = require('./spinning');
exports.doSomething = ...
module.exports = ...
})
1.優(yōu)點(diǎn):
同樣實(shí)現(xiàn)了瀏覽器端的模塊化加載茶敏。
可以按需加載壤靶,依賴就近。
2.缺點(diǎn):
依賴SPM打包惊搏,模塊的加載邏輯偏重贮乳。
其實(shí),這時(shí)我們就可以看出AMD和CMD的區(qū)別了恬惯,前者是對于依賴的模塊提前執(zhí)行向拆,而后者是延遲執(zhí)行。 前者推崇依賴前置酪耳,而后者推崇依賴就近浓恳,即只在需要用到某個(gè)模塊的時(shí)候再require。 如下:
define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好
a.doSomething()
// 此處略去 100 行
b.doSomething()
...
});
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
// 此處略去 100 行
var b = require('./b')
// 依賴可以就近書寫
b.doSomething()
// ...
});
六碗暗、 第五階段: ES6模塊化
之前的幾種模塊化方案都是前端社區(qū)自己實(shí)現(xiàn)的颈将,只是得到了大家的認(rèn)可和廣泛使用,而ES6的模塊化方案是真正的規(guī)范讹堤。 在ES6中吆鹤,我們可以使用 import 關(guān)鍵字引入模塊,通過 export 關(guān)鍵字導(dǎo)出模塊洲守,功能較之于前幾個(gè)方案更為強(qiáng)大疑务,也是我們所推崇的沾凄,但是由于ES6目前無法在瀏覽器中執(zhí)行,所以知允,我們只能通過babel將不被支持的import編譯為當(dāng)前受到廣泛支持的 require撒蟀。
雖然目前import和require的區(qū)別不大,但是還是推薦使用使用es6温鸽,因?yàn)槲磥韊s6必定是主流保屯,對于代碼的遷移成本還是非常容易的。 如:
import {mapState, mapMutations, mapActions} from 'vuex'
import axios from '../assets/js/request'
import util from '../utils/js/util.js'
export default {
created () {
this.getClassify();
this.RESET_VALUE();
console.log('created' ,new Date().getTime());
}