前端模块化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. CommonJSconst 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》