本文github仓库地址: https://github.com/Rynxiao/webpack-tutorial ,里面包括了本教程的所有代码。
【如果你觉得这篇文章写得不错,麻烦给本仓库一颗星:-D】
webpack is a module bundler.
webpack takes modules with dependencies and generates static assets representing those modules.
简单的概括就是:webpack是一个模块打包工具,处理模块之间的依赖同时生成对应模块的静态资源。
图中已经很清楚的反应了几个信息:
webpack --config webpack.config.js
)npm install -g webpack
npm install webpack
// 处理类似如下调用
import webpack from "webpack";
var webpack = require("webpack");
建议安装淘宝的npm镜像,这样下载npm包会快上很多,具体做法:
// 方式一
npm install xx --registry=https://registry.npm.taobao.org/
// 方式二:安装淘宝提供的npm工具
npm install -g cnpm
cnpm install xx
// 方式三
// 在用户主目录下,找到.npmrc文件,加上下面这段配置
registry=https://registry.npm.taobao.org/
创建配置文件(webpack.config.js
,执行webpack命令的时候,默认会执行这个文件)
module.export = {
entry : 'app.js',
output : {
path : 'assets/',
filename : '[name].bundle.js'
},
module : {
loaders : [
// 使用babel-loader解析js或者jsx模块
{ test : /\.js|\.jsx$/, loader : 'babel' },
// 使用css-loader解析css模块
{ test : /\.css$/, loader : 'style!css' },
// or another way
{ test : /\.css$/, loader : ['style', 'css'] }
]
}
};
说明一: webpack.config.js
默认输出一个webpack
的配置文件,与CLI
方式调用相同,只是更加简便
说明二: 执行webpack
命令即可以运行配置,先决条件,全局安装webpack
,项目安装各模块loader
说明三: entry
对应需要打包的入口js
文件,output
对应输出的目录以及文件名,module
中的loaders
对应解析各个模块时需要的加载器
一个简单的例子
basic/app.js
require('./app.css');
document.getElementById('container').textContent = 'APP';
basic/app.css
* {
margin: 0;
padding: 0;
}
#container {
margin: 50px auto;
width: 50%;
height: 200px;
line-height: 200px;
border-radius: 5px;
box-shadow: 0 0 .5em #000;
text-align: center;
font-size: 40px;
font-weight: bold;
}
basic/webpack.config.js
/**
* webpack打包配置文件
*/
module.exports = {
// 如果你有多个入口js,需要打包在一个文件中,那么你可以这么写
// entry : ['./app1.js', './app2.js']
entry : './app.js',
output : {
path : './assets/',
filename : '[name].bundle.js'
},
module : {
loaders : [
{ test : /\.js$/, loader : 'babel' },
{ test : /\.css$/, loader : 'style!css' }
]
}
};
basic/index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>basic webpacktitle>
head>
<body>
<div id="container">div>
<script src="./assets/main.bundle.js">script>
body>
html>
在basic
文件夹执行webpack
,打包信息如下
生成main.bundle.js
文件,chunk
名称为main
,也是webpack
默认生成的chunk
名
4.1.1webpack
的多入口配置
上例的简单配置中,只有一个入口文件,那么如果对应于一个页面需要加载多个打包文件或者多个页面想同时引入对应的打包文件的时候,应该怎么做?
entry : {
app1 : './app1.js',
app2 : './app2.js'
}
在multi-entry
文件夹执行webpack
,打包信息如下
可见生成了两个入口文件,以及各自对应的chunk
名
4.2.1 output.publicPath
output: {
path: "/home/proj/cdn/assets/[hash]",
publicPath: "http://cdn.example.com/assets/[hash]/"
}
引用一段官网的话:
The publicPath specifies the public URL address of the output files when referenced in a browser. For loaders that embed
or
tags or reference assets like images, publicPath is used as the href or url() to the file when it’s different then their location on disk (as specified by path).
大致意思就是:publicPath
指定了你在浏览器中用什么地址来引用你的静态文件,它会包括你的图片、脚本以及样式加载的地址,一般用于线上发布以及CDN部署的时候使用。
比如有下面一段配置:
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry : './app.js',
output : {
path : './assets/',
filename : '[name].bundle.js',
publicPath : 'http://rynxiao.com/assets/'
},
module : {
loaders : [
{ test : /\.js$/, loader : 'babel' },
{ test : /\.css$/, loader : 'style!css' }
]
},
plugins : [
new HtmlWebpackPlugin({
filename: './index-release.html',
template: path.resolve('index.template'),
inject: 'body'
})
]
};
其中我将publicPath
设置成了http://rynxiao.com/assets/
,其中设置到了插件的一些东西,这点下面会讲到,总之这个插件的作用是生成了上线发布时候的首页文件,其中script
中引用的路径将会被替换。如下图:
4.2.2 output.chunkFilename
各个文件除了主模块以外,还可能生成许多额外附加的块,比如在模块中采用代码分割就会出现这样的情况。其中chunkFilename
中包含以下的文件生成规则:
[id] 会被对应块的id替换.
[name] 会被对应块的name替换(或者被id替换,如果这个块没有name).
[hash] 会被文件hash替换.
[chunkhash] 会被块文件hash替换.
例如,我在output中如下设置:
output : {
path : './assets/',
filename : '[name].[hash].bundle.js',
chunkFilename: "chunk/[chunkhash].chunk.js"
}
同时我修改了一下basic/app.js
中的文件
require('./app.css');
require.ensure('./main.js', function(require) {
require('./chunk.js');
});
document.getElementById("container").textContent = "APP";
其中对应的chunk.js
就会生成带有chunkhash
的chunk
文件,如下图:
这在做给文件打版本号的时候特别有用,当时如何进行hash
替换,下面会讲到
4.2.3 output.library
这个配置作为库发布的时候会用到,配置的名字即为库的名字,通常可以搭配libraryTarget
进行使用。例如我给basic/webpack.config.js
加上这样的配置:
output : {
// ...
library : 'testLibrary'
// ...
}
那么实际上生成出来的main.bundle.js
中会默认带上以下代码:
var testLibrary = (//....以前的打包生成的代码);
// 这样在直接引入这个库的时候,就可以直接使用`testLibrary`这个变量
4.2.4 output.libraryTarget
规定了以哪一种方式输出你的库,比如:amd/cmd/或者直接变量,具体包括如下
"var"
- 以直接变量输出(默认library方式) var Library = xxx (default)
"this"
- 通过设置this
的属性输出 this["Library"] = xxx
"commonjs"
- 通过设置exports
的属性输出 exports["Library"] = xxx
"commonjs2"
- 通过设置module.exports
的属性输出 module.exports = xxx
"amd"
- 以amd方式输出
"umd"
- 结合commonjs2/amd/root
例如我以umd
方式输出,如图:
loader
中!
代表的含义
require("!style!css!less!bootstrap/less/bootstrap.less");
// => the file “bootstrap.less” in the folder “less” in the “bootstrap”
// module (that is installed from github to “node_modules”) is
// transformed by the “less-loader”. The result is transformed by the
// “css-loader” and then by the “style-loader”.
// If configuration has some transforms bound to the file, they will not be applied.
代表加载器的流式调用,例如:
{ test : /\.css|\.less$/, loader : 'style!css!less' }
就代表了先使用less加载器来解释less文件,然后使用css加载器来解析less解析后的文件,依次类推
loaders
中的include
与exclude
include
表示必须要包含的文件或者目录,而exclude
的表示需要排除的目录
比如我们在配置中一般要排除node_modules
目录,就可以这样写
{
test : /\.js$/,
loader : 'babel',
exclude : nodeModuleDir
}
官方建议:优先采用include,并且include最好是文件目录
module.noParse
使用了noParse
的模块将不会被loaders
解析,所以当我们使用的库如果太大,并且其中不包含require
、define
或者类似的关键字的时候(因为这些模块加载并不会被解析,所以就会报错),我们就可以使用这项配置来提升性能。
例如下面的例子:在basic/
目录中新增no-parse.js
var cheerio = require('cheerio');
module.exports = function() {
console.log(cheerio);
}
webpack.config.js
中新增如下配置:
module : {
loaders : [
{ test : /\.js$/, loader : 'babel' },
{ test : /\.css$/, loader : 'style!css' }
],
noParse : /no-parse.js/
}
当执行打包后,在浏览器中打开index.html
时,就会报错require is not defined
resolve.alias
为模块设置别名,能够让开发者指定一些模块的引用路径。对一些经常要被import或者require的库,如react,我们最好可以直接指定它们的位置,这样webpack可以省下不少搜索硬盘的时间。
例如我们修改basic/app.js
中的相关内容:
var moment = require("moment");
document.getElementById("container").textContent = moment().locale('zh-cn').format('LLLL');
加载一个操作时间的类库,让它显示当前的时间。使用webpack --profile --colors --display-modules
执行配置文件,得到如下结果:
其中会发现,打包总共生成了104个隐藏文件,其中一半的时间都在处理关于moment
类库相关的事情,比如寻找moment
依赖的一些类库等等。
在basic/webpack.config.js
加入如下配置,然后执行配置文件
resolve : {
alias : {
moment : 'moment/min/moment-with-locales.min.js'
}
}
有没有发现打包的时间已经被大大缩短,并且也只产生了两个隐藏文件。
配合module.noParse
使用
module.noParse
参看上面的解释
noParse: [/moment-with-locales/]
执行打包后,效果如下:
是不是发现打包的时间进一步缩短了。
配合externals
使用
externals
参看下面的解释
Webpack 是如此的强大,用其打包的脚本可以运行在多种环境下,Web 环境只是其默认的一种,也是最常用的一种。考虑到 Web 上有很多的公用 CDN 服务,那么 怎么将 Webpack 和公用的 CDN 结合使用呢?方法是使用 externals 声明一个外部依赖。
externals: {
moment: true
}
当然了 HTML 代码里需要加上一行