前端模块化总结

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

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

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

前端模块化总结

一、 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,在模块外部是无效的。加载的时候,视同匿名函数加载。

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

查看所有标签

猜你喜欢:

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

The Black Box Society

The Black Box Society

Frank Pasquale / Harvard University Press / 2015-1-5 / USD 35.00

Every day, corporations are connecting the dots about our personal behavior—silently scrutinizing clues left behind by our work habits and Internet use. The data compiled and portraits created are inc......一起来看看 《The Black Box Society》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具