前端模块化规范

前端为什么需要模块化?

随着前端能承担更多的开发责任,各种问题开始凸显,全局变量冲突、依赖关系难以管理、数据安全问题等等。

什么是模块?

  • 将一个复杂的程序依据一定的规则封装成几个块,并进行组合在一起。
  • 块的内部数据与实现是私有的,只是向外部暴露一些接口(方法)与外部其它模块通信。

模块化的好处?

  • 避免命名冲突(减少命名空间污染)
  • 更多的分离,按需加载
  • 更高复用性
  • 高可维护性

那么模块化规范有哪些呢?

模块化规范

CommonJS

CommonJS是服务器端模块的规范,Node.js采用了这个规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。

(1)、基本语法

// 定义模块math.js
const value = 1
const add = function(){
    value++
}
module.exports = {
    value,
    add
}
// 引入模块math.js
const math = require('./math')
math.add()
// 会发现value的值还是1
console.log(math.value)

注意导出的时候还可以使用exports.xxx = value

(2)、特点

  • 同步加载,由于CommonJS是用于服务器端的,服务器端的文件都在本地,所以没什么影响,但是用于浏览器就不行了,文件都是要下载的,采用同步加载的方式就会导致阻塞。
  • 输出的是值的拷贝,所以值改变时,不影响之前导出的值。
  • 运行时加载,所以无法做到提前分析依赖以及Tree-Shaking
  • 所有代码都运行在模块作用域,不会污染全局作用域。
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果,要想让模块再次运行,必须清除缓存。
  • 模块加载的顺序,按照其在代码中出现的顺序。

(3)、使用方式

  • npm使用

AMD

主要用于客户端,异步加载。AMDCommonJS的主要区别在于它是否支持异步模块加载。

// 定义导出模块
// 不依赖其它模块的模块定义
define(function(){
   return 模块
})
// 依赖其它模块的模块定义
// 第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。
define(['module1', 'module2'], function(m1, m2){
   return 模块
})
// 引入模块
require(['module1', 'module2'], function(m1, m2){
    return 模块
})

UMD

它就像是一个工厂,为了同时支持CommonJSAMD的规范,判断谁的规范支持就使用谁的规范,他的外层是一个iife。并且支持直接在前端用<script src="lib.umd.js"></script>的方式加载。

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define(['b'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory(require('b'));
    } else {
        // Browser globals (root is window)
        root.returnExports = factory(root.b);
    }
}(this, function (b) {
    //use b in some fashion.
 
    // Just return a value to define the module export.
    // This example returns an object, but the module
    // can return a function as the exported value.
    return {}
}));

ESM

ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
浏览器直接通过<script type="module">即可使用该写法。NodeJS可以通过mjs后缀或者在package.json添加"type": "module"来使用。

(1)、基本语法

// 定义模块 math.js 
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };
// 引用模块
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}

(2)、特点

  • 完全替代CJSAMD,淘汰了UMD,命名空间等规范。
  • 静态化,编译时加载,使得页面加载速度快。
  • ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import时,不会去执行模块,而是只生成一个动态的只读引用。等到真的需要用到时,再到模块里面去取值。
  • import命令具有提升效果,因为是编译时加载,所以会比其他代码都先执行。
  • ES6模块输出的是值的引用,值改变时,原本输出的值也会变化。

(3)、使用方式

  • 浏览器通过<script type="module"/>引入
  • npm使用

对外提供组件时,同时提供 esmcommonjsumd 这3种方式,并且在package.json 中对应的字段进行声明,以确保这个包可以兼容多环境。
package.json 中引用优先级如下:targetweb 时, 依次查找 browsermodulemain。其他 target , 依次找 modulemain。因此如果声明了 module, 会优先读取 module 中的路径。

参考文章

1、前端模块化详解(完整版)
2、前端模块化一——规范详述
3、前端模块化iife、CJS、AMD、UMD、ESM的区别
4、打包umd_cjs, umd, esm or iife?
5、NPM 组件你应该知道的事

posted @ 2022-02-07 20:44  SummerSatr  阅读(191)  评论(0编辑  收藏  举报