前端模块化 common js & AMD & CMD & es6 module

711 阅读4分钟

随着js复杂度的提高,模块化更是应用广泛,那有没有小伙伴对各种引入导出傻傻分不清楚?module.export跟exports 有啥区别?

前言 - 模块化

通常我们在编写代码时,会将复杂的问题根据实际情况进行合理的拆分,让代码更具备可读性与可维护性。因此一个模块可以理解为整体的一部分。

  • 每个模块都是独立的个体,当然最好是遵循最小功能模块化;
  • 模块是复用的
  • 更好的分离,可按需加载

模块化的发展

1.最简单的全局函数

function fn1(){
    
}
function fn2(){
    
}

实现: 每个函数按照功能划分

问题: 全局污染,应该偏向面向过程的开发模式

2.namespace模式 : 对象字面量的写法,对某个功能块简单的封装

let action = {
    name: 'aa',
    printName(){
        console.log(this.name)
    }
}
action.name = 'bb'
action.printName()//bb

实现: 可以封装比较复杂的组件方法,减少了全局变量

问题; 里面的方法数据是可以改变的。数据不安全

3.IIFE模式:匿名函数自调用(闭包)

jquery的源码就是用的这个模式;也包括jq的一些组件,都是这样处理。

实现: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口,避免全局变量污染

模块化规范

1.Common js

概述

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

特点

1.有自己的作用域,不会污染全局

2.文件里的内容是私有的,保证数据安全

3.模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。

4.模块加载的顺序,跟代码的顺序相关(同步加载~)

语法

//导出
module.exports = value
exports.xxx = value
//引入
require()

加载机制

CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

2.AMD

AMD和CMD都是异步的,应用于浏览器端的。因为是异步的,所以在加载完成后,可以用回调访问数据方法。

define(['module1', 'module2'], function(m1, m2){
   return 模块
})
require(['module1', 'module2'], function(m1, m2){
   使用m1/m2
})

3.CMD

跟AMD一样都是异步的,CMD规范整合了CommonJS和AMD规范的特点,在sea.js 中,遵循的是CMD规范。

define(function(require, exports, module){
  exports.xxx = value
  module.exports = value
})

define(function (require) {
  var m1 = require('./module1')
  var m4 = require('./module4')
  m1.show()
  m4.show()
})

4.ES6的模块化

ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

// CommonJS模块
let { stat, exists, readFile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

// ES6模块
import { stat, exists, readFile } from 'fs';

commonjs的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

ES6 模块的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高

两者的区别:

① CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。

② CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。