资源

官方文档
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.

安装

  1. //全局安装
  2. npm install -g webpack
  3. //安装到你的项目目录
  4. 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

  1. module.exports = {
  2. ………
  3. module: {
  4. rules: [
  5. {
  6. test: /\.css$/,
  7. use: [
  8. 'style-loader',
  9. 'css-loader'
  10. ]
  11. },
  12. …………

模块概念
https://www.webpackjs.com/concepts/modules/

webpack 模块能够以各种方式表达它们的依赖关系,几个例子如下:

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 的方式:

  • 配置(推荐):在 webpack.config.js 文件中指定 loader。
  • 内联:在每个 import 语句中显式指定 loader。
  • CLI:在 shell 命令中指定它们。

编写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

管理输出

  1. module.exports = {
  2. context: path.resolve(__dirname, '../'),
  3. entry: {
  4. app: './src/main.js'
  5. },
  6. output: {
  7. path: config.build.assetsRoot,
  8. filename: '[name].js',
  9. publicPath: process.env.NODE_ENV === 'production'
  10. ? config.build.assetsPublicPath
  11. : config.dev.assetsPublicPath
  12. },

插件

  1. plugins: [
  2. + new CleanWebpackPlugin(['dist']),
  3. new HtmlWebpackPlugin({
  4. title: 'Output Management'
  5. })
  6. ],

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 中有几个不同的选项,可以帮助你在代码发生变化后自动编译代码:

  1. webpack’s Watch Mode
  2. webpack-dev-server
  3. 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

  1. const { createProxyMiddleware } = require('http-proxy-middleware');
  2. const apiProxy = createProxyMiddleware('/api', { target: 'http://www.example.org' });
  3. // \____/ \_____________________________/
  4. // | |
  5. // context options
  6. // '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.

  1. foo://example.com:8042/over/there?name=ferret#nose
  2. \_/ \______________/\_________/ \_________/ \__/
  3. | | | | |
  4. 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 不适用于生产环境,这意味着它应当只在开发环境使用。更多详细信息,请查看 生产环境构建指南

几种开启方法:

  • hot: true }, ```

    Note that webpack.HotModuleReplacementPlugin is required to fully enable HMR. If webpack or webpack-dev-server are launched with the --hot option, this plugin will be added automatically, so you may not need to add this to your webpack.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);

  1. 社区还有许多其他 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 完全控制。
  2. HMR原理解析<br />[https://zhuanlan.zhihu.com/p/30669007](https://zhuanlan.zhihu.com/p/30669007)
  3. <a name="866b1a74"></a>
  4. # Tree Shaking
  5. [https://webpack.js.org/guides/tree-shaking/](https://webpack.js.org/guides/tree-shaking/)
  6. _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
  7. 或<br />· [BabelMinifyWebpackPlugin](https://github.com/webpack-contrib/babel-minify-webpack-plugin)<br />· [ClosureCompilerPlugin](https://github.com/roman01la/webpack-closure-compiler)
  8. 你的Tree-Shaking并没什么卵用<br />[https://github.com/wuomzfx/tree-shaking-test](https://github.com/wuomzfx/tree-shaking-test)
  9. <a name="ab5582fe"></a>
  10. # 生产环境构建
  11. [https://webpack.docschina.org/guides/production/](https://webpack.docschina.org/guides/production/)
  12. 配置分离:
  • |- webpack.common.js
  • |- webpack.dev.js
  • |- webpack.prod.js ```

避免在生产中使用 inline-*** eval-***,因为它们可以增加 bundle 大小,并降低整体性能。

  1. plugins: [
  2. + new UglifyJSPlugin({
  3. + sourceMap: true
  4. + })
  5. ]

建议开启sourceMap

配置环境

  1. module.exports = merge(common, {
  2. plugins: [
  3. + new webpack.DefinePlugin({
  4. + 'process.env.NODE_ENV': JSON.stringify('production')
  5. + })
  6. ]
  7. })

任何位于 /src 的本地代码都可以关联到 process.env.NODE_ENV 环境变量

  1. + if (process.env.NODE_ENV !== 'production') {
  2. + console.log('Looks like we are in development mode!');
  3. + }

从 webpack v4 开始, 指定 mode 会自动地配置 DefinePlugin

  1. const merge = require('webpack-merge');
  2. const common = require('./webpack.common.js');
  3. module.exports = merge(common, {
  4. mode: 'production',
  5. });

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)(例如 lodashreact)提取到单独的 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

  1. npm install compression --save //安装
  2. var compression = require(‘compression’); //引入到 express 应用
  3. app.use(compression());//添加这个作为第一个中间件

nginx

  1. gzip on;
  2. gzip_min_length 1000;
  3. gzip_proxied expired no-cache no-store private auth;
  4. gzip_types text/plain application/xml;

http://www.nginx.cn/doc/standard/httpgzip.html

静态gzip

Webpack打包时生成xxx.gz文件
Express或者Nginx里配置对请求返回响应xxx.gz

Webpack配置

  1. //1.安装Webpack压缩插件
  2. npm install compression-webpack-plugin --save-dev
  3. //2. 在webpack.config.prod.js中引入插件
  4. var CompressionPlugin = require('compression-webpack-plugin');
  5. //3. 添加到插件数组中
  6. plugins: [
  7. new webpack.DefinePlugin({
  8. 'process.env': {
  9. 'NODE_ENV': JSON.stringify('production')
  10. }
  11. }),
  12. new webpack.optimize.DedupePlugin(),
  13. new webpack.optimize.UglifyJsPlugin(),
  14. new webpack.optimize.AggressiveMergingPlugin(),
  15. new CompressionPlugin({ <-- 加上这
  16. asset: "[path].gz[query]",
  17. algorithm: "gzip",
  18. test: /\.js$|\.css$|\.html$/,
  19. threshold: 10240,
  20. minRatio: 0.8
  21. })
  22. ]

Express配置

  1. // 在 Express 中添加中间件返回 .js.gz 。所以你仍然可以从HTML加载bundle.js但会收到bundle.js.gz
  2. app.get('*.js', function (req, res, next) {
  3. req.url = req.url + '.gz';
  4. res.set('Content-Encoding', 'gzip');
  5. next();
  6. });

Nginx配置
conf/ngxin.conf里:

  1. gzip_static on;
  2. gzip_http_version 1.1;
  3. gzip_proxied expired no-cache no-store private auth;
  4. gzip_disable "MSIE [1-6]\.";
  5. 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

  1. // windows
  2. set NODE_OPTIONS=--max_old_space_size=4096
  3. // linux
  4. 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拆分的区别