JavaScript 之模块化

103 阅读3分钟

什么是模块化

模块化就是将一些系统功能进行拆分成独立可复用的模块。通常每个模块完成特定的功能,所有的模块组合起来成为一个整体,完成整个系统的功能。

无规范

早期 javascript 并未提供模块化的规范。开发者就通过语言特性模拟出该功能

立即执行函数

ajax.js 定义模块

;(function (ajax, $) {
  var contentType = 'json'
  // 定义的数据和方法放在此处,防止泄露污染全局
  ajax.get = function get() {
    console.log('GET')
  }
  ajax.post = function post() {
    console.log('POST')
  }

  ajax.print = function () {
    console.log(`contentType:${contentType}`)
    $('body').css('background', 'red')
  }
})(typeof exports === 'undefined' ? (this.ajax = {}) : exports, jQuery)

index.html 引入

<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script src="./ajax.js"></script>
<script>
  ajax.get()  // GET
  console.log(ajax.contentType); // undefined
  ajax.contentType = 'text'
  ajax.print()  // contentType:json
</script>

通过script标签引入,将ajax模块挂载在全局对象window上。
ajax模块中只暴露出了方法,不包括contentType,所以无法在外部获取与修改contentType,而是在window.ajax上新增的contentType属性。 对于第三方依赖需要手动引入,如上面传的第二个参数jQuery

优点:

  • 数据是私有的, 外部只能通过暴露的方法操作

缺点:

  • 第三方的依赖需要在定义该模块之前手动引入
  • 需要的依赖过多会导致依赖模糊,不清楚依赖关系,难以维护

如上代码,通过立即执行函数(IIFE)

CommonJS 规范

Node.js 应用由模块组成,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。每个模块内部有两个变量require 和 module

module 对象

module.id:模块识别符,通常是带有绝对路径的模块文件名
module.filename:模块文件名,带有绝对路径。
module.loaded:返回一个布尔值。表示模块是否已经完成加载
module.parent:返回一个对象,表示调用该模块的模块
module.children:返回一个数组,表示模块要用到的其他模块
module.exports:表示模块对外输出的值
module.paths:模块的目录名称
module.paths:模块的搜索路径

导出 module

module对象表示当前模块,通过exportsmodule.exports对模块中的内容进行导出。

function foo(){}

const data = 'data'

// 导出方式一
module.exports = {
  foo,
  data
}

// 导出方式二
exports.data = data
exports.foo = foo
Module {
  id: '.',
  path: '..../commonjs',
  exports: { foo: [Function: foo], data: 'data' },
  filename: '..../module1.js',
  loaded: false,
  children: [],
  paths: [
    ....,
    '/node_modules'
  ]
}

导入 require

通过require()方法可以加载模块的exports导出的值

const m = require('./module1.js')

console.log(m.data); // data
m.foo()  // Module1-foo

console.log(m); // { foo: [Function: foo], data: 'data' }

AMD 规范

AMD规范是异步模块加载,允许指定回调函数。使用需要引入工具库RequireJS.

下载 requirejs

官网:requirejs.org/requirejs导入项目,data-main用于加载入口文件(index.js)

<script data-main="index.js" src="./lib/require.js"></script>

定义模块

通过define()方法定义模块,define函数接受三个参数:

  1. 第一个参数是字符串,表示定义的模块名称,可选。如果无参默认为文件名称
  2. 第二个参数是字符串数组,表示定义的模块依赖的模块
  3. 第三个参数接受一个函数或是对象,函数参数是依赖注入的模块。在函数中通过return一个对象暴露模块的属性和方法。
// 定义函数
define({
  add:function(x,y){
    return x + y
  }
});

// 定义简单对象
define({
  name:'jack',
  age:20
});

// 具有依赖的模块
define(['./module1', './module3'], function (module1, module3) {
  return {
    name: module3.name,
    age: module3.age,
    add: module1.add,
  }
})

require 导入

require:接受2个参数

  1. 第一个参数是字符串数组,表示依赖的模块
  2. 第二个参数是回调函数
// 导入模块使用
require(['./module2'],function(module2){
  console.log(module2.name); // jack
  console.log(module2.add(1,2)); // 3
})

CMD 规范

下载引入seajs

GITHUB:github.com/seajs/seajs…

<script src="./lib/sea.js"></script>
<script type="text/javascript">
  seajs.use('./index')
</script>

使用

// module1.js
define(function (require, exports, module) {
  var data = 'module1'

  function print() {
    console.log(data)
  }

  exports.print = print
})

// module2.js
define(function (require, exports, module) {
  module.exports = {
    msg: 'module2',
  }
})

// module3.js
define(function (require, exports, module) {
  //同步引入依赖模块
  var module2 = require('./module2')
  function print() {
    console.log(module2.msg)
  }
  exports.print = print
})

// index.js
define(function(require){
  var module1 = require('./module1')
  var module3 = require('./module3')
  module1.print()  // module1
  module3.print() // module2
});

ES Module 规范

ES Module 通过export导出,import引入模块

// 暴露一个函数
export function foo(){
  console.log('foo');
}

// 暴露一个变量
export const data = [1,2]

function f1(){
  console.log('f1');
}

// 暴露一个对象
export {
  f1
}

// 默认暴露
export default {
  name:'ES Module',
  print:function(value){
    console.log(value);
  }
}
import { f1, foo, data } from './module1'
import module1 from './module1'

foo() // foo

f1() // f1

console.log(data) // [1, 2]

console.log(module1.name) // ES Module

module1.print('hello') // hello

使用import时,需要知道导入的变量名或函数名。除非是通过export default指定模块默认输出