一文学会 webpack(下)

Posted 众小厮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文学会 webpack(下)相关的知识,希望对你有一定的参考价值。

[本篇文章将介绍 webpack 的优化配置,请往下看!]

webpack-split

提取公共代码,现在的前端应用一般都是用框架构建,通常每个页面都是采用同样的技术栈及同一套样式代码,这就导致这些页面之间有很多相同的代码。如果每个页面的代码都将这些公共部分包含进去,则会造成相同资源被重复加载,打包后的资源文件过大而浪费用户流量和增加服务器成本,同时网页首屏加载缓慢,用户体验极差。所以这就需要将公共代码抽离成单独的文件。本节讲解如何运用optimization.splitChunks配置实现抽离公共代码。官网原话:

SplitChunksPlugin
Originally, chunks (and modules imported inside them) were connected by a parent-child relationship in the internal webpack graph. The CommonsChunkPlugin was used to avoid duplicated dependencies across them, but further optimizations were not possible.
Since webpack v4, the CommonsChunkPlugin was removed in favor of optimization.splitChunks.
// 意思是,SplitChunksPlugin 插件用来实现分离公共代码,但 webpack4+ 之后就不用这个插件了,换成使用配置:optimization.splitChunks。

官网示例配置:

 1optimization: {
2    //...
3    splitChunks: {
4        chunks'async',
5        minSize30000,
6        maxSize0,
7        minChunks1,
8        maxAsyncRequests5,
9        maxInitialRequests3,
10        automaticNameDelimiter'~',
11        nametrue,
12        cacheGroups: {
13            vendors: {
14                test/[\\/]node_modules[\\/]/,
15                priority-10
16            },
17            default: {
18                minChunks2,
19                priority-20,
20                reuseExistingChunktrue
21            }
22        }
23    }
24}

配置说明:

本节换用 H5 页面,相关布局设计请下载代码查看,此处仅作为 demo 演示,对 H5 移动端布局感兴趣的话请自行研究;

chunks:这表示将选择哪些块进行优化,value 值有 'all','initial','async';

minSize:块体积最小 30k;

maxSize:块最大体积,推荐是 244k。不过最大最小体积设置有时候只是作为一个 hint, 并不是很严格,如图:

一文学会 webpack(下)

minChunks:设置至少被模块共享的次数,只有满足这个次数的共享模块才会被提取;

maxInitialRequests:初始化时分割块的最大数目。项目中页面初始化用到的依赖(如React, Axios, etc)应该设置其 chunks: 'initial'。chuanks 为 initial 的依赖若超过 maxInitialRequests 值,priority 值较低的都被打包成一个块;

maxAsyncRequests:异步分割块的最大数目,项目中非首屏的页面依赖、由事件触发的业务的依赖(如Echarts)应该设置其 chunks: 'async'。chunks 为 async 的依赖若超过 maxAsyncRequests 值,priority 值较低的都被打包成一个块;

cacheGroups:缓存组,提取公共代码配置。在 output 中设置的chunkFilename: 'js/[name].bundle[chunkhash:6].js'到这里就派上用场了,只要公共文件代码没改变,chunkhash 就不会变,所以能达到长期缓存的作用,如下图,修改 Home.jsx 后执行构建,发现主文件 main 的 hash 值变了,但以 chunkhash 打出来的块,如 antd 的 hash 值没变。

一文学会 webpack(下)

一文学会 webpack(下)

为了说明问题,本节中引入了常用的几个大框架、库文件作为示例,splitChunks 配置如下:

 1optimization: {
2    splitChunks: {
3        nametrue,
4        chunks'all',
5        maxAsyncRequests15,
6        maxInitialRequests10,
7        automaticNameDelimiter'-',
8        cacheGroups: {
9            libs: {
10                name'chunk-libs',
11                test/[\\/]node_modules[\\/]/i,
12                priority10,
13                chunks'initial' // 只打包初始时依赖的第三方
14            },
15            echarts: {
16                name'chunk-echarts',
17                test/[\\/]node_modules[\\/]echarts[\\/]/i,
18                priority15// 权重要大于 libs 和 main, 不然会被打包进 libs 或 main
19                chunks: 'async'
20            },
21            antDesign: {
22                name'chunk-antd',
23                test/[\\/]node_modules[\\/]antd[\\/]/i,
24                priority20,
25                chunks'initial'
26            },
27            lodash: {
28                name'chunk-lodash',
29                test/[\\/]node_modules[\\/]lodash[\\/]/i,
30                priority25,
31                chunks'initial'
32            },
33            react: {
34                name'chunk-react',
35                test/[\\/]node_modules[\\/](react|react-dom)[\\/]/i,
36                priority30// 权重要大于 libs 和 main, 不然会被打包进 libs 或 main
37                chunks: 'initial'
38            }
39        }
40    }
41},
// https://github.com/isChosen/webpack-split

webpack-dll

动态链接库,一个动态链接库可以包含为其他模块调用的函数和数据。web 项目构建接入动态链接库的思想,需要完成以下事情:

将网页依赖的基础模块抽离出来,打包到一个个单独的动态链接库中。在一个动态链接库中可以包含多个模块;

当需要导入的模块在于某个动态链接库中时,这个模块不能被再次打包,而是去动态链接库中获取;

页面依赖的所有动态链接库都需要被加载。

web 项目的构建接入动态链接库的思想后,会大幅提升构建速度,原因在于,包含大量复用模块的动态链接库只需被编译一次,在之后的构建过程中被动态链接库包含的模块将不会重新编译,而是直接使用动态链接库中的代码。由于动态链接库中的大多数包含的是常用的第三方模块,例如 react、react-dom,所以只要不升级这些模块的版本,动态链接库就不用重新编译。

webpack 内置了对动态链接库的支持,通过以下两个内置插件接入:

DllPlugin:用于打包出一个个单独的动态链接库文件,生成 [name].dll.js 和 [name].manifest.json 文件;

DllReferencePlugin:用于在主配置文件(webpack.config.dev.js)中引入构建好的动态链接库文件。

生成动态库文件的配置:webpack.config.dll.dev.js,如下:

 1const path = require('path');
2const webpack = require('webpack');
3const CleanWebpackPlugin = require('clean-webpack-plugin');
4
5module.exports = {
6    mode'development',
7    entry: {
8        // 数组中的值都是依赖的模块路径,位于 node_modules 目录下
9        // 将 React 相关的模块放到一个单独的动态链表库中
10        react: ['react''react-dom'],
11        // 将项目需要所有的 polyfill 放到一个单独的动态链接库中
12        polyfill: ['core-js/fn/object/assign''core-js/fn/promise''whatwg-fetch'],
13        echarts: ['echarts']
14    },
15    output: {
16        // 输出动态链接库的文件名称, [name] 代表当前动态链接库的名称
17        // 也就是 entry 中配置的 react 和 polyfill, etc
18        filename: '[name].dll.js',
19        path: path.resolve(__dirname, 'dist/dll'),
20        // 存放动态链接库的全局变量名称, 例如对于 react 来说就是 _dll_react, 加上 _dll_ 防止全局变量冲突
21        library: '_dll_[name]'
22    },
23    plugins: [
24        new CleanWebpackPlugin(['dist/dll/']),
25        // 接入 DllPlugin
26        new webpack.DllPlugin({
27            // 动态链表库的全局变量名称, 和 output.library 一致
28            // 该字段的值也就是输出的 manifest.json 文件中 name 字段的值,
29            // 例如在 react.manifest.json 中就有 "name": "_dll_react"
30            context: __dirname,
31            name'_dll_[name]',
32            // 描述动态链接库的 manifest.json 文件输出时的文件名称
33            path: path.join(__dirname, 'dist/dll''[name].manifest.json')
34        })
35    ]
36}

执行脚本命令yarn build-dll 看到在 dist/dll 中生成了 [name].dll.js 和 [name].manifest.json 文件;

[name].dll.js: 即动态链接库,包含了大量模块代码,这些模块被存放在一个数组里,用数组的索引号作为 ID。并且通过 _dll_[name] 变量将自己暴露在全局中,即可以通过 window._dll_[name] 访问到其中包含的模块;

[name].manifest.json:用于描述动态链接库文件中包含哪些模块,以及每个模块的路径和 ID;

[name].bundle[hash:6].js 是被打包出来的执行入口文件,在遇到其依赖的模块在 dll.js 文件中时,会直接通过 dll.js 文件暴露的全局变量获取打包在 dll.js 文件中的模块,所以在模板 index.html 中需要将依赖的动态链接库 dll.js 文件手动添加进去,如下图:

一文学会 webpack(下)

动态链接库文件需要独立打包构建:

dll 配置文件:webpack.config.dll.dev.js,构建命令:build-dll

webpack 配置文件:webpack.config.dev.js,构建命令:build-dev

见 package.json 文件,所以说使用动态链接库要构建两次,也可以合并成一次构建,如本地服务server-dev

  如果 dist/dll 下动态链接库文件已存在,则:

"server-dev""webpack-dev-server --config webpack.config.dev.js"

  如果 dist/dll 下动态链接库文件不存在,则:

"server-dev""npm run build-dll && webpack-dev-server --config webpack.config.dev.js"

package.json 设置命令:

1"scripts": {
2    "build-dll""webpack --progress --config webpack.config.dll.dev.js",
3    "build-dev""webpack --progress --config webpack.config.dev.js",
4    "server-dev""npm run build-dll && webpack-dev-server --config webpack.config.dev.js"
5}

动态链接库文件需要接入主配置中才能实现其作用,webpack.config.dev.js 的 plugins 选项中做如下设置:

 1plugins: [
2    new webpack.DllReferencePlugin({
3        manifestrequire('./dist/dll/react.manifest.json')
4    }),
5    new webpack.DllReferencePlugin({
6        manifestrequire('./dist/dll/polyfill.manifest.json')
7    }),
8    new webpack.DllReferencePlugin({
9        manifestrequire('./dist/dll/echarts.manifest.json')
10    })
11]

作为优化配置的第一节 webpack-split(提取公共代码),和本节 webpack-dll(动态链接库)相比,他们的组件代码一样,经配置 dll 之后的编译构建速度在 split 之上(随机对比),如下图。而相对于没做优化处理的 webpack 配置的构建速度会更快。配合下文即将讲解的 webpack-happy(多进程),三者结合的配置使得 webpack 在编译模块和构建项目的打包过程中,速度将更快!

一文学会 webpack(下)

// https://github.com/isChosen/webpack-dll

webpack-happy

原文一句话介绍:

HappyPack makes initial webpack builds faster by transforming files in parallel.

javascript 是单线程语言,但可以开启多进程处理事务。通常地,在构建生产环境代码时会卡在一个时间点(一般是92%那里!),卡在那个时间点其实就是在压缩代码,由于 JavaScript 压缩代码时,需要先将代码解析成用 Object 抽象表示的 AST 语法树,再应用各种规则分析和处理 AST,所以导致这个过程计算量巨大,耗时非常多;

webpack 通过 happypack 依赖开启多进程来编译和压缩文件,将多个文件的编译压缩工作分配给多个子进程去完成,子进程还是通过相同的插件(比如压缩用的是UglifyJS)去处理工作,但这整个过程变成了并行执行。处理完成后将结果返回主进程,所以使用多进程能更快速地完成对多个文件的编译构建和压缩打包任务;

首先安装依赖 happypack 和 os(用来计算计算机核数,确定开启共享进程池的数量),HappyPack 的配置是使用插件和加载器结合一起完成工作的,所以需要配置两处地方,原文如下:

HappyPack provides both a plugin and a loader in order to do its job so you must use both to enable it.
Normally, you define loader rules to tell webpack how to process certain files. With HappyPack, you switch things around so that you pass the loaders to HappyPack's plugin and instead tell webpack to use happypack/loader.

替换 loader 和配置插件 plugins:

 1const os = require('os');
2const HappyPack = require('happypack');
3const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); // js 压缩插件
4const HappyThreadPool = HappyPack.ThreadPool({size: os.cpus().length - 1}); // 共享进程池
5
6optimization: {
7    /*
8    minimizer: [ // production 启用多进程压缩js
9        new UglifyJsPlugin({
10            cache: path.resolve(__dirname, 'dist/cache'),
11            parallel: os.cpus().length - 1
12        })
13    ]
14    */

15},
16
17// rules
18rules: [
19    {
20        test/\.(jsx?|es6)$/,
21        exclude: path.resolve(__dirname, 'node_modules'),
22        use'happypack/loader?id=jsx'
23    },
24    /* node_modules 引入的样式不需要模块化 */
25    {
26        test/\.css$/,
27        include: [path.resolve(__dirname, 'node_modules')],
28        use: [
29            MiniCssExtractPlugin.loader,
30            'happypack/loader?id=cssNodeModules'
31        ]
32    },
33    {
34        test/\.less$/,
35        include: [path.resolve(__dirname, 'node_modules')],
36        use: [
37            MiniCssExtractPlugin.loader,
38            'happypack/loader?id=lessNodeModules'
39        ]
40    },
41    /* 非 node_modules 样式模块化 */
42    {
43        test/\.css$/,
44        exclude: [path.resolve(__dirname, 'node_modules')],
45        use: [
46            MiniCssExtractPlugin.loader,
47            'happypack/loader?id=cssExcNodeModules'
48        ]
49    }
50],
51
52// plugins
53plugins: [
54    // 多进程
55    new HappyPack({
56        id'jsx',
57        loaders: ['babel-loader?cacheDirectory'],
58        threadPool: HappyThreadPool
59    }),
60    new HappyPack({
61        id'cssNodeModules',
62        loaders: [
63            'css-loader',
64            'postcss-loader'
65        ],
66        threadPool: HappyThreadPool
67    }),
68    new HappyPack({
69        id'lessNodeModules',
70        loaders: [
71            'css-loader',
72            'postcss-loader',
73            'less-loader'
74        ],
75        threadPool: HappyThreadPool
76    }),
77    new HappyPack({
78        id'cssExcNodeModules',
79        loaders: [
80            {
81                loader'css-loader',
82                options: {
83                    modulestrue,
84                    importLoaders1,
85                    localIdentName'[local]-[hash:base64:4]'
86                }
87            },
88            'postcss-loader',
89        ],
90        threadPool: HappyThreadPool
91    })
92]

说明:

将 rules 中的 loader 替换成 happypack/loader,在 plugins 中配置 happypack,这两处的配置须设置相同的 id 才能对应得上。本文编译处理样式模块使用了模块化(CSS Modules)和提取分离样式(MiniCssExtractPlugin),在配置样式的 loader 时,若将 MiniCssExtractPlugin 提供的 loader 整合在 happypack 插件中会出现问题,不能正常使用,样式模块只有在经过 happypack/loader 处理之后再使用 MiniCSSExtractPlugin.loader 提取分离,才能正常工作,所以在 loader 中他俩是并级的;

处理 js 模块时设置缓存 cacheDirectory,能提升下一次的编译构建速度;一般配置多进程的是编译处理 js 和 css 模块,图片字体文件等就不用配置了;

构建生产环境的代码

a. 压缩 js,启用多进程:

1optimization: {
2    minimizer: [
3        new UglifyJsPlugin({
4            cache: path.resolve(__dirname, 'dist/cache'),
5            parallel: os.cpus().length - 1
6        })
7    ]
8}

b. 压缩 css,此处没启用多进程,用的是 'optimize-css-assets-webpack-plugin' 插件,读者可自行探索:

 1plugins: [
2    new webpack.BannerPlugin('@CopyRight-Detcx'),
3    // 压缩分离出来的所有 css
4    new OptimizeCssAssetsPlugin({
5      cssProcessorrequire('cssnano'), // 需要安装依赖 cssnano
6      cssProcessorOptions: {
7        discardComments: {removeAlltrue}
8      },
9      canPrinttrue // 是否将插件信息打印到控制台
10    })
11]

c. 构建过程如下截图,说明多进程起作用了:

一文学会 webpack(下)

// https://github.com/isChosen/webpack-happy

webpack-onDemand

为什么要按需加载:

随着互联网的发展,一个网页承载的功能越来越多,采用单页面应用作为前端架构的网站会面临着网页需要加载的代码量很大的问题,因为许多功能都被集中整到一个 HTML 里,这会导致网页加载缓慢、交互卡顿,使用户体验非常糟糕。导致这个问题的根本原因在于一次性加载所有功能对应的代码,但其实用户在每个阶段只可能使用其中一部分功能,所以解决以上问题的方法就是用户当前需要用什么功能就只加载这个功能对应的代码,也就是所谓的按需加载。

按需加载的原则:

将整个网站划分成一个个小功能,再按照每个功能的相关程度将他们分成几类;

将每一类合并为一个 Chunk,按需加载对应的 Chunk;

用户首次打开网站时需要看到的画面所对应的功能,不用按需加载,将其放到执行入口所在的 Chunk 中,以减少用户能感知的网页加载时间;

对于不依赖大量代码的功能点,例如依赖 echarts.js 去画图表、依赖 flv.js 去播放视频的功能点,可再对其进行按需加载;

被分割出去的代码的加载需要一定的时机去触发,即当用户操作到了或者即将操作到对应的功能时再去加载对应的代码,被分割出去的代码的加载时机需要开发者根据网页的需求去衡量和确定;

由于被分割出去进行按需加载的代码在加载的过程中也需要耗时,所以可以预估用户接下来可能会进行的操作,并提前加载对应的代码,让用户感知不到网络加载。

思路理解:

前端应用框架构建的单页面应用中,一般都要配置路由来呈现不同的视图,通常地,不同的路由对应不同的组件,相当于对应不同的页面,切换路由可以看做是加载了另一个页面,加载了不同功能模块的代码。所以我们这里以路由作为分割点,按需加载不同功能模块的代码;

项目结构:

本节重新整理了项目的目录结构,如下图:

一文学会 webpack(下)

图中,src 目录,只存放组件,静态资源和其他库文件都放在同级的 static 目录下。entry.jsx 和 index.jsx 都是项目入口,不同的是 index.jsx 是以对象的形式来配置路由,而 entry.jsx 是以路由标签的形式(JSX语法)来配置路由的,因为 jsx 语法比较简单明了,所以本项目的入口文件是 entry.jsx 而不是 index.jsx。 感兴趣的同学可以对比下这两种写法。

实现原理:

本节介绍以路由作为分割点来实现项目按需加载的性能优化,技术栈和版本号为:React@16.5.2、react-router@3.2.1 和 webpack@4+;

react-router@4- 推荐使用 require.ensure() 方式配置路由按需加载,react-router@4+ 推荐使用 import(/**/) 或者 react-loadable 方式配置按需加载;

以路由作为分割点,所以配置路由所对应的组件就不能像往常那么书写了,component 要换成 getComponent,还需使用关键方法 require.ensure 来加载组件,App.jsx 不需要按需加载。entry.jsx 示例如下:

 1import React from 'react';
2import ReactDom from 'react-dom';
3import App from './components/App';
4import { Router, Route, IndexRoute, browserHistory } from 'react-router';
5
6// 提取加载组件的方法到一个配置文件(RouteConf.jsx)中,可使本文件更简洁
7const Login = (nextState, cb) => {
8    require.ensure([], require => {
9        cb(nullrequire('./components/Login').default);
10    }, 'chunk-login');
11}
12const Home = (nextState, cb) => {
13    require.ensure([], require => {
14        cb(nullrequire('./components/Home').default);
15    }, 'chunk-home');
16}
17const NoMatch = (nextState, cb) => {
18    require.ensure([], require => {
19        cb(nullrequire('./components/NoMatch').default);
20    }, 'chunk-nomatch');
21}
22
23const rootRoute = (
24    <Route path='/' component={App} >
25        <IndexRoute getComponent={Login}/>
26        <Route path='home' getComponent={Home} />
27        <Route path='*' getComponent={NoMatch} />
28    </Route>
29)
30
31ReactDom.render(
32    <Router routes={rootRoute} history={browserHistory} />,
33    document.getElementById('root')
34)

Note: default 用来处理 ES6 语法的导出的模块。

扩展

Scope Hoisting

Scope Hoisting 可以让 webpack 打包出来的代码文件更小、运行更快。原理是它会分析模块之间的依赖关系,尽可能将被打散的模块合并到一个函数中,但前提是不能造成代码冗余。因此只有那些被引用了一次的模块才能被合并。开启 Scope Hoisting 后,代码在运行时因为创建的函数作用域变少了,所以内存开销也变小了;

Scope Hoisting 依赖源码时需采用 ES6 模块化语法,还需配置 mainFields。因为大部分 npm 中的第三方库采用 commonjs 语法,但部分库会同时提供两种语法模块化的代码,所以为了充分发挥 Scope Hoisting 的作用,需要增加以下配置:

1resolve: {
2    mainFields: ["jsnext:main""browser""main"// 配合 Scope Hoisting
3},
4// plugins
5plugins: [
6    new webpack.optimize.ModuleConcatenationPlugin(), // Scope Hoisting
7]

输出分析

webpack-bundle-analyzer 可以很方便地让我们知道:

  打包出的文件中都包含了什么;

  每个文件尺寸在总体中的占比,一眼看出哪些文件的尺寸大和模块之间的包含关系;

  每个文件 Gzip 后的大小。

配置 webpack-bundle-analyzer 很简单,只要设置端口号即可。一般在 build-dev 时可以打开配置,在 server-dev 时,关闭配置(将其注释掉),配置如下:

1plugins: [
2    new BundleAnalyzerPlugin({
3        analyzerPort2018,
4        generateStatsFiletrue // 是否需要输出分析文件,默认路径是 dist, 文件名是 stats.json
5    })
6]

Scope Hoisting 开启前后对比,如下图所示。当你看到模块的依赖关系后,觉得某些第三方依赖(如react-router)比较大,可以考虑 split 提取分离出去或者弄成 dll等。截图如下:

一文学会 webpack(下)

一文学会 webpack(下)

// https://github.com/isChosen/webpack-onDemand

webpack-onDemand-v4

react-router4+ 与之前的版本有很大的区别,最大的不同是 router 在项目中的位置:

v2/v3 版本采用的方式是将路由看成是一个整体的单元,与别的组件是分离的,一般会单独放到一个 router 文件中,对其进行集中式管理;并且,布局和页面的嵌套关系由路由的嵌套关系所决定;

v4 版本则将路由进行了拆分,将其放到了各自的模块中,不再有单独的 router 模块,从分体现了组件化的思想。通俗地说,react-router 在 v4 之后本身也是一个组件,可以像引用其他组件一样放置路由(如本节项目中的 Product.jsx 组件设置的子路由)。angular 还需要一个插座(router-outlet),而 react-router4+ 可以根据需要放置在任意组件中以及任意 jsx 节点中,变得更加灵活了;

若想进一步学习 react-router4+ 请参阅官方文档:https://reacttraining.com/react-router/web/guides/philosophy

本节项目设置比较简单,目录结构如下图:

一文学会 webpack(下)

本节项目中,设置了三个入口文件:

index.jsx: 传统的路由配置,没有使用按需加载;

main.jsx: 使用 webpack 的方式来配置路由,实现按需加载;

entry.jsx: 使用 react-loadable 依赖来配置路由,实现按需加载。

App.jsx: 不需要动态加载,以路由作为分割点来配置按需加载组件,只有切换组件时才会加载对应组件的 js 脚本,请自行启动项目打开控制台的 Network 查看。

一文学会 webpack(下)

注意,以 webpack 方式按需加载需要安装依赖 '@babel/plugin-syntax-dynamic-import',而且 .babelrc 文件要将其添加进去,如下:

1{
2    "presets": [
3        "@babel/preset-env",
4        "@babel/preset-react"
5    ],
6    "plugins": ["@babel/plugin-syntax-dynamic-import"]
7}
// https://github.com/isChosen/webpack-onDemand-v4

输出分析

在 webpack-onDemand 的扩展中已经介绍了。

配置建议

介绍 webpack-onDemand 那节内容时,重新整理了项目结构,使其更符合我们搭建项目的目录设置习惯和工程输出完整性。webpack-onDemand 可以作为一个项目模板来参考!

这里将给出几个示例配置文件,分为开发环境和生产环境的配置。生产环境的配置中主要增加了对图片和代码的压缩设置,webpack 在生产环境下的会自动压缩 js 代码,但不会压缩 css 代码,而且在压缩 js 时速度很慢。所以,需要我们手动添加配置来压缩 css 和启用多进程压缩 js,以提升编译速度和打包速度;

关于外部文件(React管理不到的范围)是用copy-webpack-plugin 插件拷贝到目标路径 dist 下的,这个操作只是单纯地 copy 输出,并没有对拷贝的文件(如css/js/图片等)进行压缩。所以,要输出生产环境的代码时,请先自行压缩得到相应的压缩文件([name].min.[ext]),webpack.config.pro.js 拷贝插件如下代码所示:

1// 复制静态资源
2new CopyWebpackPlugin([
3    {
4        from'static/css/*.min.css',
5        to'static/css/[name].[ext]',
6        toType'template'
7    }
8])

如果使用 vs code 编译器可以安装 minify 插件来压缩文件,如下图所示:

问题:既然用了 webpack 还要手动去压缩得到压缩文件,这不符合工程化和配置集中化的理念。有朋友说可以在 package.json 的 scripts 中设置命令,如:

"build-pro""minify static/*.css && webpack --progress --config webpack.config.pro.js",

可是我整不出来!此处表示能力有限,如果有哪位朋友实现了这个功能,欢迎在留言区贴 demo 连接;

Development

  webpack.config.dll.dev.js

  webpack.config.dev.js

Production

webpack.config.dll.pro.js

webpack.config.pro.js

// https://github.com/isChosen/webpack-suggestion
// https://github.com/isChosen/webpack-onDemand-more

结束语

本次介绍的 webpack 最初是在公司里做分享演示用的,但由于内容较多,篇幅较长,导致分享达不到预期的效果,所以整理成了这篇文章;

建议下载和查看代码,结合代码阅读,动手练习,可以更好地理解文中所介绍的内容。学习需要投入一定的时间和耐心,请务必坚持到底;

本篇文章介绍了 webpack 的基本配置,同时也进一步介绍了优化配置,但并不完全,优化配置还可以有更多。另外文中也没涉及原理分析和自定义加载器插件部分,关于原理等更多内容,推荐去看看吴浩麟写的书《深入浅出Webpack》,写得很好,我从中学到了很多;

关于低版本 webpack 迁移的事情,我觉得没必要昂,学了本文,就应该直接换成 webpack4+,而且由于各大前端框架都有自己的脚手架工具,导致 webpack 的优势越来越小了,可以看出 webpack 的发展将趋向于简单化,所以建议不要再使用 webpack3 以下版本了。相对于其他脚手架工具来说,我觉得 webpack 还是很灵活的;

由于谁也逃不出王境泽定律,面面俱到这辈子是不可能的,永远都不可能的了!有错误在所难免,如遇到不明白的配置,建议多去 webpack、npm 和 github 这类网站上进行查找、验证和对比,也欢迎真香留言,提醒其他朋友,但我是不会改正的!

在学习 webpack 的过程中都挺顺利的,就是整理成文章和处理 demo 花了很多很多时间,同时也感谢身边提供建议和帮助的同事,谢谢你们;

希望这篇文章和示例 demo 能让更多的朋友学会 webpack,如果这篇文章对你有帮助,哪怕是小小的帮助,我感觉都没白费力气,这也算是我对社区的一点贡献吧!

最后,欢迎转发,欢迎Fork,谢谢点赞,谢谢阅读!

以上是关于一文学会 webpack(下)的主要内容,如果未能解决你的问题,请参考以下文章

一文搞懂 Webpack 多入口配置

一文学会Java的交互式编程环境jshell

一文学会Java的交互式编程环境jshell

一文学会Java的交互式编程环境jshell

一文彻底搞懂webpack devtool

一文理解Linux的基本指令(下)(三分钟学会Linux基本指令)