前端模块化总结

栏目: JavaScript · 发布时间: 5年前

内容简介:开发者可以只需要实现业务逻辑,其他可加载别人写好的公共模块。缺点:缺点:

开发者可以只需要实现业务逻辑,其他可加载别人写好的公共模块。

前端模块化总结

一、 script 引入文件

<script src="jquery.js"></script>
<script src="jquery_scroller.js"></script>
<script src="bootstarp.js"></script>
<script src="commutil.js"></script>
<script src="main.js"></script>
复制代码

①最原始的写法

/**
    commutil.js(main.js)中:
    模块就是实现特定功能的一组方法。
    只要把不同的函数简单地放在一起,就算是一个模块。
*/
function fun1(){
    ...
}
function fun2(){
    ...
}
...
复制代码

缺点:

  1. 所有的模块都处于全局作用域下, 容易造成命名冲突
  2. 依赖关系不明显, 比如 main.js 中有使用 jquery , 那么 jquery 就一定要先加载,
  3. 但是从引入方式中我们无法直观的察觉依赖关系, 不利于维护

②将上述的多个方法写在一个对象中

var obj = new Object({
     _count : 0,
     fun1 : function (){
        ...
     },
     fun2 : function (){
        ...
     }
 });
复制代码
//调用方法
obj.fun1()
obj.fun2()
复制代码

缺点:

  1. 这样的写法会暴露所有模块成员,内部状态可以被外部改写。
  2. 比如,外部代码可以直接改变内部计数器的值。
obj._count = 1;
复制代码

③立即执行函数写法

使用”立即执行函数”(Immediately-Invoked Function Expression, IIFE ),可以达到不暴露私有成员的目的。

var obj = (function() {
    var _count = 0;
    var fun1 = function() {
        alert(_count)
    }
    var fun2 = function() {
        alert(_count + 1)
    }   
    return {
        fun1, fun2
    }
})()
复制代码

使用上面的写法,外部代码无法读取内部的_count变量。

console.info(obj._count);  //undefined
//调用内部函数可以
obj.fun1()
复制代码
jQuery

二、 require 导入模块【运行时加载】

随着 web 项目越来越大, JS 的代码量也与日俱增,于是社区就自发约定了几种模块化的方案: requirejs 遵循 AMDseajs 遵循 CMDnodemodule 遵循 CommonJS 规范,虽然写法上有所不同,都是为了能够间接实现模块化的基础上保持较为一致的代码风格。

  • 2009年,美国程序员 Ryan Dahl 创造了 node.js 项目,将 javascript 语言用于服务器端编程。 这标志“Javascript模块化编程”正式诞生。
  • 前端的复杂程度有限,没有模块也可以,但服务器端,需要与操作系统和其他应用程序互动,没有有模块,根本没法编程。
  • node 编程最重要的思想之一就是模块,正是这个思想,让 JavaScript 的大规模工程成为可能。
  • 模块化编程在 js 界流行,也是基于此,随后在浏览器端, requirejsseajs 之类的 工具 包也出现了。
  • 可以说在对应规范下, require 统治了 ES6 之前的所有模块化编程,即使现在,在 ES6 module 被完全实现之前,还是这样。

①CommonJS

一个文件就是一个模块, 其内部定义的变量, 方法都处于该模块内, 不会对外暴露.

//新建 a.js, 导出sayHello和introSelf
// a.js
function sayHello(name) {
    console.log(`Hello ${name}`)
}
function introSelf(name, age){
    var msg = "myName is " + name + " and I'm " + age + " old";
    console.log(msg);
}
module.exports.sayHello = sayHello
module.exports.introSelf = introSelf
/**或
module.exports = {
    sayHello: sayHello,
    introSelf: introSelf
}
*/

//在 b.js 中引入 a 并调用
// b.js
const a = require('./a')
a.sayHello('Lucy') 
a.introSelf('Lucy', 20) 
复制代码
  • 由于 CommonJs 是同步加载的模块的, 在服务端( node ), 文件都在硬盘上, 所以同步加载也无所谓, 但是在浏览器端, 同步加载就体验不好了. 所以 CommonJs 主要使用于 node 环境下.
  • 但是,对于浏览器,这却是个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,如果等待时间过长,浏览器会处于“假死”状态。
  • 因此,浏览器端的模块,不能采用”同步加载”( synchronous ),只能采用”异步加载”( asynchronous )。这就是 AMD 规范诞生的背景。

②AMD 和 CMD

  1. AMDAsynchronous Module Definition 的缩写,意思就是”异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
  2. CMD (Common Module Definition) ,阿里的玉伯提出, 是seajs推崇的规范, CMD 则是依赖就近,用的时候再 require (实现了按需加载)。 模块必须采用特定的 define() 函数来定义。
  3. AMDCMD 模块必须采用特定的define()函数来定义。
define(id?, dependencies?, factory)
    /**
    - id:字符串,模块名称(可选)
    - dependencies: 是我们要载入的依赖模块(可选),
            使用相对路径。,注意是数组格式
    - factory: 工厂方法,返回一个模块函数
    */
复制代码

如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

  1. 对于依赖的模块 AMD 推崇依赖前置(js可以方便知道依赖模块是谁,立即加载),而 CMD 推崇依赖就近(需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病 CMD 的一点)
//AMD2.0之前
 require(['./a', './b'], function(a, b) {
 a.doSomething();
 b.doSomething();
 })
 // AMD2.0之后
 define(['./a', './b'], function(a, b) {
 a.doSomething();
 b.doSomething();
 })
 
 // CMD
 define(function(require, exports, module) {
 var a = require('./a');
 a.doSomething();
 var b = require('./b');
 b.doSomething();
 })
复制代码
  1. 调用模块
require([module], callback);
复制代码
//如有一个math.js
require(['math'], function (math) {
  math.add(2, 3);
});
复制代码

三、ES6 MODULES【编译时加载】

随着ES2015的发布,官方标准定义了一种模块化的方案,那就是import、export。可是,标准毕竟是标准,各大浏览器和node终端要实现标准还是有一段距离的。

  1. 阮一峰ES6-export命令
  • 模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
  • 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量。
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
复制代码
  • 上面代码是profile.js文件,保存了用户信息。ES6 将其视为一个模块,里面用export命令对外部输出了三个变量。
  • export的写法,除了像上面这样,还有另外一种。
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };
复制代码
  • 导入模块
import { firstName, lastName, year } from './profile.js';
//也可以用as
import { lastName as surname } from './profile.js';
复制代码
  • 上面代码在export命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在var语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。
  • export命令除了输出变量,还可以输出函数或类(class)。
export function multiply(x, y) {
  return x * y;
};
复制代码
  • 上面代码对外输出一个函数multiply。
  • 通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。
function v1() { ... }
function v2() { ... }
export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};
复制代码
  • 上面代码使用as关键字,重命名了函数v1和v2的对外接口。重命名后,v2可以用不同的名字输出两次。
  • 需要特别注意的是,export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
// 报错
export 1;

// 报错
var m = 1;
export m;
复制代码
  • 上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出 1,第二种写法通过变量m,还是直接输出 1。1只是一个值,不是接口。正确的写法是下面这样。
// 写法一
export var m = 1;

// 写法二
var m = 1;
export {m};

// 写法三
var n = 1;
export {n as m};
复制代码
  • 上面三种写法都是正确的,规定了对外的接口m。其他脚本可以通过这个接口,取到值1。它们的实质是,在接口名与模块内部变量之间,建立了一一对应的关系。
  • 同样的,function和class的输出,也必须遵守这样的写法。
// 报错
function f() {}
export f;

// 正确
export function f() {};

// 正确
function f() {}
export {f};
复制代码
  1. 阮一峰ES6-export default命令
  • 从前面的例子可以看出,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
  • 为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。
// export-default.js
export default function () {
  console.log('foo');
}
复制代码
  • 上面代码是一个模块文件export-default.js,它的默认输出是一个函数。
  • 其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。
// import-default.js
import customName from './export-default';
customName(); // 'foo'
复制代码
  • 上面代码的import命令,可以用任意名称指向export-default.js输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时import命令后面,不使用大括号。
  • export default命令用在非匿名函数前,也是可以的。
// export-default.js
export default function foo() {
  console.log('foo');
}

// 或者写成

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

export default foo;
复制代码
  • 上面代码中,foo函数的函数名foo,在模块外部是无效的。加载的时候,视同匿名函数加载。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

JavaScript实战手册

JavaScript实战手册

David Sawyer McFarland / 李强 / 机械工业出版社 / 2009 / 89.00元

在《JavaScript实战手册》中,畅销书作者David McFarland教你如何以高级的方式使用JavaScript,即便你只有很少或者没有编程经验。一旦掌握了这种语言的结构和术语,你将学习如何使用高级的JavaScript工具来快速为站点添加有用的交互,而不是一切从头开始编写脚本。和其他的Missing Manuals图书不同,《JavaScript实战手册》清楚、精炼,手把手地讲解。 ......一起来看看 《JavaScript实战手册》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具