Javarscript中模块(module)、加载(load)与捆绑(bundle)详解


Posted in Javascript onMay 28, 2017

JS模块简介

js模块化,简单说就是将系统或者功能分隔成单独的、互不影响的代码片段,经过严格定义接口,使各模块间互不影响,且可以为其他所用。

常见的模块化有,C中的include (.h)文件、java中的import等。

为什么JS需要模块

很显然,没有模块我们也可以实现同样的功能,为什么我们还要使用模块来写js代码呢?下面几点是模块化给我们带来的一些变化:

  • 抽象代码:我们在使用模块来调用一个api时,可以不用知道内部是如何实现的,避免去理解其中复杂的代码;
  • 封装代码:在不需要再次修改代码的前提下,我们可以在模块内部隐藏其具体实现;
  • 复用代码:一些常用的、通用的功能,以模块来实现可以避免过多的重复代码;
  • 管理依赖:可以通过简单的修改依赖项来管理功能的实现,而不需要去重新修改自己内部的代码实现。

ES5及之前的模块系统

在ES5及之前版本,还没有原生的模块语法。不过这并不代表ES5之前,前端没有使用模块。简单介绍两种:IIFE、Revealing Module.

IIFE

Immediately Invoked Function Expression,立即执行函数表达式。

(function(){
  // ...
 })()

看上面的代码,IIFE可以说成是一个在定义的时候就执行的匿名函数。注意函数是先被”()”包起来了,然后后面紧跟”()”表示执行函数。如果是以下代码,将会报错:

function(){
  console.log('test');
 }()
 // => Uncaught SyntaxError: Unexpected token )

这种写法表示,先定义一个匿名函数,然后再去解析”()”。由于在第一行”function”出现在首位,这表明此处定义一个函数,函数后紧跟”()”,此时表示单独解析”()”,就会报出上面的错误信息,因此需要先将函数定义包裹起来。
“(function…)”这种写法表示执行”()”内部代码,并返回该语句执行结果,此处返回结果为该函数,后面紧跟”()”即表示执行该函数。IIFE可以帮助我们做到:

  • 不需要了解具体的代码实现情况下取得想要的效果;
  • 在内部定义的变量不会污染全局作用域。

显而易见,这种编码方式并没有提供良好的机制来解决依赖管理问题。

Revealing Module

根据字面暂解释为揭示模式,与IIFE形式类似,但是提供了一个返回值。方便集中管理公有的api,使模块、公用api更加简洁清晰。

// Expose module as global variable
 var singleton = function(){
  // Inner logic
  function sayHello(){
  console.log('Hello');
  }
  // Expose API
  return {
  sayHello: sayHello
  }
 }()

稍微注意下,上面的代码,我们并没有用”()”去包裹,因为关键字”function”并不在该行的开头。

我们可以像下面这样使用模块api:

// Access module functionality
 singleton.sayHello();
 // => Hello

当然,我们也可以以构造函数形式导出:

// Expose module as global variable
 var Module = function(){
  // Inner logic
  function sayHello(){
  console.log('Hello');
  }
  // Expose API
  return {
  sayHello: sayHello
  }
 }

请注意,上面函数在定义的时候并没有执行。

我们可以这么使用它:

var module = new Module();
module.sayHello(); // => Hello

与IIFE一样,揭示模式并没有提供良好的解决依赖管理的方案。

更多模块化解决方案

ES6或者ES2015,自带原生的模块语法。

在这之前,有以下几种常见的用于模块化的解决方案:

  • AMD
  • CMD
  • CommonJs
  • UMD
  • System.register
  • ES6

AMD

AMD,Asynchronous Module Definition,异步模块定义。AMD形式被用于浏览器端,使用”define”来定义模块依赖:

//Calling define with a dependency array and a factory function
 define(['dep1', 'dep2'], function (dep1, dep2) {
  //Define the module value by returning a value.
  return function () {};
 });

CMD

CMD,Common Module Definition,通用模块定义。该规范由国内大神玉伯提出,与AMD区别在与AMD是依赖关系前置,有该依赖就必须先加载依赖,CMD是按需加载。

// 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()
 ...
 })

CommonJs

CommonJs在Node.js中用的较多,使用”require”来定义依赖,使用”module.exports”来定义模块:

var dep1 = require('./dep1');
 var dep2 = require('./dep2');
 module.exports = function(){
  // ...
 }

UMD

UMD,Universal Module Definition,通用模块定义。可以用于浏览器端与Node.js端:

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
  // AMD. Register as an anonymous module.
   define(['b'], factory);
  } else if (typeof module === 'object' && module.exports) {
  // Node. Does not work with strict CommonJS, but
  // only CommonJS-like environments that support module.exports,
  // like Node.
  module.exports = factory(require('b'));
  } else {
  // Browser globals (root is window)
  root.returnExports = factory(root.b);
  }
 }(this, function (b) {
  //use b in some fashion.
  // Just return a value to define the module export.
  // This example returns an object, but the module
  // can return a function as the exported value.
  return {};
 }));

System.register

System.register方式设计初衷主要是为了在ES5中能够支持ES6模块语法:

import { p as q } from './dep';
 var s = 'local';
 export function func() {
  return q;
 }
 export class C {
 }

ES6 module

ES6中自带原生的模块语法,使用关键字”export”来导出模块的公用api:

// lib.js
 // Export the function
 export function sayHello(){
  console.log('Hello');
 }
 // Do not export the function
 function somePrivateFunction(){
  // ...
 }

以关键字”import”来导入模块:

import { sayHello } from './lib';
 sayHello();
 // => Hello

目前各浏览器对ES6的支持度不一,因此我们现在需要使用编译器,像Babel,来将ES6的代码编译成ES5的形式。

模块加载器

一个模块加载器可以理解模块,并以固定的形式来加载模块。

模块加载器工作在运行时,流程大致如下:

  • 你在浏览器中运行模块加载器;
  • 你告诉模块加载器需要加载哪个主文件;
  • 模块加载器下载并解析主文件;
  • 模块加载器按需加载其他文件。

一些比较常见的模块加载器有:

  • RequireJS:以AMD风格加载模块;
  • SeaJS:以CMD风格加载模块;
  • SystemJS:以AMD, CommonJS, UMD 或者 System.register风格加载模块;
  • jspm:jspm基于SystemJS,是模块加载器,同时也具备浏览器端包管理功能。

模块打包

模块打包可以替换模块加载器。

然而,相比模块加载器,模块打包动作是在编译时运行的:

  • 使用模块打包在编译期生成一个js文件;(例如bundle.js)
  • 在浏览器中加载该文件。

截止目前,比较常用的模块打包方案有以下两种:

  • Browserify:为CommonJS模块打包;
  • Webpack: 为AMD、CommonJS、ES6模块打包。

总结

为了在现代js开发环境中更好的使用这些工具,你首先需要知道模块、模块化解决方案、模块加载、模块打包之前的区别。

模块是一段封装好的代码,可以以公用api形式导出并在其他代码中被加载和调用;

模块化解决方案或者模块化思想,实际含义是定义一个模块的语法。由于定义语法的差异,目前常用的有AMD、CMD、CommonJS、UMD等;

模块加载,在运行期解析和加载模块。常见的有RequireJS、SeaJS、SystemJS和jspm;

模块打包,其替换了模块加载的概念,在编译期间生成一个所有代码整合后的bundle.js文件。常见的有Browserify和Webpack。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家三水点靠木的支持。

Javascript 相关文章推荐
利用javascript移动div层-javascript 拖动层
Mar 22 Javascript
原来Jquery.load的方法可以一直load下去
Mar 28 Javascript
JQuery实现绚丽的横向下拉菜单
Dec 19 Javascript
JavaScript 学习笔记之基础中的基础
Jan 13 Javascript
微信企业号开发之微信考勤Cookies的使用
Sep 11 Javascript
JS从一组数据中找到指定的单条数据的方法
Jun 02 Javascript
AngularJS 应用身份认证的技巧总结
Nov 07 Javascript
AngularJs中 ng-repeat指令中实现含有自定义指令的动态html的方法
Jan 19 Javascript
从零开始学习Node.js系列教程之基于connect和express框架的多页面实现数学运算示例
Apr 13 Javascript
使用vue-cli编写vue插件的方法
Feb 26 Javascript
Javascript call及apply应用场景及实例
Aug 26 Javascript
JavaScript+HTML实现学生信息管理系统
Apr 20 Javascript
js每隔两秒输出数组中的一项(实例)
May 28 #Javascript
javascript 封装Date日期类实例详解
May 28 #Javascript
Vue实现选择城市功能
May 27 #Javascript
使用 Node.js 对文本内容分词和关键词抽取
May 27 #Javascript
vue子组件使用自定义事件向父组件传递数据
May 27 #Javascript
javascript回调函数的概念理解与用法分析
May 27 #Javascript
原生JavaScript实现的简单省市县三级联动功能示例
May 27 #Javascript
You might like
PHP 文件扩展名 获取函数
2009/06/03 PHP
PHP+SQL 注入攻击的技术实现以及预防办法
2011/01/27 PHP
PHP高自定义性安全验证码代码
2011/11/27 PHP
php输出1000以内质数(素数)示例
2014/02/16 PHP
php使用wordwrap格式化文本段落的方法
2015/03/17 PHP
利用PHP fsockopen 模拟POST/GET传送数据的方法
2015/09/22 PHP
PHP filter_var() 函数, 验证判断EMAIL,URL等
2021/03/09 PHP
开发中可能会用到的jQuery小技巧
2014/03/07 Javascript
通过设置CSS中的position属性来固定层的位置
2015/12/14 Javascript
ajax实现动态下拉框示例
2017/01/10 Javascript
微信小程序实战之上拉(分页加载)效果(2)
2017/04/17 Javascript
bootstrap的常用组件和栅格式布局详解
2017/05/02 Javascript
纯js实现动态时间显示
2020/09/07 Javascript
AngularJS 教程及实例代码
2017/10/23 Javascript
Vue resource三种请求格式和万能测试地址
2018/09/26 Javascript
详解vue中async-await的使用误区
2018/12/05 Javascript
微信小程序实现判断是分享到群还是个人功能示例
2019/05/03 Javascript
解决vue侦听器watch,调用this时出现undefined的问题
2020/10/30 Javascript
[54:53]2014 DOTA2国际邀请赛中国区预选赛 LGD-GAMING VS CIS 第二场
2014/05/23 DOTA
python直接访问私有属性的简单方法
2016/07/25 Python
基于python中的TCP及UDP(详解)
2017/11/06 Python
Python实现的拟合二元一次函数功能示例【基于scipy模块】
2018/05/15 Python
Python3非对称加密算法RSA实例详解
2018/12/06 Python
numpy.where() 用法详解
2019/05/27 Python
Python数据结构与算法(几种排序)小结
2019/06/22 Python
python过滤中英文标点符号的实例代码
2019/07/15 Python
Django rstful登陆认证并检查session是否过期代码实例
2019/08/13 Python
Python 复平面绘图实例
2019/11/21 Python
Django中的session用法详解
2020/03/09 Python
Python AutoCAD 系统设置的实现方法
2020/04/01 Python
python多进程使用函数封装实例
2020/05/02 Python
Lancer Skincare官方网站:抗衰老皮肤护理
2020/11/20 全球购物
小学庆六一活动总结
2014/08/28 职场文书
2015年社区服务活动总结
2015/03/25 职场文书
酒店工程部经理岗位职责
2015/04/09 职场文书
Python查找算法的实现 (线性、二分,分块、插值查找算法)
2022/04/24 Python