前端模块化
1. 模块化优点
目前由于MVVM模式的流行,各种语言都更注重模块化。模块化设计的好处:
- 作用域:避免全局变量污染;
- 复用:可重复利用封装的模块为不同地方实现相同功能;
- 解耦:减少不同功能代码相互关联性,让代码分工明确,也方便Debug;
- 按需加载:加快加载速度、异步加载不必要的部分;
2. 模块化方法整理
从最早的script标签开始,前端模块化经过了多种编程方案的演化,逐步完善。
2.1 script标签
<script src="module_a.js"></script>
<script src="module_b.js"></script>
<script src="main.js"></script>
所有的js文件共享全局作用域,容易引起作用域污染。
2.2 闭包函数(立即执行函数)
(function(){
// xxx
})()
这是笔者早年使用最多的js编程方式 0_0
虽然避免了作用域污染的问题,但多个文件内的函数互相调用时,处理较为麻烦。常用方法是:
- 在闭包内定义一个
window.myFunc = function(){}
的方法,在闭包外可以调用; - 使用事件监听,给需要外部调用的方法设置事件,从外部触发事件;介绍一种自定义事件来控制闭包函数间传值的方法:
// 利用闭包函数自定义一个事件监听触发机制
// 自定义机制,不会受到默认的事件影响
var EventManager = (function() {
var events = {}
return {
add: function(name, fn) {
if(!events[name]){
events[name] = [];
}
events[name].push(fn);
},
fire: function(name, args) {
var fnList = events[name];
if(fnList){
for (var i = 0, l = fnList.length; i < l; i++) {
var fn = fnList[i];
if (fn && typeof fn == 'function') {
fn(args);
}
}
}
}
}
})()
// 在闭包内监听,可调用闭包内方法
(function() {
EventManager.add('user.login', function(data) {
console.log('user.login', data)
})
})()
// 在任意位置触发
EventManager.fire('user.login', {name: 'lc'})
自定义事件监听,相对于window下的函数,更加灵活,也更符合模块化的思想。举个例子:
通信系统中,用户登录后,需要获取聊天记录、通信录、个人信息,这些分别在不同的模块(闭包)中。
如果用函数的思想,需要在登录的地方进行不同的方法调用,这样就使得登录模块与多个业务模块产生了耦合;如果用自定义事件的方法,登录后只需要广播一个事件,同时在多个业务模块分别监听事件,各模块间就完全没有耦合,就算任意删掉一个模块也可以保证其他模块正常运行。
存在问题:
不论是全局函数、全局事件、自定义事件,在调用每个闭包中的方法时,斗需要确保该闭包先执行后调用。在复杂项目中,需要先执行大量闭包函数,会导致启动慢、逻辑复杂等各种问题。
2.3 AMD - 异步模块定义
define('myfunc', ['math'], function(math) {
math.sum(1, 2)
});
通过 define
函数引入需要的依赖包,每个模块所依赖的包/模块一目了然。
2.4 CMD - 通用模块定义
define(function(require, exports, module) {
const math = require('math')
math.sum(1, 2)
})
CMD的原则是将引入模块尽量后置,在使用的时候才去引入。
这样使得js执行时效率更高,更符合lazy load的思维方式,但对代码管理确不是很方便。
2.5. CommonJS
const math = require('math')
module.exports = function() {
math.sum(1, 2)
}
目前NodeJS的模块管理常用的就是这种方式,很多NPM的包也是这样处理的模块引入。
2.6 ES6的模块化
import { myFunc1, myFunc2 } from 'myFuncs';
import Vue from 'vue';
export function hello() {};
export default {
// ...
};
Vue、React等常用框架目前都在使用这种模块化方法。
比如在vue中,配合vue-router,在组件中按需import模块或模块中的函数,可以通过webpack实现按需加载。同时,这种模块化方式使得模块间的方法调用更加方便,不需要考虑模块声明前后顺序,因为webpack会自动生成依赖树。
2.7 样式文件模块化
// util.less
.common {
color: pink;
}
// main.less
@import 'util.less'
.red {
color: red;
}
样式文件目前也支持模块化。
参考:《深入浅出Webpack》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。