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 相关文章推荐
非常好的js代码
Jun 27 Javascript
jquery.lazyload  实现图片延迟加载jquery插件
Feb 06 Javascript
jQuery学习笔记(2)--用jquery实现各种模态提示框代码及项目构架
Apr 08 Javascript
使用JS 清空File控件的路径值
Jul 08 Javascript
JS+CSS设置img在DIV中只显示Img垂直居中的部分
Oct 24 Javascript
jquery专业的导航菜单特效代码分享
Aug 29 Javascript
AngularJS中的表单简单入门
Jul 28 Javascript
JS实现关键词高亮显示正则匹配
Jun 22 Javascript
element-ui 时间选择器限制范围的实现(随动)
Jan 09 Javascript
jQuery简单实现根据日期计算星期几的方法
Jan 09 jQuery
JavaScript实现的拼图算法分析
Feb 13 Javascript
使用Vue开发自己的Chrome扩展程序过程详解
Jun 21 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
一个查看session内容的函数
2006/10/09 PHP
PHP校验ISBN码的函数代码
2011/01/17 PHP
浅析SVN常见问题及解决方法
2013/06/21 PHP
解决file_get_contents无法请求https连接的方法
2013/12/17 PHP
php编写的抽奖程序中奖概率算法
2015/05/14 PHP
[原创]CI(CodeIgniter)简单统计访问人数实现方法
2016/01/19 PHP
学习PHP的数组总结【经验】
2016/05/05 PHP
php中关于长度计算容易混淆的问题分析
2016/05/27 PHP
PHP实现的简单组词算法示例
2018/04/10 PHP
jquery validate使用攻略 第四步
2010/07/01 Javascript
jQuery中get和post方法传值测试及注意事项
2014/08/08 Javascript
javascript快速排序算法详解
2014/09/17 Javascript
使用jQuery实现验证上传图片的格式与大小
2014/12/03 Javascript
jQuery控制网页打印指定区域的方法
2015/04/07 Javascript
JS实现动态表格的添加,修改,删除功能(推荐)
2016/06/15 Javascript
Vue2.0 事件的广播与接收(观察者模式)
2018/03/14 Javascript
微信小程序自定义tabBar组件开发详解
2020/09/24 Javascript
vue实现网络图片瀑布流 + 下拉刷新 + 上拉加载更多(步骤详解)
2020/01/14 Javascript
python基础教程之python消息摘要算法使用示例
2014/02/10 Python
python操作日期和时间的方法
2014/03/11 Python
Python入门之modf()方法的使用
2015/05/15 Python
python遍历 truple list dictionary的几种方法总结
2016/09/11 Python
Python3 socket同步通信简单示例
2017/06/07 Python
TensorFlow实现MLP多层感知机模型
2018/03/09 Python
致Python初学者 Anaconda入门使用指南完整版
2018/04/05 Python
如何使用Python实现斐波那契数列
2019/07/02 Python
Kali Linux安装ipython2 和 ipython3的方法
2019/07/11 Python
Python从文件中读取指定的行以及在文件指定位置写入
2019/09/06 Python
python 统计文件中的字符串数目示例
2019/12/24 Python
python爬取招聘要求等信息实例
2020/11/20 Python
使用CSS3代码绘制可爱的Hello Kitty猫
2016/08/03 HTML / CSS
办公自动化专业大学生职业规划书
2014/03/06 职场文书
社区文化建设方案
2014/05/02 职场文书
2016新党章学习心得体会
2016/01/15 职场文书
PostgreSQL并行计算算法及参数强制并行度设置方法
2022/04/06 PostgreSQL
postgreSQL数据库基础知识介绍
2022/04/12 PostgreSQL