在前面几篇文章中,我们先给大家介绍了如何从 0 到 1 创建现代化前端项目,以及如何使用脚手架创建并在开发中提效。那么,在项目创建好之后,就要进入到开发的阶段了。接下来,就给大家介绍在开发阶段怎么使用工程化的手段进行提效。本篇文章就来给你介绍一下分治思想在前端的应用 —— 模块化。
分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法就是运用分治思想的一种很重要的算法。
在最原始的软件开发时期,网页开发只需要实现简单的页面样式和页面交互逻辑即可,大部分任务只是“切图”。但是随着前端技术的发展和应用(比如 ajax 和 Node.js),使得前端也逐步具备开发大型项目的能力,网页开发也越来越趋向于桌面应用。在开发大型项目的道路上,项目的代码量和复杂度都在日益增长,JS 面临的首要问题就是:如何组织代码以及如何进行复用代码。
模块化就是用了分治的思想,将项目拆分为一个个的模块,分而治之。模块化是一种代码组织和代码复用的方式。模块化也是时代发展的产物,是前端可以开发大型项目的基石。
本文我就带你了解一下前端领域的模块化相关内容,先介绍什么是模块化以及模块化的发展历史,最后再分析一下常见的几种模块化规范。
# 什么是模块
模块是指由数个基础功能组件组成的特定功能组件,可用来组成具完整功能之系统、设备或程序。 —— 维基百科
我们通常所说的“模块”指的是编程语言提供的一种组织代码的机制,可以将代码拆分为独立且通用的单元。在开发的时候,按照模块分别开发,最后再将不同的模块“组装”在一起。
具体到前端领域,模块指的就是实现某一功能的一段 JS 代码或者一个 JS 文件。每个模块其隐藏了内部的实现,通过暴露出来的接口进行互相的调用通信。
# 如何实现模块
在了解了什么是模块之后,我们再来看下如何去实现一个模块。
上面我们也提到了,模块是为了实现某个功能的代码集合。在 JS 中,最简单的将几个功能函数放在一起,就可以说是实现了一个模块。
比如,我们想要实现一个四则运算模块,我们可以定义加、减、乘、除四个函数 add()、subtract()、multiply()、divide() 然后将它们组合在一起,这就是一个四则运算模块。具体如下:
// arithmetic 模块
const add = () => {
console.log('add')
}
const subtract = () => {
console.log('subtract')
}
const multiply = () => {
console.log('multiply')
}
const divide = () => {
console.log('divide')
}
理论上来说这就是一个最简单的模块,但是在我们实际的开发中,应该没有人会直接这样使用。虽然这种方式定义的模块理解成本最低也最方便,但是缺点也很明显:不仅存在污染全局命名空间的问题,而且各个模块之间的关系并没有体现,模块内都是相互独立的个体。
更进一步,我们可以将这四个函数封装在 arithmetic 对象中,将其作为对象的属性,在使用的时候调用对象的属性即可,如下:
const add = () => {
console.log('add')
}
const subtract = () => {
console.log('subtract')
}
const multiply = () => {
console.log('multiply')
}
const divide = () => {
console.log('divide')
}
const arithmetic = {
_initData: 0,
add,
subtract,
multiply,
divide,
}
这种将函数定义对象属性的写法更像是一个模块而不是 4 个独立的函数。但是这种方式也有缺点:对象内的状态可能会被外部改写。比如,我们可以在外部直接给 _initData 赋值为 1:
arithmetic._initData = 1;
console.log(arithmetic._initData) // 1
console 的结果是 1,说明我们可以直接将对象内部的值改掉,模块内的私有属性和私有方法被污染了。
我们继续优化,使用立即执行函数 IIFE(Immediately-Invoked Function Expression)的写法。IIFE 会创建一个只使用一次的函数,然后立即执行;IIFE 可以创建闭包进行作用域隔离,从而保护私有变量。使用 IIFE 方式实现的模块化可以解决上面的问题,
