- 配置
整个配置中我们使用 Node 内置的 path 模块,并在它前面加上 __dirname这个全局变量。可以防止不同操作系统之间的文件路径问题,并且可以使相对路径按照预期工作
指南
- 安装 安装最新版本或是定制版本 (对于大多数项目都建议本地安装)
version 版本号
npm install --save-dev webpack
npm isntall --save-deb webpack@<version>
如果你使用webpack 4+ 版本 你还需要安装
npm install --save-dev webpack-cli
webpack 通过运行一个或多个 npm scripts,会在本地 node_modules 目录中查找安装的 webpack:
"scripts": {
"start": "webpack --config webpack.config.js"
}
当你在本地安装 webpack 后,你能够从 node_modules/.bin/webpack 访问它的 bin 版本。
npm install --global webpack (全局安装不推荐)
最新体验版本
npm install webpack@beta
npm install webpack/webpack#<tagname/branchname>
起步
- 基本安装
mkdir webpack-demo && cd webpack-demo 创建文件夹
npm init -y 初始化
npm install webpack webpack-cli --save-dev 安装
npm install --save lodash
执行 npx webpack,会将我们的脚本作为入口起点,然后 输出 为 main.js。Node 8.2+ 版本提供的 npx 命令,可以运行在初始安装的 webpack 包(package)的 webpack 二进制文件(./node_modules/.bin/webpack)
-
模块 ES2015 中的 import 和 export 语句已经被标准化 注意,webpack 不会更改代码中除 import 和 export 语句以外的部分。如果你在使用其它 ES2015 特性,请确保你在 webpack 的 loader 系统中使用了一个像是 Babel 或 Bublé 的转译器
-
使用一个配置文件 添加 webpack-config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
比起 CLI 这种简单直接的使用方式,配置文件具有更多的灵活性。我们可以通过配置方式指定 loader 规则(loader rules)、插件(plugins)、解析选项(resolve options),以及许多其他增强功能。了解更多详细信息,请查看配置文档。
- NPM脚本 (NPM Scripts) 考虑到用 CLI 这种方式来运行本地的 webpack 不是特别方便,我们可以设置一个快捷方式。在 package.json 添加一个 npm 脚本(npm script):
package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
+ "build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.0.1",
"webpack-cli": "^2.0.9",
"lodash": "^4.17.5"
}
}
运行 npm run build 正常打包 ok
管理资源
为了从 JavaScript 模块中 import 一个 CSS 文件,你需要在 module 配置中 安装并添加 style-loader 和 css-loader
- 加载CSS :
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'
+ ]
+ }
+ ]
+ }
};
webpack 根据正则表达式,来确定应该查找哪些文件,并将其提供给指定的 loader。在这种情况下,以 .css 结尾的全部文件,都将被提供给 style-loader 和 css-loader。
- 加载图片
npm install --save-dev file-loader
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'
+ ]
+ }
]
}
};
现在,当你 import MyImage from './my-image.png',该图像将被处理并添加到 output 目录,_并且_ MyImage 变量将包含该图像在处理后的最终 url。当使用 css-loader 时,如上所示,你的 CSS 中的 url('./my-image.png') 会使用类似的过程去处理。loader 会识别这是一个本地文件,并将 './my-image.png' 路径,替换为输出目录中图像的最终路径。html-loader 以相同的方式处理 <img src="./my-image.png" />。
- 加载字体 配置 test
+ {
+ test: /\.(woff|woff2|eot|ttf|otf)$/,
+ use: [
+ 'file-loader'
+ ]
+ }
通过配置好 loader 并将字体文件放在合适的地方,你可以通过一个 @font-face 声明引入。本地的 url(...) 指令会被 webpack 获取处理,就像它处理图片资源一样
src/style.css
+ @font-face {
+ font-family: 'MyFont';
+ src: url('./my-font.woff2') format('woff2'),
+ url('./my-font.woff') format('woff');
+ font-weight: 600;
+ font-style: normal;
+ }
.hello {
color: red;
+ font-family: 'MyFont';
background: url('./icon.png');
}
- 加载数据 此外,可以加载的有用资源还有数据,如 JSON 文件,CSV、TSV 和 XML。类似于 NodeJS,JSON 支持实际上是内置的,也就是说 import Data from './data.json' 默认将正常运行。要导入 CSV、TSV 和 XML,你可以使用 csv-loader 和 xml-loader。让我们处理这三类文件
npm install --save-dev csv-loader xml-loader
webpack.config.js
+ {
+ test: /\.(csv|tsv)$/,
+ use: [
+ 'csv-loader'
+ ]
+ },
+ {
+ test: /\.xml$/,
+ use: [
+ 'xml-loader'
+ ]
+ }
- 全局资源 上述所有内容中最出色之处是,以这种方式加载资源,你可以以更直观的方式将模块和资源组合在一起。无需依赖于含有全部资源的 /assets 目录,而是将资源与代码组合在一起。例如,类似这样的结构会非常有用:
- |- /assets
+ |– /components
+ | |– /my-component
+ | | |– index.jsx
+ | | |– index.css
+ | | |– icon.svg
+ | | |– img.png
这种配置方式会使你的代码更具备可移植性,因为现有的统一放置的方式会造成所有资源紧密耦合在一起。假如你想在另一个项目中使用 /my-component,只需将其复制或移动到 /components 目录下。只要你已经安装了任何扩展依赖(external dependencies),并且你已经在配置中定义过相同的 loader,那么项目应该能够良好运行。
但是,假如你无法使用新的开发方式,只能被固定于旧有开发方式,或者你有一些在多个组件(视图、模板、模块等)之间共享的资源。你仍然可以将这些资源存储在公共目录(base directory)中,甚至配合使用 alias 来使它们更方便 import 导入
管理输出
调整配置。我们将在 entry 添加 src/print.js 作为新的入口起点(print),然后修改 output,以便根据入口起点名称动态生成 bundle 名称:
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')
}
};
结果会生成 print.bundle.js 和 app.bundle.js 文件 这也是我们在 index.html 文件中指定的文件名称相对应。如果你在浏览器中打开index.html 就可以看到点击按钮会发生什么
但是 如果我们更改了我们的一个入口起点的名称 甚至添加一个新的名称 生成的重命名在一个构建中,但是我们的index。html 文件仍会引用新的名字 我们用 HtmlWebpackPlugin 来解决
- 设定 HtmlWebpackPlugin 安装
npm install --save-dev html-webpack-plugin
webpack.congif.js 添加
+ const HtmlWebpackPlugin = require('html-webpack-plugin'); //大小写一定不能错
+ plugins: [
+ new HtmlWebpackPlugin({
+ title: 'Output Management'
+ })
+ ],
- 清理 dist 文件夹 去除编译多余的文件 安装
npm install clean-webpack-plugin --save-dev
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
plugins: [
+ new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Output Management'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
- Manifest 通过 manifest,webpack 能够对「你的模块映射到输出 bundle 的过程」保持追踪
开发
- 使用 source map 更容易地追踪错误和警告
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
+ devtool: 'inline-source-map',
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Development'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
-
自动编译 webpack's Watch Mode webpack-dev-server webpack-dev-middleware
-
使用观察模式 package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
+ "watch": "webpack --watch",
"build": "webpack"
},
- 使用 webpack-dev-server npm install webpack-dev-server
配置
module.export = {
+ devServer: {
+ contentBase: path.join(__dirname, "dist"),
+ port: 8080,
+ open: true
+ }
}
package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"build": "webpack",
+ "start": "webpack-dev-server"
}
启动: npm run start
- 使用webpack-dev-middleware
安装:
npm install --save-dev express webpack-dev-middleware
配置webpack.config.js文件确保中间件能够正确启用
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
devtool: 'inline-source-map',
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Output Management'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
+ publicPath: '/'
}
};
server.js (与src同级)
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');
});
配置 package.json文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"start": "webpack-dev-server --open",
+ "server": "node server.js",
"build": "webpack"
},
启动 npm run server 打开 localhost:3000 可以打开
- 调整文本编辑器
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"。
模块热替换HMR
(HMR 不适用与生产环境,这意味着它应当只在开发环境使用)
- 启用HMR 目的是更新 webpack-dev-server的配置 和使用webpack内置的HMR 插件
| 如果你使用了 webpack-dev-middleware 而没有使用 webpack-dev-server,请使用 webpack-hot-middleware package 包,以在你的自定义服务或应用程序上启用 HMR。 |
- (1)在devServer属性中添加hot属性并赋值为true (2)引入两个插件到webpacke配置文件: (3)在入口文件底部添加代码 使得在所有代码发生变化时,能够通知webpack webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const webpack = require('webpack');
module.exports = {
entry: {
- app: './src/index.js',
- print: './src/print.js'
+ app: './src/index.js'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
+ hot: true
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Hot Module Replacement'
}),
+ new webpack.NamedModulesPlugin(),
+ new webpack.HotModuleReplacementPlugin()
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
正常使用我们的热更新 npm run dev 启用我们的 dev-server
index.js 入口文件
import _ from 'lodash';
import printMe from './print.js';
function component() {
var element = document.createElement('div');
var btn = document.createElement('button');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
btn.innerHTML = 'Click me and check the console!';
btn.onclick = printMe;
element.appendChild(btn);
return element;
}
document.body.appendChild(component());
+
+ if (module.hot) {
+ module.hot.accept('./print.js', function() {
+ console.log('Accepting the updated printMe module!');
+ printMe();
+ })
+ }
- 热更新的修改样式表
npm install --save-dev style-loader css-loader
配置 webpack配置,让loader生效 webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
hot: true
},
+ module: {
+ rules: [
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader']
+ }
+ ]
+ },
plugins: [
new CleanWebpackPlugin(['dist'])
new HtmlWebpackPlugin({
title: 'Hot Module Replacement'
}),
new webpack.HotModuleReplacementPlugin()
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
热加载样式表 与将其导入模块一样简单: 新建 styles.css 文件
body{
background: blue;
}
index.js 入口文件导入
import './styles.css'
修改样式表 会自动更新
社区还有许多其他 loader 和示例,可以使 HMR 与各种框架和库(library)平滑地进行交互……
React Hot Loader:实时调整 react 组件。
Vue Loader:此 loader 支持用于 vue 组件的 HMR,提供开箱即用体验。
Elm Hot Loader:支持用于 Elm 程序语言的 HMR。
Redux HMR:无需 loader 或插件!只需对 main store 文件进行简单的修改。
Angular HMR:No loader necessary! A simple change to your main NgModule file is all that's required to have full control over the HMR APIs.没有必要使用 loader!只需对主要的 NgModule 文件进行简单的修改,由 HMR API 完全控制。
- 搭建生产环境
- 安装 webpack-merge:
npm install webpack- merge --save-dev
- 建立三个配置文件: 自定义名字 webpack.common.sj 等 dev prod webpack.base.conf.js webpack.dev.conf.js webpack.prod.conf.js
其中 webpack.base.conf.js表示最基础的配置信息,开发环境和生产环境都需要配置的信息
比如 entry output module等 在另外两个文件中配置一些 对应环境特有的信息,然后通过webpack-merge模块 与 webpack.base.conf.js 整合
3. 添加npm script
// package.json
{
"scripts": {
"start": "webpack-dev-server --open --config webpack.dev.conf.js",
"build": "webpack --config webpack.prod.conf.js"
}
}
此外建议设置mode属性 因为生产环境下回自动开启代码压缩 免去了配置的麻烦
webpack-demo
|- package.json
- |- webpack.config.js
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js
|- /dist
|- /src
|- index.js
|- math.js
|- /node_modules
webpack.common.js
+ const path = require('path');
+ const CleanWebpackPlugin = require('clean-webpack-plugin');
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
+
+ module.exports = {
+ entry: {
+ app: './src/index.js'
+ },
+ plugins: [
+ new CleanWebpackPlugin(['dist']),
+ new HtmlWebpackPlugin({
+ title: 'Production'
+ })
+ ],
+ output: {
+ filename: '[name].bundle.js',
+ path: path.resolve(__dirname, 'dist')
+ }
+ };
webpack.dev.js
+ const merge = require('webpack-merge');
+ const common = require('./webpack.common.js');
+
+ module.exports = merge(common, {
+ devtool: 'inline-source-map',
+ devServer: {
+ contentBase: './dist'
+ }
+ });
webpack.prod.js
+ const merge = require('webpack-merge');
+ const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
+ const common = require('./webpack.common.js');
+
+ module.exports = merge(common, {
+ plugins: [
+ new UglifyJSPlugin()
+ ]
+ });
配置package.json
"scripts": {
- "start": "webpack-dev-server --open",
+ "start": "webpack-dev-server --open --config webpack.dev.js",
- "build": "webpack"
+ "build": "webpack --config webpack.prod.js"
},
到此 就基本完成了 各类环境的配置 注意 在生产环境中 有个UglifyJSPlugin 代码压缩
另外还有 BabelMinifyWebpackPlugin ClosureCompilerPlugin
如果决定尝试以上这些,只要确保新插件也会按照 tree shake 指南中所陈述的,具有删除未引用代码(dead code)的能力足矣。
- source map 鼓励使用 因为它们对调试源码(debug)和运行基准测试(benchmark tests)很有帮助。虽然有如此强大的功能,然而还是应该针对生成环境用途,选择一个构建快速的推荐配置(具体细节请查看 devtool)。对于本指南,我们将在生产环境中使用 source-map 选项,而不是我们在开发环境中用到的 inline-source-map
webpack.prod.js
const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
+ devtool: 'source-map',
plugins: [
- new UglifyJSPlugin()
+ new UglifyJSPlugin({
+ sourceMap: true
+ })
]
});
| 避免在生产中使用 inline-*** 和 eval-***,因为它们可以增加 bundle 大小,并降低整体性能。 |
- 指定环境
许多library将通过
process.env.NODE_ENV环境变量关联,以决定library中应该引用哪些内容。例如 当不处于生产环境中时,某些library为了使调试变得容易, 可能会添加额外的日志记录(log)和测试(test)其实当使用 process.env.NODE_ENV === 'production' 时,一些 library 可能针对具体用户的环境进行代码优化,从而删除或添加一些重要代码。我们可以使用 webpack 内置的DefinePlugin为所有的依赖定义这个变量:
webpack.prod.js
+ const webpack = require('webpack');
const merge = require('webpack-merge');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
devtool: 'source-map',
plugins: [
new UglifyJSPlugin({
sourceMap: true
- })
+ }),
+ new webpack.DefinePlugin({
+ 'process.env.NODE_ENV': JSON.stringify('production')
+ })
]
});
| 技术上讲,NODE_ENV 是一个由 Node.js 暴露给执行脚本的系统环境变量。通常用于决定在开发环境与生产环境(dev-vs-prod)下,服务器工具、构建脚本和客户端 library 的行为。然而,与预期不同的是,无法在构建脚本 webpack.config.js 中,将 process.env.NODE_ENV 设置为 "production",请查看 #2537。因此,例如 process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js' 这样的条件语句,在 webpack 配置文件中,无法按照预期运行。 |
如果你正在使用像 react 这样的 library,那么在添加此 DefinePlugin 插件后,你应该看到 bundle 大小显著下降。还要注意,任何位于 /src 的本地代码都可以关联到 process.env.NODE_ENV 环境变量,所以以下检查也是有效的:
src/index.js 检测
import { cube } from './math.js';
+
+ if (process.env.NODE_ENV !== 'production') {
+ console.log('Looks like we are in development mode!');
+ }
function component() {
var element = document.createElement('pre');
element.innerHTML = [
'Hello webpack!',
'5 cubed is equal to ' + cube(5)
].join('\n\n');
return element;
}
document.body.appendChild(component());
- tips
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
| 注意使用 引入必须用 {} 下面new CleanWebpackPlugin(), 里面试一个对象option 默认是空 |
注意按照 需要的依赖 loader和Plugin 如果报错是
ERROR in app.bundle.js from UglifyJs
Unexpected token: name «src_element», expected: punc «;» [app.bundle.js:17705,4]
尝试 babel版本降低
npm install --save-dev babel-loader@7
还是出错
当项目中用了es6、jsx之类的,就必须要先通过babel转换一下,再交给webpack去打包。
需要先安装babel: npm install babel-loader babel-core babel-preset-es2015 --save-dev 然后在webpack.config.js的同级目录下,创建一个 .babelrc 的文件,注意:babelrc 前面有个点
文件里面添加这样一句话:
1 { "presets": ["es2015"] }
但是还是报错 如果你有什么解决方法请留言 联系,感谢。
-
Split CSS 正如在管理资源中最后的 加载 CSS 小节中所提到的,通常最好的做法是使用
ExtractTextPlugin将 CSS 分离成单独的文件。在插件文档中有一些很好的实现例子。disable选项可以和--env标记结合使用,以允许在开发中进行内联加载,推荐用于热模块替换和构建速度。 -
CLI 替代选项 以上描述也可以通过命令行实现。例如,
--optimize-minimize标记将在后台引用UglifyJSPlugin。和以上描述的DefinePlugin实例相同,--define process.env.NODE_ENV="'production'"也会做同样的事情。并且,webpack -p将自动地调用上述这些标记,从而调用需要引入的插件。
这些简便方式虽然都很不错,但是我们通常建议只使用配置方式,因为在这两种场景中下,配置方式能够更好地帮助你了解自己正在做的事情。配置方式还可以让你更方便地控制这两个插件中的其他选项。
性能优化 TreeShaing
通常配置package.json文件中的 sideEffects属性 可以指定哪些文件需要移除多余代码 如果sideEffects设置为false 那么表示文件的未使用的代码可以放心移除 如果有些文件的冗余代码不能移除。可以尝试设置sideEffects属性为一个数组,数组内容为文件的路径字符串 TreeShaing 会移除你在 js文件中使用不到的代码
压缩输出 指定无副作用的文件之后,设置mode为"production",再次构建代码,可以发现未使用到的代码已经被移除。
- Tips
- 在
module.rules属性中,设置include属性以指定哪些文件需要被loader处理。 - 只使用必要的loader。
- 保持最新版本。
- 减少项目文件数
{
"name": "your-project",
"sideEffects": false
}
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js",
"*.css"
]
}
最后,还可以在 module.rules 配置选项 中设置 "sideEffects"。
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
- }
+ },
+ mode: "production"
};
- 注意
- 1 使用 ES2015 模块语法(即 import 和 export)。
- 2 在项目 package.json 文件中,添加一个 "sideEffects" 入口。
- 3 引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。
代码分离
-
常用的三种分离方式
- 入口起点: 使用
entry配置手动分离代码 - 防止重复:使用
CommonsChunkPlugin去重和分离 Chunk - 动态导入:通过模块的内联函数调用来分离代码
- 入口起点: 使用
-
入口分离 entry
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
|- index.js
+ |- another-module.js
|- /node_modules
another-module.js
import _ from 'lodash';
console.log(
_.join(['Another', 'module', 'loaded!'], ' ')
);
webpack.config.js
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js',
another: './src/another-module.js'
},
plugins: [
new HTMLWebpackPlugin({
title: 'Code Splitting'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
后果就是我们引入的依赖包 会重复打包。比如 lodash
- 防止重复 解决方案
CommonsChunkPlugin插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。让我们使用这个插件,将之前的示例中重复的 lodash 模块去除
webpack.config.js
const path = require('path');
+ const webpack = require('webpack');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js',
another: './src/another-module.js'
},
plugins: [
new HTMLWebpackPlugin({
title: 'Code Splitting'
- })
+ }),
+ new webpack.optimize.CommonsChunkPlugin({
+ name: 'common' // 指定公共 bundle 的名称。
+ })
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
这里我们使用 CommonsChunkPlugin 之后,现在应该可以看出,index.bundle.js 中已经移除了重复的依赖模块。需要注意的是,CommonsChunkPlugin 插件将 lodash 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了大小。执行 npm run build
代码分离的插件和loader
-
ExtractTextPlugin: 用于将 CSS 从主应用程序中分离。 -
bundle-loader: 用于分离代码和延迟加载生成的 bundle。 -
promise-loader: 类似于 bundle-loader ,但是使用的是 promises。 -
CommonsChunkPlugin插件还可以通过使用显式的 vendor chunks 功能,从应用程序代码中分离 vendor 模块。 -
动态导入 对于动态到入 webpack 有两个类似的技术 对于动态导入 第一种,优先选择 使用符合
ECMAScript提案 的import()语法。第二种,则是使用 webpack 特定的require.ensure。
webpack.config.js
const path = require('path');
- const webpack = require('webpack');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
+ index: './src/index.js'
- index: './src/index.js',
- another: './src/another-module.js'
},
plugins: [
new HTMLWebpackPlugin({
title: 'Code Splitting'
- }),
+ })
- new webpack.optimize.CommonsChunkPlugin({
- name: 'common' // 指定公共 bundle 的名称。
- })
],
output: {
filename: '[name].bundle.js',
+ chunkFilename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
src/index.js
const path = require('path');
- const webpack = require('webpack');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
+ index: './src/index.js'
- index: './src/index.js',
- another: './src/another-module.js'
},
plugins: [
new HTMLWebpackPlugin({
title: 'Code Splitting'
- }),
+ })
- new webpack.optimize.CommonsChunkPlugin({
- name: 'common' // 指定公共 bundle 的名称。
- })
],
output: {
filename: '[name].bundle.js',
+ chunkFilename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
注意,在注释中使用了 webpackChunkName。这样做会导致我们的 bundle 被命名为 lodash.bundle.js ,而不是 [id].bundle.js 。想了解更多关于 webpackChunkName 和其他可用选项,请查看 import() 相关文档。让我们执行 webpack,查看 lodash 是否会分离到一个单独的 bundle
由于 import() 会返回一个 promise,因此它可以和 async 函数一起使用。但是,需要使用像 Babel 这样的预处理器和Syntax Dynamic Import Babel Plugin。下面是如何通过 async 函数简化代码
- function getComponent() {
+ async function getComponent() {
- return import(/* webpackChunkName: "lodash" */ 'lodash').then(_ => {
- var element = document.createElement('div');
-
- element.innerHTML = _.join(['Hello', 'webpack'], ' ');
-
- return element;
-
- }).catch(error => 'An error occurred while loading the component');
+ var element = document.createElement('div');
+ const _ = await import(/* webpackChunkName: "lodash" */ 'lodash');
+
+ element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+
+ return element;
}
getComponent().then(component => {
document.body.appendChild(component);
});
bundle 分析(bundle analysis) 如果我们以分离代码作为开始,那么就以检查模块作为结束,分析输出结果是很有用处的。官方分析工具 是一个好的初始选择。下面是一些社区支持(community-supported)的可选工具:
-
webpack-chart: webpack 数据交互饼图。 -
webpack-visualizer: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。 -
webpack-bundle-analyzer: 一款分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户。 -
懒加载 在代码分割的时候使用懒加载了
function getComponent() {
const element = document.createElement('div');
return import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) => {
const element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}).catch(error => 'An error occurred while loading the component');
}
// 按需加载,当点击了页面,才会引入lodash,也是单页应用路由懒加载的实现原理
window.addEventListener('click', function(){
getComponent().then(component => {
document.body.appendChild(component);
})
});