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 相关文章推荐
ppk谈JavaScript style属性
Oct 10 Javascript
js中的referrer返回上一页使用介绍
Sep 26 Javascript
jquery datepicker参数介绍和示例
Apr 15 Javascript
node.js中的events.emitter.once方法使用说明
Dec 10 Javascript
JS实现超精简的链接列表在固定区域内滚动效果代码
Nov 04 Javascript
BootStrap Validator对于隐藏域验证和程序赋值即时验证的问题浅析
Dec 01 Javascript
jQuery实现倒计时重新发送短信验证码功能示例
Jan 12 Javascript
Bootstrap常用组件学习(整理)
Mar 24 Javascript
mockjs,json-server一起搭建前端通用的数据模拟框架教程
Dec 18 Javascript
element-ui的回调函数Events的用法详解
Oct 16 Javascript
微信jssdk踩坑之签名错误invalid signature
May 19 Javascript
解决vue axios跨域 Request Method: OPTIONS问题(预检请求)
Aug 14 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执行速度全攻略(下)
2006/10/09 PHP
使用sockets:从新闻组中获取文章(二)
2006/10/09 PHP
DEDE采集大师官方留后门的删除办法
2011/01/08 PHP
php5 apache 2.2 webservice 创建与配置(java)
2011/01/27 PHP
解析在apache里面给php写虚拟目录的详细方法
2013/06/24 PHP
PHP分页类集锦
2014/11/18 PHP
php实现的简单数据库操作Model类
2016/11/16 PHP
Smarty模板配置实例简析
2019/07/20 PHP
将list转换为json失败的原因
2013/12/17 Javascript
JQUERY 设置SELECT选中项代码
2014/02/07 Javascript
js实现正则匹配中文标点符号的方法
2015/12/23 Javascript
jQuery Easyui实现左右布局
2016/01/26 Javascript
JS实现的手机端精简幻灯片效果
2016/09/05 Javascript
微信小程序 简单教程实例详解
2017/01/13 Javascript
JavaScript调用模式与this关键字绑定的关系
2018/04/21 Javascript
浅谈vue项目如何打包扔向服务器
2018/05/08 Javascript
详解微信小程序调起键盘性能优化
2018/07/24 Javascript
vue中格式化时间过滤器代码实例
2019/04/17 Javascript
iview form清除校验状态的实现
2019/09/19 Javascript
nodejs处理tcp连接的核心流程
2021/02/26 NodeJs
[03:39]DOTA2英雄梦之声_第05期_幽鬼
2014/06/23 DOTA
[45:56]Ti4正赛第一天 VG vs NEWBEE 3
2014/07/19 DOTA
python的常见命令注入威胁
2013/02/18 Python
Python 字典与字符串的互转实例
2017/01/13 Python
Python实现加载及解析properties配置文件的方法
2018/03/29 Python
pandas string转dataframe的方法
2018/04/11 Python
python 串行执行和并行执行实例
2020/04/30 Python
PyTorch实现重写/改写Dataset并载入Dataloader
2020/07/14 Python
CSS3 :not()选择器实现最后一行li去除某种css样式
2016/10/19 HTML / CSS
阿玛尼美妆加拿大官方商城:Giorgio Armani Beauty加拿大
2017/10/24 全球购物
飞利浦法国官网:Philips法国
2019/07/10 全球购物
Java 中访问数据库的步骤?Statement 和PreparedStatement 之间的区别?
2012/06/05 面试题
生产厂厂长岗位职责
2013/12/25 职场文书
绿化工程实施方案
2014/03/17 职场文书
华山导游词
2015/02/03 职场文书
python实现socket简单通信的示例代码
2021/04/13 Python