欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript模块化教程

程序员文章站 2022-08-26 10:26:27
js是一种脚本语言,与php类似。但是如今php不再仅仅构建一个简单的web,它也在向java看齐,也能够支撑起一个庞大的web系统。国外非常有名的脸书网就是一个典型的例子。一门语...

js是一种脚本语言,与php类似。但是如今php不再仅仅构建一个简单的web,它也在向java看齐,也能够支撑起一个庞大的web系统。国外非常有名的脸书网就是一个典型的例子。一门语言,要构建出庞大复杂的系统的基础之一就是语言文件的相互引用。毕竟一个单文件直接实现一个庞大复杂的系统是十分吃力的。

后端模块化

commonjs

在互联网早期,javascript 一直在前端发光发热,但在后端基本上没有什么建树。并且 javascript 官方的一些规范也仅仅只是对于前端做了很多要求,没有考虑到后端,这样导致 javascript 一直寄生于中。但是 javascript 社区不满足仅仅让 javascript 这门语言只存在于浏览器里,他们希望能够在任何地方都能运行 javascript。于是,commonjs 应运而生,它是社区制定的一份 javascript 规范。

commonjs 涵盖的内容

模块 二进制 buffer 字符集编码 i/o流 进程环境 文件系统 套接字 单元测试 web服务器网关接口 包管理 …

commonjs 的模块规范

commonjs 的模块规范很简单,主要包含三部分,模块定义,模块标识以及模块引用。

模块引用

示例如下:

var math = require('math');

主要使用了require()方法,他接受一个参数,模块标识。

模块定义

示例如下:

exports.add = function (a, b) {
  return a + b;
};

主要使用了exports对象,只要将方法挂载在exports对象上作为属性即可定义导出的方式。

下面给出一个模块引入及使用的例子:

var math = require('math');
exports.getall = function (x, y) {
  return math.add(x, y);
};

模块标识

模块标识就是传递给require()方法的参数,他要求是符合小驼峰命名的字符串,或者以.,..开头的相对路径,或者绝对路径。他可以没有文件后缀名js。

commonjs 与 node.js

commonjs 只是定义了一份模块的规范,而 node.js 根据这份规范实现了模块机制。这好比接口和实现类,一个给出了接口,另一个真正实现了接口,但是实现可能有很多种不同的方式。

前端模块化

说完后端模块化,我们再来聊一聊前端模块化。

如何去实现前端的模块化呢?

有人可能会说,这还不简单,既然后端模块化已经可以良好的工作了,那我们直接照搬到前端不就可以了嘛!

理论上是的,但是现实中却遇到了一个大问题。我们要知道在后端,文件的 i/o 很快,因为我们直接在服务器的硬盘上读写文件即可。但是在前端可不是这个样子,所有文件的 i/o 都是要经过 http 请求去完成的,网络延时是个大问题。

如果一个页面空白3秒钟以上,那么用户肯定会不爽,一般他们会刷新,如果刷新页面几次都不能看到页面,那可能会直接关掉页面,甚至以后都不会再次访问你的站点。

所以,在前端我们实现模块化不能仿照后端那样,进行同步加载,最好的方式就是异步加载。而在前端比较火的两个异步加载规范就是 amd 和 cmd。

amd

全称:asynchronous module definition,异步模块定义 amd 是 requirejs 在过程中对模块定义的规范化产出

在 amd 规范中,一个模块就是一个文件。该规范的核心就是define()函数,define() 是一个全局函数,用来定义模块。

define(id, dependencies, factory);

下面给出一个示例:

define("types/manager", ["types/employee"], function (employee) {
    function manager () {
        this.reports = [];
    }
    //开始执行
    manager.prototype = new employee();
    //返回经理构造函数可以由其他模块的应用。
    return manager;
}
);

cmd

全称:common module definition,公共模块加载 cmd 是 seajs 在推广过程中对模块定义的规范化产出

在 cmd 规范中,一个模块就是一个文件。该规范的核心就是define()函数,define() 是一个全局函数,用来定义模块。

define(function(require, exports, module) {

  // 模块代码

});

下面给出一个示例:

define(function(require, exports, module) {
    // 通过 require 引入依赖 注意 .js 可以省略
    var $ = require('jquery');
    // 你也可以引入自己的函数依赖
    var spinning = require('./yourfunction');
    var util = {};
    util.sayhello = function(){
        return 'seajs向你问好';
    }
    // 通过 exports 对外提供接口
    module.exports = util;
});

amd 和 cmd 的区别

对于依赖的模块,amd 是提前执行,cmd 是延迟执行。不过 requirejs 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。cmd 推崇 as lazy as possible。 cmd 推崇依赖就近,amd 推崇依赖前置。
//  cmd
define(function(require, exports, module) {
    var a = require('./a')
    a.dosomething()  
    // 此处略去 100 行  
    var b = require('./b') 
    // 依赖可以就近书写 
    b.dosomething() 
    // ... 
})
// amd 默认推荐的是
define(['./a', './b'], function(a, b) { 
    // 依赖必须一开始就写好  
    a.dosomething()  
    // 此处略去 100 行 
    b.dosomething()   
    ...
})

es6

es6 这个词的原意,就是指 javascript 语言的下一个版本,他由国际标准化组织ecma提出。es6 模块是编译时加载,而commonjs 模块是运行时加载。

es6 模块功能主要由两个命令构成:export和import。

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

export命令

输出变量

export var firstname = 'michael';

输出函数

export function multiply(x, y) {
  return x * y;
};

使用大括号输出的一组变量

var firstname = 'michael';
var lastname = 'jackson';
var year = 1958;
function f() {}
export {firstname, lastname, year, f};
使用as关键字重命名
function v1() { ... }
function v2() { ... }
export {
  v1 as streamv1,
  v2 as streamv2,
  v2 as streamlatestversion
};

import 命令

import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。

引入变量

import {firstname, lastname, year} from './profile';
function setname(element) {
  element.textcontent = firstname + ' ' + lastname;
}

使用as关键字重命名

import { lastname as surname } from './profile';

执行加载模块

import 'lodash';

整体加载

import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));

export default 命令

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

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

import customname from './export-default';
customname();