前端模块化的原因
随着前端的功能越来越复杂,代码量越来越多。如果众多的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引用其中的变量和函数。