Webpack 是当下最热门的前端资源模块化管理和打包工具,它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。
一.安装
可以通过npm来安装,全局安装:
$ npm install -g webpack
项目根目录下安装,通过cmd环境进入到项目根目录下:
$ npm install --save-dev webpack
或安装镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install --save-dev webpack
(注)此时在项目根目录会出现一个package.json文件
二.配置文件
在项目根目录下新增一个webpack.config.js文件,内容如下:
var webpack = require('webpack');
module.exports = {
//根目录
context: __dirname + "",
//文件入口
entry: {
index: './js/main.js',
other: './js/other.js'
},
//文件出口
output: {
path: __dirname + "/dist", // 设置输出目录
filename: '[name].bundle.js', // 输出文件名
},
module: {
//加载器配置
loaders: [
//.css文件使用 style-loader 和 css-loader 来处理
//【$ npm install css-loader style-loader --save-dev】命令行表示初始化style-loader, css-loader
{ test: /\.css$/, loader: "style!css" },
//图片文件使用 url-loader来处理,小于8kb的直接转为base64
//【$ npm install url-loader --save-dev】
//【$ npm install file-loader --save-dev】
{ test: /\.(jpeg|png|gif|svg)$/i, loader: 'url-loader?limit=8192&name=[name].[ext]' },
//字体文件使用 url-loader来处理,小于8kb的直接转为base64
//{ test: /\.(woff|woff2|eot|ttf|otf)$/i, loader: 'url-loader?limit=8192&name=[name].[ext]' },
//.js文件使用 babel-loader 来处理
//【$ npm install babel-loader】
//{ test: /\.js$/, loader: 'babel-loader' , exclude: /node_modules/},
//.scss 文件使用 style-loader、css-loader 和 sass-loader 来处理
//{ test: /\.js?$/, loader: ['react-hot-loader', 'babel-loader'], exclude: /node_modules/ },
//{ test: /\.scss$/, loader: 'style!css!sass?sourceMap' },
//.less文件使用 style-loader、css-loader 和 less-loader来处理
//{ test: /\.less$/, loader: "style!css!less" },
//.jsx文件使用 babel-loader来处理
//{ test: /\.jsx$/, loader: ['babel-loader'] , exclude: /(node_modules|bower_components)/}
//【$ npm install --save-dev react-hot-loader】
//【$ npm install --save-dev webpack-dev-server】
//【$ npm install less --save-dev】命令行表示初始化less
//【$ npm install css-loader style-loader --save-dev】命令行表示初始化style-loader, css-loader
//【$ npm install less less-loader --save-dev】命令行表示初始化less-loader, 基于style-loader,css-loader
]
},
// 插件
plugins: [
// 默认会把所有入口节点的公共代码提取出来,生成一个common.js
new webpack.optimize.CommonsChunkPlugin('common'),
// 压缩代码抬头注释
new webpack.BannerPlugin('此处添加打包抬头注释!'),
// 代码压缩
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
],
//其它解决方案配置
resolve: {
// 配置简写,配置过后,书写该文件路径的时候可以省略文件后缀。如require("common")
extensions: ['.js', '.jsx', '.coffee']
},
//由于压缩后的代码不易于定位错误,配置该项后发生错误时即可采用source-map的形式直接显示你出错代码的位置。
devtool: 'eval-source-map'
}
该文件里已经做了相关注释,主要想说的一点是文件入口entry配置,如果你只有一个文件入口,则直接用
entry: "./js/main.js"
本例中我使用了两个文件入口文件,分别是main.js和other.js。这里项目已经搭建完成,项目整体目录结构如下:
(注)libs目录下的文件自行搜索即可找到
三.代码模块
此例中新建了2个模块,分别是MyFunction.js和MyObject.js这两个文件代码如下:
MyFunction.js:
var content = new Object();
content.showContent = function() {
console.log("This is my Function!");
}
exports = module.exports = content;
MyObject.js:
function content() {
var showContent = function() {
console.log("This is my Object!");
}
var object = new Object();
object.showContent = showContent;
return object;
}
exports = module.exports = content;
(注)这两个模块代码也展示了两种不同创建模块js写法的规范此时编辑主程main.js文件:
$(document).ready(function() {
console.log("Test Start---------------------");
async.auto({
//执行代码 [创建型对象]
t1: function(callback) {
var MyObject = require("./core/MyObject.js");
var myObject = new MyObject();
myObject.showContent();
callback(null);
},
//执行代码 [方法型对象]
t2: function(callback) {
var MyFunction = require("./core/MyFunction.js");
MyFunction.showContent();
callback(null);
},
//执行代码 [图片型对象]
t3: function(callback) {
$("#img").attr("src", "./js/assets/img.png");
callback(null);
},
//执行代码 [解析JSON数据]
t4: function(callback) {
$.getJSON("./js/assets/data.json", function(data) {
console.log("this is type of " + typeof data);
callback(null);
});
},
//执行代码 [AJAX请求同域数据]
t5: ["t4", function(callback, results) {
$.ajax({
url: "./js/assets/data.txt",
async: false,
error: function(response, state) {
console.log("ajax_error:" + response);
callback(null);
},
success: function(response, state) {
console.log("ajax_ok:" + response);
callback(null);
}
});
}],
access: ["t1", "t2", "t3", "t4", "t5",
function(callback, results) {
callback(null);
}
]
}, function(error, results) {
console.log("Test End-----------------------");
});
});
该主程文件用到了async异步执行,这样就可以更清晰的看到打印结果。
四.打包展示
在cmd环境下进入到你的项目根目录,执行打包命令:
$ webpack -d
(注)webpack还有其他的一些打包和编译命令,可以自己搜索一下,此处不再介绍
打包成功后的显示如下:此时会在项目根目录下新增了一个dist文件夹,里面有三个文件,分别为common.bundle.js、index.bundle.js和other.bundle.js,其中common.bundle.js表示两入口文件共有模块,我们现在只测其中一个入口文件即可,在index.html将打包文件引入即可展示执行代码。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="js/libs/async.js"></script>
<script src="js/libs/jquery-1.8.0.min.js"></script>
<script src="dist/common.bundle.js"></script>
<script src="dist/index.bundle.js"></script>
</head>
<body>
<img id="img" />
</body>
</html>
执行结果:
要记住一点,Webpack虽然都支持CommonJS规范和AMD规范,但是最终的主程序是打包到同一个bundle.js文件中,这就意味着bundle.js文件有可能较大,此时若将Webpack应用于浏览器环境而不是服务端环境时,由于同步加载原因就有可能引起浏览器加载过程较慢的情况发生。此时的决策是(1)在webpack.config.js配置项中plugins除去不必要的插件;(2)压缩代码,webpack 自带了一个压缩插件 UglifyJsPlugin,只需要在配置文件中引入即可。
{
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
}
加入了这个插件之后,编译的速度会明显变慢,所以一般只在生产环境启用。
模块化规范
在描述Webpack中有说到,它都支持CommonJS规范和AMD规范,在Web前端中,模块化规范主要有3种,分别是CommonJS规范、AMD规范和CMD规范,而后两种规范都是在CommonJS规范的基础上发展而来。
CommonJS规范
commonJS规范主要的标志是定义了require和exports或module.exports的写法实现代码模块化管理。CommonJS是在浏览器环境之外构建JavaScript生态系统为目标产生的项目,比如服务器和桌面环境中。CommonJS规范是为了解决JavaScript的作用域问题而定义的模块形式,可以使每个模块在它自身的命名空间中执行。该规范的主要内容是:模块必须通过 module.exports导出对外的变量或接口,通过require()来导入其他模块的输出到当前模块。写法如下:
// moduleA.js
module.exports = function( value ){
return value * 2;
}
// moduleB.js
var multiplyBy2 = require('./moduleA');
var result = multiplyBy2(4);
CommonJS是同步加载模块, 服务器端的Node.js遵循CommonJS规范。核心思想是允许模块通过require 方法来同步加载所要依赖的其他模块,然后通过 exports或module.exports来导出需要暴露的接口。可以说当初定义CommonJS规范的主要目的是应用在服务端。
(注)应用实现如Node.js
AMD规范
AMD规范是在CommonJS规范的基础上应用而来,因为CommonJS规范是同步执行,不利于在浏览器上执行,所以AMD规范应运而生。AMD规范的主要标志是定义了define写法,是异步加载模块。AMD规范其实只有一个主要接口 define(id,dependencies,factory),它要在声明模块的时候指定所有的依赖dependencies,并且还要当做形参传到factory中,对于依赖的模块提前执行,依赖前置。
define("module", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});
require(["module"], function(module) { /* ... */ });
(注)应用实现如require.js框架
CMD规范
CMD规范与AMD规范几乎一样,唯一不同的是在定义模块时不需要知道其依赖条件,即无依赖前置一说,在需要调用其他模块时直接require进来就可以使用,即遵循就近依赖,延时执行的原则。
define(function(require, exports, module) {
var $ = require('jquery');
var Spinning = require('./spinning');
exports.doSomething = ...
module.exports = ...
})
(注)应用实现如seajs.js框架
RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。SeaJS 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 服务器端。据统计,针对模块化框架的使用情况,国外较喜欢使用AMD规范,而国内则较多使用CMD规范,毕竟CMD规范是从国内发展而来。
实例Demo:http://download.csdn.net/detail/zeping891103/9796332