如今的前端开发, 不只仅是完成浏览的基本需求,而且一般是一个单页面应用,每个视图经过异步的方式加载,这致使页面初始化和使用过程当中会加载愈来愈多的 JavaScript 代码. 如何在开发环境组织好这些碎片化的代码和资源,而且保证他们在浏览器端快速、优雅的加载和更新,就须要一个模块化系统css
把函数做为模块 缺陷: 污染全局变量 模块成员之间没什么关系 面向对象思想 并使用当即执行函数 实现闭包 避免了变量污染 同时同一模块内的成员也有了关系 在模块外部没法修改咱们没有暴露出来的变量、函数 这就是简单的模块前端
模块的加载和传输,咱们首先能想到两种极端的方式,一种是每一个模块文件都单独请求,另外一种是把全部模块打包成一个文件而后只请求一次。显而易见,每一个模块都发起单独的请求形成了请求次数过多,致使应用启动速度慢;一次请求加载全部模块致使流量浪费、初始化过程慢。这两种方式都不是好的解决方案,它们过于简单粗暴。node
分块传输,按需进行懒加载,在实际用到某些模块的时候再增量更新,才是较为合理的模块加载方案。要实现模块的按需加载,就须要一个对整个代码库中的模块进行静态分析、编译打包的过程。jquery
在上面的分析过程当中,咱们提到的模块仅仅是指JavaScript模块文件。然而,在前端开发过程当中还涉及到样式、图片、字体、HTML 模板等等众多的资源。若是他们均可以视做模块,而且均可以经过require
的方式来加载,将带来优雅的开发体验,那么如何作到让 require
能加载各类资源呢?在编译的时候,要对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,而后将不一样类型的模块提交给适配的加载器来处理。Webpack 就是在这样的需求中应运而生。webpack
<script>
的书写顺序进行加载服务器端的 Node.js 遵循 CommonJS规范,该规范的核心思想是容许模块经过 require
方法来同步加载所要依赖的其余模块,而后经过 exports
或 module.exports
来导出须要暴露的接口。es6
require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;
复制代码
或web
// moduleA.js
module.exports = function( value ){
return value * 2;
}
复制代码
// moduleB.js
var multiplyBy2 = require('./moduleA');
var result = multiplyBy2(4);
复制代码
优势:npm
缺点:gulp
define(id?, dependencies?, factory)
,它要在声明模块的时候指定全部的依赖 dependencies
,而且还要当作形参传到 factory
中,对于依赖的模块提早执行,依赖前置。浏览器
define("module", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });
复制代码
一些用例: 定义一个名为 myModule
的模块,它依赖 jQuery
模块:
define('myModule', ['jquery'], function($) {
// $ 是 jquery 模块的输出
$('body').text('hello world');
});
// 使用
define(['myModule'], function(myModule) {});
复制代码
注意:在 webpack 中,模块名只有局部做用域,在 Require.js 中模块名是全局做用域,能够在全局引用。 定义一个没有 id
值的匿名模块,一般做为应用的启动函数:
define(['jquery'], function($) {
$('body').text('hello world');
});
复制代码
依赖多个模块的定义:
define(['jquery', './math.js'], function($, math) {
// $ 和 math 一次传入 factory
$('body').text('hello world');
});
复制代码
模块输出:
define(['jquery'], function($) {
var HelloWorldize = function(selector){
$(selector).text('hello world');
};
// HelloWorldize 是该模块输出的对外接口
return HelloWorldize;
});
复制代码
在模块定义内部引用依赖:
define(function(require) {
var $ = require('jquery');
$('body').text('hello world');
});
复制代码
优势:
缺点:
4.CMD
define(function(require, exports, module) {
var $ = require('jquery');
var Spinning = require('./spinning');
exports.doSomething = ...
module.exports = ...
})
复制代码
优势:
缺点:
5.UMD
6.ES6模块
ES6 模块的设计思想,是尽可能的静态化,使得编译时就能肯定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时肯定这些东西。
import "jquery";
export function doStuff() {}
module "localModule" {}
复制代码
优势:
缺点:
实现:
从前有两个规范,一个是AMD 一个是CMD RequireJS是AMD规范的实现,SeaJS是CMD规范的实现, 一个主张提早加载依赖,一个主张延迟加载依赖 后来出现了 commonjs规范 webpack就是支持commonjs规范的 目前能够说是主导了前端构建格局。
CommomJS是服务端规范,node就是采用这个规范,他是同步加载,毕竟服务端不用考虑异步。 它是对通用的JavaScript模式的标准化尝试,它包含有 AMD 定义 AMD是异步加载模块的缩写,使用require引入模块,提倡依賴前置。 CMD与AMD其实挺接近的,还由于有sea.js,中文资料仍是比较亲切的。 还有就是AMD和CommomJS的中间者UMD Webpack其实就是一个打包工具,他的思想就是一切皆模块,css是模块,js是模块,图片是模块。而且提供了一些列模块加载(各类-loader)来编译模块。官方推荐使用commonJS规范,可是也支持CMD和AMD
不管是node
应用模块,仍是webpack
配置 ,均是采用CommonJS
模块化规范
Webpack 对 CommonJS 的 AMD 的语法作了兼容, 方便迁移代码
不过实际上, 引用模块的规则是依据 CommonJS 来的
require('lodash') // 从模块目录查找
require('./file') // 按相对路径查找
复制代码
AMD 语法中, 也要注意, 是按 CommonJS 的方案查找的
define (require, exports. module) ->
require('lodash') # commonjs 当中这样是查找模块的
复制代码
在一开始,咱们先讲一下它和以往咱们所用的模块管理工具备什么不同。在最开始的阶段,Js并无这些模块机制,各类Js处处飞,得不到有效妥善的管理。后来前端圈开始制定规范,最耳熟能详的是CommonJs和AMD。
CommonJs是应用在NodeJs,是一种同步的模块机制。它的写法大体以下:
var firstModule = require("firstModule");
//your code...
module.export = anotherModule
复制代码
AMD的应用场景则是浏览器,异步加载的模块机制。require.js的写法大体以下:
define(['firstModule'], function(module){
//your code...
return anotherModule
})
复制代码
其实咱们单比较写法,就知道CommonJs是更为优秀的。它是一种同步的写法,对Human友好,并且代码也不会繁琐臃肿。但更重要的缘由是,随着npm成为主流的JavaScript组件发布平台,愈来愈多的前端项目也依赖于npm上的项目,或者自身就会发布到npm平台。因此咱们对如何可使用npm包中的模块是咱们的一大需求。因此browserify工具就出现了,它支持咱们直接使用require()的同步语法去加载npm模块。
固然咱们这里不得不说的是,ES2015(ES6)里也有了本身的模块机制,也就是说ES6的模块机制是官方规定的,咱们经过babel(一种6to5的编译器)可使用比较多的新特性了,包括咱们提到的模块机制,而它的写法大体以下:
import {someModule} from "someModule";
// your codes...
export anotherModule;
复制代码
固然上面的写法只是最基本的,还有其余的不一样加载模块的写法,能够看一下阮一峰老师的ECMAScript 6 入门或者babel的相关文档Learn ES2015。
browserify的出现很是棒,但webpack更胜一筹!
咱们来看看webpack支持哪些功能特性:
1. export default{
add(){}
}
2. export fucntion add(){} 至关于 将add方法当作一个属性挂在到exports对象
复制代码
若是导出的是:export default{ add(){}}
那么能够经过 import obj from './calc.js'
若是导出的是:
export fucntion add(){}
export fucntion substrict(){}
export const PI=3.14
那么能够经过按需加载 import {add,substrict,PI} from './calc.js'
复制代码
CommonJS - Node.js
var sum = function(a,b){
return parseInt(a) + parseInt(b);
}
// 方法1
// 导出模块成员
exports.sum = sum;
//引入模块
var module = require('./xx.js');
var ret = module.sum(12,13);
// 方法2
// 导出模块成员
module.exports = sum;
//引入模块
var module = require('./xx.js');
module();
// // 方法1
// exports.sum = sum;
// exports.subtract = subtract;
//
// var m = require('./05.js');
// var ret = m.sum(1,2);
// var ret1 = m.subtract(1,2);
// console.log(ret,ret1);
//
// // 方法2
// module.exports = {
// sum : sum,
// subtract : subtract,
// multiply : multiply,
// divide : divide
// }
//
// var m = require('./05.js');
// console.log(m);
复制代码
##回顾webpack
根据模块的依赖关系进行静态分析,而后将这些模块按照指定的规则生成对应的静态资源。如何在一个大规模的代码库中,维护各类模块资源的分割和存放,维护它们之间的依赖关系,而且无缝的将它们整合到一块儿生成适合浏览器端请求加载的静态资源。市面上已经存在的模块管理和打包工具并不适合大型的项目,尤为单页面 Web 应用程序。最紧迫的缘由是如何在一个大规模的代码库中,维护各类模块资源的分割和存放,维护它们之间的依赖关系,而且无缝的将它们整合到一块儿生成适合浏览器端请求加载的静态资源。 这些已有的模块化工具并不能很好的完成以下的目标:
Webapck 和其余模块化工具备什么区别呢?
require("./templates/" + name + ".jade")
。CommonJS和AMD是用于JavaScript模块管理的两大规范,前者定义的是模块的同步加载,主要用于NodeJS;然后者则是异步加载,经过requirejs等工具适用于前端。随着npm成为主流的JavaScript组件发布平台,愈来愈多的前端项目也依赖于npm上的项目,或者 自身就会发布到npm平台。所以,让前端项目更方便的使用npm上的资源成为一大需求。 web开发中经常使用到的静态资源主要有JavaScript、CSS、图片、Jade等文件,webpack中将静态资源文件称之为模块。 webpack是一个module bundler(模块打包工具),其能够兼容多种js书写规范,且能够处理模块间的依赖关系,具备更强大的js模块化的功能。Webpack对它们进行统 一的管理以及打包发布
1. 对 CommonJS 、 AMD 、ES6的语法作了兼容 2. 对js、css、图片等资源文件都支持打包 3. 串联式模块加载器以及插件机制,让其具备更好的灵活性和扩展性,例如提供对CoffeeScript、ES6的支持 4. 有独立的配置文件webpack.config.js 5. 能够将代码切割成不一样的chunk,实现按需加载,下降了初始化时间 6. 支持 SourceUrls 和 SourceMaps,易于调试 7. 具备强大的Plugin接口,大可能是内部插件,使用起来比较灵活 8.webpack 使用异步 IO 并具备多级缓存。这使得 webpack 很快且在增量编译上更加快