总结下前端的模块化

81 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

随着前端页面的不断壮大,页面也越来越负责,本来在最开始的时候js是没有模块化的,但是项目越来越负责,为了后期管理的容易,催生出了模块化的概念,这是一种管理规范,通过分割模块的形式解耦各个功能。

没有模块化带来的问题

早期js是没有模块化的概念的,对于js代码我们在一个文件夹中写好后在html文件中进行引入,但是这样就会带来问题,尤其是现在前端的代码量越来越大,有的时候涉及到了团队协作开发,这个时候没有模块化的概念就会出现变量重复等问题。

最开始的方案

为了解决js作用域的问题,我们一开始是使用立即执行函数来解决的,因为js没有块级作用域的概念,只有函数作用域,所以只能通过函数来模拟块级作用域,来实现作用域的分离。

(function(){...})();

这很好的解决了作用域的问题,但是对于加载的顺序问题依旧存在(对于文件的引用有加载顺序的要求)。

commonJS

我们熟悉的node就是使用的commonJS。其特点是优先从缓存中加载,其不能在浏览器端使用,其模块的加载是同步的,所以不能符合浏览器的异步环境,其中模块的引入是通过require语法,模块的导师是通过module.exports或者exports输出的值是拷贝值,需要注意这里的exports是module.exports的值的引用。

exports 和 module.exports 的区别

  • 每个模块中都有一个 module 对象
  • module 对象中有一个 exports 对象
  • 我们可以把需要导出的成员都挂载到 module.exports 接口对象中
  • 也就是:moudle.exports.xxx = xxx 的方式
  • 但是每次都 moudle.exports.xxx = xxx 很麻烦,点儿的太多了
  • 所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:exports
  • exports === module.exports 结果为 trues
  • 所以对于:moudle.exports.xxx = xxx 的方式 完全可以:expots.xxx = xxx
  • 当一个模块需要导出单个成员的时候,这个时候必须使用:module.exports = xxx 的方式
  • 不要使用 exports = xxx 不管用
  • 因为每个模块最终向外 return 的是 module.exports
  • exports 只是 module.exports 的一个引用
  • 所以即便你为 exports = xx 重新赋值,也不会影响 module.exports
  • 但是有一种赋值方式比较特殊:exports = module.exports 这个用来重新建立引用关系的

AMD

为了能在浏览器端实现模块化,所以推出了AMD的模块化规范,其实现库有RequireJS,其支持异步加载使用如下:

  • define(moduleName, [module], factory) 定义模块,一个文件可设置多个模块

  • require([module], callback) 引入模块

ES module

对于熟悉脚手架开发的前端开发者来说这个规范一定不陌生,要注意前面介绍的commonJS、AMD、CMD等都不是ES官方的标准,为了实现统一的规范,于是ES6版本中官方才从语言层面实现了模块化的功能。示例如下

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

我们通过export来导出模块,可以有默认暴露export default 但是一个文件只能有一个默认暴露,同时还可以有分别暴露的形式,不同的暴露形式对应不同的导入,对于默认暴露我们直接import后接随意的名字皆可,但是对于分别暴露就要使用{对应名字}的形式来导入:

默认暴露

let a=11;
export defalut a;
import a from "a.js"
import otherName from "a.js"

分别暴露

let a=11;
export a;
import {a} from "a.js"

注意在浏览器端使用ES6模块时:

<script type="module" src="a.js"></script>

会进行异步加载,不会阻塞浏览器,但是现在支持还不是很好,所以还需要babel进行转化才能正常使用。