Skip to content

Latest commit

 

History

History
178 lines (118 loc) · 6.55 KB

模块化.md

File metadata and controls

178 lines (118 loc) · 6.55 KB

前端模块化


一、模块化理解

1、什么是模块化
  • 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),并进行组合在一起
  • 块的内部数据与实现是私有的,只是向外部暴露一些接口(方法)与外部其他模块进行通讯
2、模块化的进化过程
  • 全局function模式:将不同的功能封装成不同的全局函数
    • 编码:将不同的功能封装成不同的全局函数
    • 缺点:污染全局命名空间,容易引起命名冲突或数据不安全,而且模块成员之间看不出来直接关系
  • namespace模式:简单对象封装
    • 作用:减少了全局变量,解决命名冲突
    • 缺点:数据不安全(外部可以直接修改模块内部的数据)
let myModule = {
    data: 'Hello World',
    foo(){
        console.log(`foo() ${this.data}`)
    }
}

myModule.data = 'other data' //能直接修改内部的数据
myModule.foo() //此时模块内部数据已经不是原来的值

这样的写法会暴露所有模块成员,内部状态可以被外部改写。

3、模块化的好处
  • 避免命名冲突(减少命名空间污染)
  • 更好的分离,按需加载
  • 更高的复用性
  • 高可维护性
4、引入多个<script>后出现问题
  • 请求过多:我们要依赖多个模块机会发送多个请求,导致请求过多
  • 依赖模糊:我们不知道他们具体依赖关系是什么,也很容易因为不了解他们之间的依赖关系导致加载先后顺序出错
  • 难以维护:以上两种原因就导致很难维护,很可能出现牵一发而动全身的情况导致出现严重的问题。模块化固然有多个好处,但一个页面引入多个js文件难免出现很多问题。而这些问题可以通过模块化规范解决,以下介绍开发中最流行的commonJS,AMD,ES6,CMD规范

二、模块化规范

1、CommonJS

(1) 概述

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

(2) 特点

  • 所有代码都运行在模块作用域,不会污染全局作用域
  • 模块可以多次加载,但只会在第一次加载时运行依次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要向让模块再次运行,必须清除缓存
  • 模块加载的顺序,按照其在代码中出现的顺序

(3) 基本语法

  • 暴露模块:module.exports = valueexports.xxx = value
  • 引入模块:require(xxx),如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径

CommonJS暴露的模块到底是什么?CommonJS规范,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性是对外的接口。加载某个模块,其实是加载该模块的module.exports属性

// module 基本实现
var module = {
  id: "xxxx", // 我总得知道怎么去找到他吧
  exports: {}, // exports 就是个空对象
};
// 这个是为什么 exports 和 module.exports 用法相似的原因
var exports = module.exports;
var load = function (module) {
  // 导出的东西
  var a = 1;
  module.exports = a;
  return module.exports;
};

exports和module.exports的区别

  • module.exports的默认值为{}
  • exports是module.exports的引用
  • exports默认指向module.exports的内存空间
  • require()返回的是module.exports而不是exports
  • 若对exports重新赋值,则断开exports对module.exports的指向
//example.js
var x = 5;
var addX = function(value){
    return value + x;
}
module.exports.x = x;
module.exports.addX = addX;

//另一文件引入
var example = require('./example.js')
console.log(example.x)
console.log(example.addX(1))

require命令用于加载模块文件。require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错

模块查找机制

  • 优先从缓存中加载
  • 核心模块(已经被编译为二进制文件,只需按照名字加载即可)
  • 路径形式自定义模块
  • 第三方模块
    • 先找到当前目录下的node.modules目录,找到奴鲁中的包名
    • 找到包名下的package.json文件,找到其中的main属性,main属性中就记录了当前第三方包的入口文件
    • 若以上条件都不成立,则在上一级目录中找node_modules,直到当前磁盘根目录

2、ES6模块化

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

(1) ES6语法

export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能

/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export {basicNum, add};

/** 引入模块 **/
import {basicNum, add} from './math';

如上例所示,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

// export-default.js
export default function () {
  console.log('foo');
}

// import-default.js
import customName from './export-default';
customName(); // 'foo'

模块默认输出, 其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。

三、总结

  • CommonJS规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载,因此有了AMD,CMD的方案
  • AMD规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,AMD规范开发成本高,代码阅读和书写比较困难,模块定义方式的予以不顺畅
  • ES6在语言标准的层面上,实现了模块功能,而且实现的相当简单,完全可以取代CommonIS和AMD规范,成为浏览器和服务器通用的模块解决方案