JS模块化规范

106 阅读3分钟

概述

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

JS模块化的历史解决方案

  • 使用命名空间(即定义在某个对象中)
var obj = {
    module:'module',
    test:function(){
     console.log('call test function',module);
    }
}
  • IIFE立即执行函数 (函数式javascript唯一的localScope)。
(function(window){
    let module = "module";
    function test(){
        console.log('call test function',module);
    }
    window.JQueue = {
        test
    }
})(Window)
  • IIFE立即执行函数的增强版,引入依赖

引入jQuery依赖

(function (window, $) {
    console.dir('$', $);
    let module = "module";
    function test() {
        console.log('call test function', module);
    }
    window.JQueue = {
        test
    }
    $('body').css('background', 'red')
})(Window, jQuery);

模块化的好处

  • 避免命名冲突
  • 更好的分离,按需加载
  • 更高的复用性
  • 高可维护性

一个页面需要引入多个js文件的问题

  1. 请求过多。
  2. 依赖关系模糊,我们不知道他们具体的依赖关系是什么,而且如果需要根据依赖关系来确定引入的先后顺序。
  3. 难以维护,由于上述原因,很可能出现牵一发而动全身的情况导致项目出现严重的问题。

所以出现了模块化的几种规范来解决上述问题。

模块化的几种规范

1. CommonJS

说明

  • 每一个文件都是一个模块。
  • 在服务器端:模块的加载是运行时同步加载的。
  • 在浏览器端: 模块需要提前编译打包处理。(浏览器识别不了require等方法)。

基本语法

1.暴露模块

module.exports = value
exports.xxx = value

2.引入模块

require(xxx);
//xxx
//1. 第三方模块:xxx为模块名
//2. 自定义模块:xxx为模块文件路径

实现

  • 服务器实现 nodejs

module1.js

module.exports = {
    message:'hello world',
    test(){
        console.log('call module1 test function');
    }
}

module2.js

module.exports = function(){
    console.log('call module2 function');
}

module3.js

exports.test = function(){
    console.log('test() module2');
}

exports.getMessage = function(){
    console.log('call module2 getMessage function');
}

exports.arr = [1,2,3,4,5,6,7]

app.js

var module1 = require('./module1.js');
var module2 = require('./module2.js');
var module3 = require('./module3.js');

console.log('module1', module1);
console.log('module2', module2);
console.log('module3', module3);

//输出结果
//module1 { message: 'hello world', test: [Function: test] }
//module2 function(){
//    console.log('call module2 function');
//}
//module3 { test: [Function],
//  getMessage: [Function],
//  arr: [ 1, 2, 3, 4, 5, 6, 7 ] 
//}
  • 浏览器端实现 Browserify(也被称为CommonJS的浏览器端的打包工 browserify官网

2. AMD

说明

专门用于浏览器端,模块的加载是异步的。

基本语法

1. 暴露模块

//无依赖
define(function () {
    let name = "module1";
    function getName() {
        return name;
    }
    return {
        getName
    }
})

//有依赖
define(['module1'], function (module1) {
    let msg = 'module2';
    function showMsg() {
        console.log(msg, module1.getName());
    }
    return {
        showMsg
    }
})

2. 导入模块

(function(){
    requirejs.config({
        paths:{
          module1: './module1', //不需要加后缀名
          module2: './module2' //不需要加后缀名
        }
    })

    requirejs(['module2'],function(module2){
        module2.showMsg();
    })
})();

浏览器端实现

Require.js

3. CMD

一个阿里人写的,使用的不太广泛。据说现在卖给老外了。这里不详细介绍了。

浏览器端实现

seajs 官网已经找不到。

4. ES6

说明

依赖模块需要编译打包处理。(因为es6中有部分语法,浏览器还不支持,所以需要转成es5语法)

基础语法

导出模块

  1. 批量导出
export const message = "hello world";
export const data = [1,2,3,4,5];
export const obj = {
    key:1,
    value: 'obj'
}

export function getName(){
    console.log('module1: My Name is wangyanfei');
}

2.具名导出

function getName(){
    console.log('module2: this is module2.js');
}

function getLocation(){
    console.log('module2: call getLocation function');
}

export {
    getName,
    getLocation
}

3.默认导出

export default function getName(){
    console.log('module3: call getName function');
}

引入模块

1.具名引入

import {message,getName} from './module1.js';
import {getName as getN} from './module2.js';
  1. 批量导入
import module3 from './module3.js';

浏览器端实现

使用babel(Babel 中文网 · Babel - 下一代 JavaScript 语法的编译器 (babeljs.cn)

.babelrc是babel运行时的配置文件。rc是run command的缩写

因为有的浏览器无法识别 import 和 export 语法,所以要对js进行编译成浏览器器可以识别的es5语法。可以按照下面2步执行:

  1. 使用babel将es6编译为es5代码(包含CommonJS语法)
  2. 使用 browserify 编译成浏览器可以识别的js语法。