上一篇文章《前端工程化-序言》中提到”项目复杂度”的问题。

文章开头,其实可以先思考一下,项目复杂度为什么会提升?

  • 不好的开发习惯,没有规范?
  • 单文件、巨石代码?
  • 命令式编程,毫无章法可言?

结论是肯定的,大部分场景的项目复杂度的提升都离不开以上的三种原因,当然也有别的原因,我这里暂时先列举这三种主要的。

所以,前端开发的思想以及技术要革新,模块化技术也随之更替演变。

模块化之function-猿猴模式

借助函数实现模块化,最终还是暴露在 window 上,内部封闭了若干常量。

function getEnvUrl() {
    return {
        qa: 'test.xxx',
        pre: 'pre.xxx',
        prod: 'prod.xxx'
    }
}

function getGatwayByEnv() {
    return {
        qa: 'test.gatway.xxx',
        pre: 'pre.gatway.xxx',
        prod: 'prod.gatway.xxx'
    }
}

const qaUrl = getEnvUrl().qa;
  • 缺点
    • 容易明名冲突
    • 模块函数众多,且毫无关联

改进:
- 可以作函数聚合、函数收敛

模块化之namespace-原始人

使用全局变量聚合所有函数模块以及常量模块;

var __module__ = {
    __vars: {
        env: ['qa', 'pre', 'prod']
    },
    // ...
}

// 严格意义上等同于 window.__module__
  • 优点:
    • 避免了命名冲突
  • 缺点:
    • 外部容易修改

改进Object.freeze 冻结对象属性。

模块化之IIFE-山顶洞人

闭包实现模块化,保证内部的变量私有化,只暴露访问函数,外部无法改变内部私有化变量;

;(function (root) {
    var x = 1;
    const getX = () => x;
    root.__XModule = { 
        getX
    };
})(window)

;(function (root, xModule) {
    root.__YModule = xModule.getX();
})(window, window.__XModule)
  • 好处
    • 解决了私有化变量的问题
    • 解决了模块之间互相引用的问题

籍此,早起模块化的实现方案就此诞生了,但是按照早期的方案而言,一个项目中如果存在若干个模块就势必会出现若干个 script 标签。

由此导致的后果就是: 拖慢应用加载速度,牺牲用户的体验,流量流失。


在介绍现代模块化的前面,这里先解释下,模块化到底是什么?

模块化是 规范,不是 实现

  • Node
    • commonjs
    • esmodule
      • mjs
  • Browser
    • amd
    • cmd
    • esmodule
  • Node & Browser
    • umd

本篇文章暂时不具体介绍 amdcmd 规范,因为目前基本不再使用了。

CommonJs

CommonJs规范,本身是基于 IIFE 机制实现的。

那么,可以思考:requiremodule 等为什么可以直接使用?

模块加载时,自动注入。

;(function Module(require, module) {
  
})(require = () => 0, module = { exports: {} })

下面是 CommonJs 模块加载原理。


图片来源于:https://javascript.ruanyifeng.com/nodejs/module.html#toc14

  • 特点
    • 同步加载
    • 运行时加载,会先加载一遍全模块,再缓存
    • 模块输出是值拷贝
      • 注意:是浅拷贝

ES Module

规范参考:https://michaelcurrin.github.io/dev-cheatsheets/cheatsheets/javascript/general/modules/es-modules.html

  • 特点
    • 同步/异步加载
    • 编译时加载
    • 模块输出是值引用

End

文章结束了,请思考一个问题:

模块化最终解决了什么问题?

参考