声明 本教程整理于互联网
webpack是当下最热门的前端资源模块化管理和打包工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,到实际需要的时候再异步加载。通过loader的转换,任何形式的资源都可以视作模块,比如CommonJs模块、AMD模块、ES6模块、CSS、图片、JSON、Coffeescript、 LESS等
# 一、模块系统的演进
-
模块系统主要解决模块的定义、依赖和导出,先来看看已经存在的模块系统
-
<script>标签
<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
<script src="module3.js"></script>
-
这是最原始的
JavaScript文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在window对象中,不同模块的接口调用都是一个作用域中,一些复杂的框架,会使用命名空间的概念来组织这些模块的接口,典型的例子如YUI库 -
这种原始的加载方式暴露了一些显而易见的弊端
- 全局作用域下容易造成变量冲突
- 文件只能按照
<script>的书写顺序进行加载 - 开发人员必须主观解决模块和代码库的依赖关系
- 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪
# 1.1 CommonJS
- 服务器端的
Node.js遵循CommonJS规范,该规范的核心思想是允许模块通过require方法来同步加载所要依赖的其他模块,然后通过exports或module.exports来导出需要暴露的接口
require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;
-
优点:
- 服务器端模块便于重用
NPM中已经有将近20万个可以使用模块包- 简单并容易使用
-
缺点:
- 同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的
- 不能非阻塞的并行加载多个模块
-
实现
-
服务器端的
Node.js -
Browserify,浏览器端的CommonJS实现,可以使用NPM的模块,但是编译打包后的文件体积可能很大 -
modules-webmake,类似Browserify,还不如Browserify灵活 -
wreq,Browserify的前身
# 1.2 AMD
Asynchronous Module Definition规范其实只有一个主要接口define(id?, dependencies?,factory),它要在声明模块的时候指定所有的依赖dependencies,并且还要当做形参传到factory中,对于依赖的模块提前执行,依赖前置
define("module", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});require(["module", "../file"], function(module, file) { /* ... */ });
-
优点:
- 适合在浏览器环境中异步加载模块
- 可以并行加载多个模块
-
缺点:
- 提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义 不顺畅
- 不符合通用的模块化思维方式,是一种妥协的实现
-
实现:
RequireJScurl
# 1.3 CMD
Common Module Definition规范和AMD很相似,尽量保持简单,并与CommonJS和Node.js的Modules规范保持了很大的兼容性
define(function(require, exports, module) {
var $ = require('jquery');
var Spinning = require('./spinning');
exports.doSomething = ...
module.exports = ...
})
- 优点:
- 依赖就近,延迟执行
- 可以很容易在
Node.js中运行
- 缺点:
- 依赖
SPM打包,模块的加载逻辑偏重
- 依赖
- 实现:
Sea.jscoolie
# 1.4 ES6 模块
EcmaScript6标准增加了JavaScript语言层面的模块体系定义。ES6模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西
import "jquery";
export function doStuff() {}
module "localModule" {}
- 优点:
- 容易进行静态分析
- 面向未来的
EcmaScript标准
- 缺点:
- 原生浏览器端还没有实现该标准
- 全新的命令字,新版的
Node.js才支持
- 实现:
Babel
# 1.5 前端模块加载
- 前端模块要在客户端中执行,所以他们需要增量加载到浏览器中
- 模块的加载和传输,我们首先能想到两种极端的方式,一种是每个模块文件都单独请求,另一种是把所有模块打包成一个文件然后只请求一次。显而易见,每个模块都发起单独的请求造成了请求次数过多,导致应用启动速度慢;一次请求加载所有模块导致流量浪费、初始化过程慢。这两种方式都不是好的解决方案,它们过于简单粗暴
- 分块传输,按需进行懒加载,在实际用到某些模块的时候再增量更新,才是较为合理的模块加载方案
- 要实现模块的按需加载,就需要一个对整个代码库中的模块进行静态分析、编译打包的过 程
# 1.6 所有资源都是模块
在上面的分析过程中,我们提到的模块仅仅是指
JavaScript模块文件。然而,在前端开发过程中还涉及到样式、图片、字体、HTML模板等等众多的资源。这些资源还会以各种方言的形式存在,比如coffeescript、less、sass、众多的模板库、多语言系统(i18n)等
- 如果他们都可以视作模块,并且都可以通过
require的方式来加载,将带来优雅的开发体验,比如
require("./style.css");
require("./style.less");
require("./template.jade");
require("./image.png");
- 那么如何做到让
require能加载各种资源呢?
# 1.7 静态分析
在编译的时候,要对整个代码进行静态分析,分析出各个模块的类型和它们依赖关系,然后将不同类型的模块提交给适配的加载器来处理。比如一个用
LESS写的样式模块,可以先用LESS加载器将它转成一个CSS模块,在通过CSS模块把他插入到页面的<style>标签中执行。Webpack就是在这样的需求中应运而生
# 二、webpack基础知识
# 2.1 什么是 Webpack
Webpack是一个模块打包器。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源
# 2.2 Webpack 的特点
-
代码拆分
Webpack有两种组织模块依赖的方式,同步和异步。异步依赖作为分割点,形成一个新的块。在优化了依赖树后,每一个异步区块都作为一个文件被打包
-
Loader
Webpack本身只能处理原生的JavaScript模块,但是loader转换器可以将各种类型的资源转换成JavaScript模块。这样,任何资源都可以成为Webpack可以处理的模块
-
智能解析
Webpack有一个智能解析器,几乎可以处理任何第三方库,无论它们的模块形式是CommonJS、AMD还是普通的JS文件。甚至在加载依赖的时候,允许使用动态表达式require("./templates/" + name + ".jade")
-
插件系统
Webpack还有一个功能丰富的插件系统。大多数内容功能都是基于这个插件系统运行的,还可以开发和使用开源的Webpack插件,来满足各式各样的需求
-
快速运行
Webpack使用异步I/O和多级缓存提高运行效率,这使得Webpack能够以令人难以置信的速度快速增量编译
# 2.3 总览
- 他的目的就是把有依赖关系的各种文件打包成一系列的静 态资源
webpack简单点来说就就是一个配置文件,所有的魔力都是在这一个文件中发生的。 这个配置文件主要分 为三大块entry入口文件 让webpack用哪个文件作为项目的入口output出口 让webpack把处理完成的文件放在哪里module模块 要用什么不同的模块来处理各种类型的文件

# 2.4 安装
- 先装好node和npm,因为webpack是一个基于node的项目。然后
npm install -g webpack
- 此时
Webpack已经安装到了全局环境下,可以通过命令行webpack -h试试 - 通常我们会将
Webpack安装到项目的依赖中,这样就可以使用项目本地版本的Webpack
# 进入项目目录
# 确定已经有 package.json,没有就通过 npm init 创建
# 安装 webpack 依赖
$ npm install webpack --save-dev
Webpack目前有两个主版本,一个是在 master 主干的稳定版,一个是在webpack-2分支的测试版,测试版拥有一些实验性功能并且和稳定版不兼容,在正式项目中应该使用稳定版
# 查看 webpack 版本信息
$ npm info webpack
# 安装指定版本的 webpack
$ npm install webpack@1.12.x --save-dev
- 如果需要使用
Webpack开发工具,要单独安装
$ npm install webpack-dev-server --save-dev
# 2.5 建立一个项目
mkdir webpack
cd webpack
npm init
- 如果你使用git管理你的这个项目的话,建议你新建一个
.gitignore文件,不要让git提交一些node依赖的模 块 - 创建一个静态页面
index.html和一个JS入口文件entry.js:
<!-- index.html -->
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
// entry.js
document.write('It works.')
- 然后编译
entry.js并打包到bundle.js:
$ webpack entry.js bundle.js
- 打包过程会显示日志:
- 用浏览器打开
index.html将会看到It works. - 接下来添加一个模块
module.js并修改入口entry.js:
// module.js
module.exports = 'It works from module.js.'
# 三、Loader
安装 loader: