资源
官方文档
https://webpack.docschina.org/concepts/
https://doc.webpack-china.org/guides/getting-started
https://webpack.js.org/guides/getting-started/
https://webpack.docschina.org/guides/getting-started/
<深入浅出webpack>
https://webpack.wuhaolin.cn/
https://segmentfault.com/a/1190000015088834 只是其中第5章
webpack-cli
https://github.com/webpack/webpack-cli
webpack CLI provides a flexible set of commands for developers to increase speed when setting up a custom webpack project.
安装
//全局安装
npm install -g webpack
//安装到你的项目目录
npm install --save-dev webpack
配置
https://webpack.js.org/configuration/
传参
Webpack 调教之参数的投喂 https://github.com/wayou/wayou.github.io/issues/14
管理资源
https://www.webpackjs.com/guides/asset-management
module属性里, 依次为各种文件类型添加对应的loader
module.exports = {
………
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
…………
模块概念
https://www.webpackjs.com/concepts/modules/
webpack 模块能够以各种方式表达它们的依赖关系,几个例子如下:
- ES2015
import
语句 - CommonJS
require()
语句 - AMD
define
和require
语句 - css/sass/less 文件中的
@import
语句。 - 样式(
url(...)
)或 HTML 文件(``)中的图片链接(image url)
webpack 通过 loader 可以支持各种语言和预处理器编写模块
模块方法
https://www.webpackjs.com/api/module-methods/
loader
loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
https://www.webpackjs.com/concepts/#loader
https://www.webpackjs.com/concepts/loaders/
在你的应用程序中,有三种使用 loader 的方式:
编写loader
https://www.webpackjs.com/contribute/writing-a-loader/
loader API
https://www.webpackjs.com/api/loaders/
所谓 loader 只是一个导出为函数的 JavaScript 模块。loader runner 会调用这个函数,然后把上一个 loader 产生的结果或者资源文件(resource file)传入进去。函数的 this 上下文将由 webpack 填充,并且 loader runner 具有一些有用方法,可以使 loader 改变为异步调用方式,或者获取 query 参数。
https://webpack.js.org/loaders/
具体的各loader的文档
plugins
https://www.webpackjs.com/concepts/plugins/
插件目的在于解决 loader 无法实现的其他事
webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。
编写插件: https://www.webpackjs.com/contribute/writing-a-plugin
管理输出
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
插件
plugins: [
+ new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Output Management'
})
],
HtmlWebpackPlugin 用于生成html
CleanWebpackPlugin 每次构建前清理 /dist
文件夹等
WebpackManifestPlugin 通过 manifest,webpack 能够对「你的模块映射到输出 bundle 的过程」保持追踪 , 见 https://doc.webpack-china.org/concepts/manifest
manifest
通过 manifest,webpack 能够对「你的模块映射到输出 bundle 的过程」保持追踪
配置
https://www.webpackjs.com/configuration/output/
开发
https://doc.webpack-china.org/guides/development
webpack 中有几个不同的选项,可以帮助你在代码发生变化后自动编译代码:
- webpack’s Watch Mode
- webpack-dev-server
- webpack-dev-middleware
webpack-dev-server中使用mock-server
https://panjiachen.github.io/vue-element-admin-site/zh/guide/essentials/mock-api.html#新方案
在webpack-dev-server中after这个Middleware中实现mock逻辑
dev-server proxy
https://webpack.js.org/configuration/dev-server/#devserverproxy
const { createProxyMiddleware } = require('http-proxy-middleware');
const apiProxy = createProxyMiddleware('/api', { target: 'http://www.example.org' });
// \____/ \_____________________________/
// | |
// context options
// 'apiProxy' is now ready to be used as middleware in a server.
- context: Determine which requests should be proxied to the target host. (more on context matching)
- options.target: target host to proxy to. (protocol + host)
RFC 3986 path
is used for context matching.
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
scheme authority path query fragment
https://github.com/chimurai/http-proxy-middleware#context-matching
createProxyMiddleware({...})
- matches any path, all requests will be proxied.createProxyMiddleware('/', {...})
- matches any path, all requests will be proxied.createProxyMiddleware('/api', {...})
- matches paths starting with/api
- createProxyMiddleware([‘/api’, ‘/ajax’, ‘/someotherpath’], {…})
- wildcard path matching
- custom matching
模块热替换
在运行时更新各种模块,而无需进行完全刷新
HMR 不适用于生产环境,这意味着它应当只在开发环境使用。更多详细信息,请查看 生产环境构建指南
几种开启方法:
- 更新 webpack-dev-server 的配置
```
devServer: {
contentBase: './dist',
- hot: true
},
```
Note that
webpack.HotModuleReplacementPlugin
is required to fully enable HMR. Ifwebpack
orwebpack-dev-server
are launched with the--hot
option, this plugin will be added automatically, so you may not need to add this to yourwebpack.config.js
. See the HMR concepts page for more information.
- 通过 Node.js API ``` const webpackDevServer = require(‘webpack-dev-server’); const webpack = require(‘webpack’);
const config = require(‘./webpack.config.js’); const options = { contentBase: ‘./dist’, hot: true, host: ‘localhost’ };
webpackDevServer.addDevServerEntrypoints(config, options); const compiler = webpack(config); const server = new webpackDevServer(compiler, options);
社区还有许多其他 loader 和示例,可以使 HMR 与各种框架和库(library)平滑地进行交互……<br />· [React Hot Loader](https://github.com/gaearon/react-hot-loader):实时调整 react 组件。<br />· [Vue Loader](https://github.com/vuejs/vue-loader):此 loader 支持用于 vue 组件的 HMR,提供开箱即用体验。<br />· [Elm Hot Loader](https://github.com/fluxxu/elm-hot-loader):支持用于 Elm 程序语言的 HMR<br />· [Redux HMR](https://survivejs.com/webpack/appendices/hmr-with-react/#configuring-hmr-with-redux):无需 loader 或插件!只需对 main store 文件进行简单的修改。<br />· [Angular HMR](https://github.com/AngularClass/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 完全控制。
HMR原理解析<br />[https://zhuanlan.zhihu.com/p/30669007](https://zhuanlan.zhihu.com/p/30669007)
<a name="866b1a74"></a>
# Tree Shaking
[https://webpack.js.org/guides/tree-shaking/](https://webpack.js.org/guides/tree-shaking/)
_tree shaking_ 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。<br />· Use ES2015 module syntax (i.e. `import` and `export`).<br />· Ensure no compilers transform your ES2015 module syntax into CommonJS modules (this is the default behavior of the popular Babel preset @babel/preset-env - see the [documentation](https://babeljs.io/docs/en/babel-preset-env#modules) for more details).<br />· Add a `"sideEffects"` property to your project's `package.json` file.<br />· Use the [`production`](https://webpack.js.org/configuration/mode/#mode-production) `mode` configuration option to enable [various optimizations](https://webpack.js.org/configuration/mode/#usage) including minification and tree shaking.<br />~~<br />~~使用插件: ~~[~~`UglifyJSPlugin`~~](https://doc.webpack-china.org/plugins/uglifyjs-webpack-plugin) Webpack4中 只需要 optimizati.minimize
或<br />· [BabelMinifyWebpackPlugin](https://github.com/webpack-contrib/babel-minify-webpack-plugin)<br />· [ClosureCompilerPlugin](https://github.com/roman01la/webpack-closure-compiler)
你的Tree-Shaking并没什么卵用<br />[https://github.com/wuomzfx/tree-shaking-test](https://github.com/wuomzfx/tree-shaking-test)
<a name="ab5582fe"></a>
# 生产环境构建
[https://webpack.docschina.org/guides/production/](https://webpack.docschina.org/guides/production/)
配置分离:
- |- webpack.common.js
- |- webpack.dev.js
- |- webpack.prod.js ```
避免在生产中使用 inline-***
和 eval-***
,因为它们可以增加 bundle 大小,并降低整体性能。
plugins: [
+ new UglifyJSPlugin({
+ sourceMap: true
+ })
]
建议开启sourceMap
配置环境
module.exports = merge(common, {
plugins: [
+ new webpack.DefinePlugin({
+ 'process.env.NODE_ENV': JSON.stringify('production')
+ })
]
})
任何位于 /src
的本地代码都可以关联到 process.env.NODE_ENV 环境变量
+ if (process.env.NODE_ENV !== 'production') {
+ console.log('Looks like we are in development mode!');
+ }
从 webpack v4 开始, 指定 mode
会自动地配置 DefinePlugin
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
});
https://webpack.docschina.org/guides/production/
代码分离
有三种常用的代码分离方法:
· 入口起点:使用 entry 配置手动地分离代码。
· 防止重复:使用 CommonsChunkPlugin 去重和分离 chunk。
· 动态导入:通过模块的内联函数调用来分离代码。
bundle 分析(bundle analysis)
https://www.webpackjs.com/guides/code-splitting/
如果我们以分离代码作为开始,那么就以检查模块作为结束,分析输出结果是很有用处的。官方分析工具 是一个好的初始选择。下面是一些社区支持(community-supported)的可选工具:
- webpack-chart: webpack 数据交互饼图。
- webpack-visualizer: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
- webpack-bundle-analyzer: 一款分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户。
懒加载
许多框架和类库对于如何用它们自己的方式来实现(懒加载)都有自己的建议。这里有一些例子:
· React: Code Splitting and Lazy Loading
· Vue: Lazy Load in Vue using Webpack’s code splitting
缓存
https://www.webpackjs.com/guides/caching/
将 webpack 的样板(boilerplate)和 manifest 提取出来
将第三方库(library)(例如 lodash
或 react
)提取到单独的 vendor
chunk 文件中, 使用 HashedModuleIdsPlugin
避免 vendor bundle 的 module.id
变化导致打包后文件的文件名变化
生产环境中 cacheGroups 不一定生效, 见 webpack/webpack#9726
shimming
v. 给…垫入填片
https://www.webpackjs.com/guides/shimming/
- 使用
ProvidePlugin
注入全局变量如_
$
- 使用
imports-loader
覆写this
- 加载polyfill
- …
比如 babel-polyfill
, 没有必要在 main.js
里引入. 可以将其单独打成一个bundle, 按需加载. 详见文档.
构建性能
https://webpack.docschina.org/guides/build-performance/
https://segmentfault.com/a/1190000018493260
https://juejin.im/post/5e53dbbc518825494905c45f
详见官方文档:
- 将 loader 应用于最少数量的必要模块, 使用
include
字段 - 每个额外的 loader/plugin 都有其启动时间。尽量少地使用工具。
- 使用
DllPlugin
为更改不频繁的代码生成单独的编译结果。 thread-loader
可以将非常消耗资源的 loader 分流给一个 worker pool- 使用
cache-loader
启用持久化缓存。
开发环境
- 需要注意的是不同的
devtool
设置,会导致性能差异。 - 避免在生产环境下才会用到的工具. 栗如
TerserPlugin
ts-loader
开启transpileOnly
生产环境
parallel-webpack
:它允许在一个 worker 池中运行 compilation。cache-loader
:可以在多个 compilation 之间共享缓存。
thread-loader 多进程打包
cache-loader 大大缩减二次构建的时间, 但是初始构建会更慢
这俩仅仅需要在一些性能开销较大的 loader 之前添加
speed-measure-webpack-plugin 分析各阶段消耗的时间
优化构建
通过gzip压缩显著的减少bundle.js文件的大小。
有两种方法这样做。1. 动态 gzip (不是首选) 2. 构建时gzip压缩 (首选)
动态gzip
会增加CPU的压力,影响性能
压缩比越高cpu占用越高,所以请配置合适的压缩比
express.js
npm install compression --save //安装
var compression = require(‘compression’); //引入到 express 应用
app.use(compression());//添加这个作为第一个中间件
nginx
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain application/xml;
http://www.nginx.cn/doc/standard/httpgzip.html
静态gzip
Webpack打包时生成xxx.gz文件
Express或者Nginx里配置对请求返回响应xxx.gz
Webpack配置
//1.安装Webpack压缩插件
npm install compression-webpack-plugin --save-dev
//2. 在webpack.config.prod.js中引入插件
var CompressionPlugin = require('compression-webpack-plugin');
//3. 添加到插件数组中
plugins: [
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin(),
new webpack.optimize.AggressiveMergingPlugin(),
new CompressionPlugin({ <-- 加上这
asset: "[path].gz[query]",
algorithm: "gzip",
test: /\.js$|\.css$|\.html$/,
threshold: 10240,
minRatio: 0.8
})
]
Express配置
// 在 Express 中添加中间件返回 .js.gz 。所以你仍然可以从HTML加载bundle.js但会收到bundle.js.gz
app.get('*.js', function (req, res, next) {
req.url = req.url + '.gz';
res.set('Content-Encoding', 'gzip');
next();
});
Nginx配置
conf/ngxin.conf里:
gzip_static on;
gzip_http_version 1.1;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
说明:
syntax: gzip_static on|off
default: gzip_static off
context: http, server, location
Enables the module. You should ensure that the timestamps of the compressed and uncompressed files match.
http://www.nginx.cn/doc/optional/gzipstatic.html
参考
CompressionWebpackPlugin https://webpack.docschina.org/plugins/compression-webpack-plugin/
主要Nginx https://zhuanlan.zhihu.com/p/37429159
主要Nginx 英文 https://medium.com/@selvaganesh93/how-to-serve-webpack-gzipped-file-in-production-using-nginx-692eadbb9f1c
主要Express https://www.zcfy.cc/article/two-quick-ways-to-reduce-react-app-s-size-in-production
Tomcat等的配置 https://segmentfault.com/a/1190000012571492
PWA
https://webpack.docschina.org/guides/progressive-web-application/
使用 workbox-webpack-plugin
生成 service-worker.js
在 client 代码中正常注册 Service Worker
dev-server内存不足
node >= 8
// windows
set NODE_OPTIONS=--max_old_space_size=4096
// linux
export NODE_OPTIONS=--max_old_space_size=4096
node < 8, 使用 increase-memory-limit
版本对比
https://scotch.io/tutorials/whats-new-in-webpack-4
https://webpack.js.org/migrate/4/https://webpack.js.org/migrate/4/
疑问
通过entry和cacheGroup拆分的区别