安装webpack
本地安装
npm install --save-dev webpack
npm install --save-dev webpack@<version>
对于大多数项目,我们建议本地安装。这可以使我们在引入破坏式变更(breaking change)的依赖时,更容易分别升级项目。通常,webpack 通过运行一个或多个 npm scripts,会在本地 node_modules 目录中查找安装的 webpack:
"scripts":{
"start":"webpack --config webpack.config.js"
}
全局安装
npm install --global webpack
最新体验版本
npm install webpack@beta
npm install webpack/webpack
安装这些最新体验版本时要小心!它们可能仍然包含 bug,因此不应该用于生产环境。
起步
创建目录,初始化npm ,和在本地安装webpack
mkdir webpack-demo && cd webpack-demo
npm init -y
npm install --save-dev webpack
Lodash
有多年开发经验的工程师,往往都会有自己的一套工具库,称为 utils、helpers 等等,这套库一方面是自己的技术积累,另一方面也是对某项技术的扩展,领先于技术规范的制定和实现。
Lodash 就是这样的一套工具库,它内部封装了诸多对字符串、数组、对象等常见数据类型的处理函数,其中部分是目前 ECMAScript 尚未制定的规范,但同时被业界所认可的辅助函数。目前每天使用 npm 安装 Lodash 的数量在百万级以上,这在一定程度上证明了其代码的健壮性,值得我们在项目中一试。
安装
npm install --save lodash
管理资源
加载css
为了从 JavaScript 模块中 import 一个 CSS 文件,你需要在 module 配置中 安装并添加 style-loader 和 css-loader:
npm install --save-dev style-loader css-loader
修改webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module:{
rules:[
{
test:/\.css$/,
use:[
'style-loader',
'css-loader'
]
}
]
}
};
现在我们可以使用import './style.css'
这种代码了。
加载图片
假想,现在我们正在下载 CSS,但是我们的背景和图标这些图片,要如何处理呢?使用 file-loader,我们可以轻松地将这些内容混合到 CSS 中:
npm install --save-dev file-loader
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
+ {
+ test: /\.(png|svg|jpg|gif)$/,
+ use: [
+ 'file-loader'
+ ]
+ }
]
}
};
加载字体
那么,像字体这样的其他资源如何处理呢?file-loader 和 url-loader 可以接收并加载任何文件,然后将其输出到构建目录。这就是说,我们可以将它们用于任何类型的文件,包括字体。让我们更新 webpack.config.js 来处理字体文件:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
+ {
+ test: /\.(woff|woff2|eot|ttf|otf)$/,
+ use: [
+ 'file-loader'
+ ]
+ }
]
}
};
加载数据
npm install --save-dev csv-loader xml-loader
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
+ {
+ test: /\.(csv|tsv)$/,
+ use: [
+ 'csv-loader'
+ ]
+ },
+ {
+ test: /\.xml$/,
+ use: [
+ 'xml-loader'
+ ]
+ }
]
}
};
管理输出
webpack.config.js
const path = require('path');
module.exports = {
// entry: './src/index.js',
entry:{
app:'./src/index.js',
print:'./src/print.js'
},
output: {
// filename: 'bundle.js',
filename:'[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
...
};
执行npm run build
后,我们可以看到
Hash: 1e971c5b5f7389f57299
Version: webpack 3.9.1
Time: 785ms
Asset Size Chunks Chunk Names
app.bundle.js 561 kB 0, 1 [emitted] [big] app
print.bundle.js 2.74 kB 1 [emitted] print
[0] ./src/print.js 85 bytes {0} {1} [built]
[1] ./src/index.js 430 bytes {0} [built]
[3] (webpack)/buildin/global.js 509 bytes {0} [built]
[4] (webpack)/buildin/module.js 517 bytes {0} [built]
[5] ./src/style.css 1.04 kB {0} [built]
[6] ./node_modules/.0.28.7@css-loader!./src/style.css 198 bytes {0} [built]
+ 4 hidden modules
我们可以看到,webpack 生成 print.bundle.js 和 app.bundle.js 文件,这也和我们在 index.html 文件中指定的文件名称相对应。如果你在浏览器中打开 index.html,就可以看到在点击按钮时会发生什么。
设定HtmlWebpackPlugin
安装
npm install --save-dev html-webpack-plugin
cnpm install --save-dev html-webpack-plugin
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// entry: './src/index.js',
entry:{
app:'./src/index.js',
print:'./src/print.js'
},
output: {
// filename: 'bundle.js',
filename:'[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module:{
...
},
plugins:[
new HtmlWebpackPlugin({
title:'Output Management'
})
]
};
在我们构建之前,你应该了解,虽然在 dist/ 文件夹我们已经有 index.html 这个文件,然而 HtmlWebpackPlugin 还是会默认生成 index.html 文件。这就是说,它会用新生成的 index.html 文件,把我们的原来的替换。让我们看下在执行 npm run build 后会发生什么:
Hash: f6192796510782b20e6c
Version: webpack 3.9.1
Time: 1028ms
Asset Size Chunks Chunk Names
app.bundle.js 561 kB 0, 1 [emitted] [big] app
print.bundle.js 2.74 kB 1 [emitted] print
index.html 254 bytes [emitted]
[0] ./src/print.js 85 bytes {0} {1} [built]
[1] ./src/index.js 430 bytes {0} [built]
[3] (webpack)/buildin/global.js 509 bytes {0} [built]
[4] (webpack)/buildin/module.js 517 bytes {0} [built]
[5] ./src/style.css 1.04 kB {0} [built]
[6] ./node_modules/.0.28.7@css-loader!./src/style.css 198 bytes {0} [built]
+ 4 hidden modules
Child html-webpack-plugin for "index.html":
1 asset
[2] (webpack)/buildin/global.js 509 bytes {0} [built]
[3] (webpack)/buildin/module.js 517 bytes {0} [built]
+ 2 hidden modules
如果你在代码编辑器中将 index.html 打开,你就会看到 HtmlWebpackPlugin 创建了一个全新的文件,所有的 bundle 会自动添加到 html 中。
如果你想要了解更多 HtmlWebpackPlugin 插件提供的全部功能和选项,那么你就应该多多熟悉 HtmlWebpackPlugin 仓库。
清理/dist文件夹
你可能已经注意到,由于过去的指南和代码示例遗留下来,导致我们的 /dist 文件夹相当杂乱。webpack 会生成文件,然后将这些文件放置在 /dist 文件夹中,但是 webpack 无法追踪到哪些文件是实际在项目中用到的。
通常,在每次构建前清理 /dist 文件夹,是比较推荐的做法,因此只会生成用到的文件。让我们完成这个需求。
npm isntall clean-webpack-plugin --save-dev
cnpm isntall clean-webpack-plugin --save-dev
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
// entry: './src/index.js',
entry:{
app:'./src/index.js',
print:'./src/print.js'
},
output: {
// filename: 'bundle.js',
filename:'[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module:{
rules:[
...
]
},
plugins:[
new CleanWebpackPlugin(['dist']),//清理的文件夹
new HtmlWebpackPlugin({
title:'Output Management'
})
]
};
Manifest
你可能会感兴趣,webpack及其插件似乎“知道”应该哪些文件生成。答案是,通过 manifest,webpack 能够对「你的模块映射到输出 bundle 的过程」保持追踪。如果你对通过其他方式来管理 webpack 的输出更感兴趣,那么首先了解 manifest 是个好的开始。
开发
在这里我们学习如何搭建一个开发环境,让我们的开发更容易一些。
使用source map
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
// entry: './src/index.js',
entry:{
app:'./src/index.js',
print:'./src/print.js'
},
output: {
// filename: 'bundle.js',
filename:'[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module:{
rules:[
...
]
},
plugins:[
new CleanWebpackPlugin(['dist']),//清理的文件夹
new HtmlWebpackPlugin({
title:'Output Management'
})
],
devtool:'inline-source-map'
};
添加devtool:'inline-source-map'
选择一个开发工具
每次要编译代码时,手动运行 npm run build 就会变得很麻烦。
webpack 中有几个不同的选项,可以帮助你在代码发生变化后自动编译代码:
webpack's Watch Mode
webpack-dev-server
webpack-dev-middleware
多数场景中,你可能需要使用 webpack-dev-server,但是不妨探讨一下以上的所有选项。
使用观察模式
你可以指示 webpack "watch" 依赖图中的所有文件以进行更改。如果其中一个文件被更新,代码将被重新编译,所以你不必手动运行整个构建。
我们添加一个用于启动 webpack 的观察模式的 npm script 脚本:
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"watch": "webpack --watch",
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"clean-webpack-plugin": "^0.1.17",
"css-loader": "^0.28.7",
"csv-loader": "^2.1.1",
"file-loader": "^1.1.5",
"html-webpack-plugin": "^2.30.1",
"style-loader": "^0.19.0",
"webpack": "^3.9.1",
"xml-loader": "^1.2.1"
},
"dependencies": {
"lodash": "^4.17.4"
}
}
现在,你可以在命令行中运行 npm run watch,就会看到 webpack 编译代码,然而却不会退出命令行。这是因为 script 脚本还在观察文件。
唯一的缺点是,为了看到修改后的实际效果,你需要刷新浏览器。如果能够自动刷新浏览器就更好了,可以尝试使用 webpack-dev-server,恰好可以实现我们想要的功能。
使用webpack-dev-server
webpack-dev-server 为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。
npm install --save-dev webpack-dev-server
cnpm install --save-dev webpack-dev-server
修改配置文件,告诉开发服务器(dev server),在哪里查找文件:
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
// entry: './src/index.js',
entry:{
app:'./src/index.js',
print:'./src/print.js'
},
output: {
// filename: 'bundle.js',
filename:'[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module:{
rules:[
...
]
},
plugins:[
new CleanWebpackPlugin(['dist']),//清理的文件夹
new HtmlWebpackPlugin({
title:'Output Management'
})
],
devtool:'inline-source-map',
devServer:{
contentBase:'./dist'
}
};
以上配置告知 webpack-dev-server,在 localhost:8080 下建立服务,将 dist 目录下的文件,作为可访问文件。
让我们添加一个 script 脚本,可以直接运行开发服务器(dev server):
package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"watch": "webpack --watch",
"start": "webpack-dev-server --open"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"clean-webpack-plugin": "^0.1.17",
"css-loader": "^0.28.7",
"csv-loader": "^2.1.1",
"file-loader": "^1.1.5",
"html-webpack-plugin": "^2.30.1",
"style-loader": "^0.19.0",
"webpack": "^3.9.1",
"webpack-dev-server": "^2.9.5",
"xml-loader": "^1.2.1"
},
"dependencies": {
"lodash": "^4.17.4"
}
}
我这边试了一下后,发现如果webpack-dev-server启动起来的话,还需要安装下面的东西
cnpm install --save-dev parseurl
cnpm install --save-dev express
然后才能启动起来。
就是执行npm start
命令。
使用webpack-dev-middleware
webpack-dev-middleware 是一个中间件容器(wrapper),它将通过 webpack 处理后的文件发布到一个服务器(server)。在内部 webpack-dev-server 它使用,然而,它可以作为一个单独的包来提供,可以进行更多的自定义设置来实现更多需求。接下来是一个 webpack-dev-middleware 配合 express server 的示例。
首先,安装 express 和 webpack-dev-middleware:
npm install --save-dev express webpack-dev-middleware
接下来我们需要对 webpack 的配置文件做一些调整,以确保中间件(middleware)功能能够正确启用:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
// entry: './src/index.js',
entry:{
app:'./src/index.js',
print:'./src/print.js'
},
output: {
// filename: 'bundle.js',
filename:'[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
+ publicPath:'/'
},
module:{
rules:[
...
]
},
plugins:[
new CleanWebpackPlugin(['dist']),//清理的文件夹
new HtmlWebpackPlugin({
title:'Output Management'
})
],
devtool:'inline-source-map',
devServer:{
contentBase:'./dist'
}
};
webpack-demo
|- package.json
|- webpack.config.js
+ |- server.js
|- /dist
|- /src
|- index.js
|- print.js
|- /node_modules
server.js
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const app = express();
const config = require('./webpack.config.js');
const compiler = webpack(config);
// Tell express to use the webpack-dev-middleware and use the webpack.config.js
// configuration file as a base.
app.use(webpackDevMiddleware(compiler,{
publicPath:config.output.publicPath
}));
// Serve the files on port 3000.
app.listen(3000, function () {
console.log('Example app listening on port 3000!\n');
});
然后,我们添加一个npm srcipt,以便我们更方便地运行服务:
package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"watch": "webpack --watch",
"start": "webpack-dev-server --open",
"server":"node server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"clean-webpack-plugin": "^0.1.17",
"css-loader": "^0.28.7",
"csv-loader": "^2.1.1",
"express": "^4.16.2",
"file-loader": "^1.1.5",
"html-webpack-plugin": "^2.30.1",
"parseurl": "^1.3.2",
"style-loader": "^0.19.0",
"webpack": "^3.9.1",
"webpack-dev-middleware": "^1.12.2",
"webpack-dev-server": "^2.9.5",
"xml-loader": "^1.2.1"
},
"dependencies": {
"lodash": "^4.17.4"
}
}
现在,我们运行npm run server
,结果如下:
Hash: ad60018b6b26603b9a3a
Version: webpack 3.9.1
Time: 1472ms
Asset Size Chunks Chunk Names
app.bundle.js 1.48 MB 0, 1 [emitted] [big] app
print.bundle.js 6.42 kB 1 [emitted] print
index.html 256 bytes [emitted]
[0] ./src/print.js 85 bytes {0} {1} [built]
[1] ./src/index.js 430 bytes {0} [built]
[2] ./node_modules/.4.17.4@lodash/lodash.js 540 kB {0} [built]
[3] (webpack)/buildin/global.js 509 bytes {0} [built]
[4] (webpack)/buildin/module.js 517 bytes {0} [built]
[5] ./src/style.css 1.04 kB {0} [built]
[6] ./node_modules/.0.28.7@css-loader!./src/style.css 198 bytes {0} [built]
[7] ./node_modules/.0.28.7@css-loader/lib/css-base.js 2.26 kB {0} [built]
[8] ./node_modules/.0.19.0@style-loader/lib/addStyles.js 9.41 kB {0} [built]
[9] ./node_modules/.0.19.0@style-loader/lib/urls.js 3.01 kB {0} [built]
Child html-webpack-plugin for "index.html":
Asset Size Chunks Chunk Names
index.html 544 kB 0
[0] ./node_modules/.2.30.1@html-webpack-plugin/lib/loader.js!./node_modules/.2.30.1@html-webpack-plugin/default_index.ejs 546 bytes {0} [built]
[1] ./node_modules/.4.17.4@lodash/lodash.js 540 kB {0} [built]
[2] (webpack)/buildin/global.js 509 bytes {0} [built]
[3] (webpack)/buildin/module.js 517 bytes {0} [built]
webpack: Compiled successfully.
调整文本编辑器
使用自动编译代码时,可能会在保存文件时遇到一些问题。某些编辑器具有“安全写入”功能,可能会影响重新编译。
要在一些常见的编辑器中禁用此功能,请查看以下列表:
Sublime Text 3 - 在用户首选项(user preferences)中添加 atomic_save: "false"。
IntelliJ - 在首选项(preferences)中使用搜索,查找到 "safe write" 并且禁用它。
Vim - 在设置(settings)中增加 :set backupcopy=yes。
WebStorm - 在 Preferences > Appearance & Behavior > System Settings 中取消选中 Use "safe write"。