前端模塊化的原因
隨着前端的功能越來越複雜,代碼量越來越多。如果衆多的js文件不採用模塊化的方式管理,勢必會遇到一個問題,那就是全局變量重名。相信很多前端開發者都遇到過這個問題。。。
實際項目開發中,通常不同人有不同的分工,分別負責不同的功能模塊。
假設A在part1.js中定義了全局變量tag,並賦了值,B在part2.js中恰好也定義了同名變量tag,也對tag賦了值,那麼tag作爲全局變量,它的值取決於part1.js和part2.js的執行順序,值具有不確定性。如果a在其他js文件(part3.js)中再想用part1.js中tag的值,數據可能會不一致。
part1.js
var tag=true;
var name='a';
function add(a,b)
{
return a+b;
}
console.log('我是a');
part2.js
var tag=false;
var name='b';
console.log('我是b');
part3.js
if(tag)
{
console.log('我使用了part1中的tag');
}
三個js的引用順序如下:
<body>
<script src="../js/part1.js"></script>
<script src="../js/part2.js"></script>
<script src="../js/part3.js"></script>
</body>
part2.js中tag的值覆蓋了part1.js的值,當a在part3.js中使用tag時,tag的值已經發生了改變。
結果如下:並沒有輸出part3.js中的“我使用了part1中的tag”這句話。
通過匿名函數實現模塊化
要讓part1.js,part2.js,part3.js互不影響,擁有獨立的作用域,可以考慮採用匿名函數。但是,part3.js中想要引用part1.js中的變量,因此,也不能完全獨立,part3.js中要能訪問到part1.js中的變量或者函數。修改如下:
part1.js
通過匿名函數實現作用域的隔離,使各個js文件互不影響。爲了讓其他js文件能引用相關的變量和函數,返回一個全局對象供其他js文件引用。
var part1 = (function(){
var obj1={};
var tag=true;
var name='a';
function add(a,b)
{
return a+b;
}
console.log('我是a');
obj1.tag=tag;
obj1.add=add;
return obj1;
})()
part2.js
var part2=(function(){
var obj2={};
var tag=false;
var name='b';
console.log('我是b');
obj2.tag=tag;
obj2.name=name;
return obj2;
})()
part3.js
在引用時,通過對象引用相關的變量和函數。
if(part1.tag)
{
console.log('我使用了part1中的tag');
}
console.log(part1.add(10,20))
結果如下:part1和part2成了兩個互不影響的模塊,如果要引用part1或者part2中的變量和函數,可以通過返回對象來引用。
ES6模塊化
通過匿名函數加返回值的方式實現模塊化是比較繁瑣的,es6中已經幫我們實現了模塊化。
在引用js文件的時候,通過添加type="modual"屬性,各個js文件實現獨立,通過export和import,各js文件實現交互。
具體如下:
三個文件都作爲模塊引入,加入了type="modual"
<body>
<script src="../js/part1.js" type="module"></script>
<script src="../js/part2.js" type="module"></script>
<script src="../js/part3.js" type="module"></script>
</body>
part1.js
爲了讓其他js文件引用相關的變量和函數,需要將相關的變量和函數導出export{tag,add},這裏採用的是es6的語法,省略了key值。
var tag=true;
var name='a';
function add(a,b)
{
return a+b;
}
console.log('我是a');
export{
tag,add
}
part2.js
part2暫時不需要輸出變量和函數
var tag=false;
var name='b';
console.log('我是b');
function mul(a,b)
{
return a*b
}
part3.js
引用的時候通過import語法,import {tag,add} from './part1.js',tag和add分別對應export中的變量或者函數的名稱,part1.js是指定引用哪個文件中的export。
import {tag,add} from './part1.js'
if(tag)
{
console.log('我使用了part1中的tag');
}
console.log(add(10,20))
結果如下:
export可以將所有需要輸出的變量和函數寫在一起,以對象的形式返回。也可以在定義的時候指定export。例如:將part2.js中的name變量和mul函數輸出。
part2.js修改如下:在函數前面添加export關鍵字,變量的導出也是類似的。
var tag=false;
export var name='b';
console.log('我是b');
export function mul(a,b)
{
return a*b;
}
在part3.js中引用該函數:
import {tag,add} from './part1.js'
if(tag)
{
console.log('我使用了part1中的tag');
}
console.log(add(10,20));
import {name,mul} from './part2.js'
console.log(mul(10,20));
結果如下:
在export和import的時候,變量或者函數的名稱必須一致,否則就找不到了。如果想在import的時候,重新給變量取個名字,需要用到export default。但是default無論是變量還是函數,只能有一個。
part2.js修改如下:輸出了一個default sex
var tag=false;
export var name='b';
console.log('我是b');
export function mul(a,b)
{
return a*b;
}
const sex='男';
export default sex;
part3.js
給sex重新取一個名稱s
import {tag,add} from './part1.js'
if(tag)
{
console.log('我使用了part1中的tag');
}
console.log(add(10,20));
import {name,mul} from './part2.js'
console.log(mul(10,20));
import s from './part2.js'
console.log('性別是:'+s);
結果如下:
如果需要引用的變量特別多,一個一個寫會比較長,影響美觀,可以通過對象的形式引入:
import * as obj from "./xxx.js",通過obj引用其中的變量和函數。