引言 webpack5 预计会在 2020 年年初发布,之前从 alpha 版本就有关注,本次重点更新在长期缓存,tree shakking 和 es6 打包这块。具体变更可以参考https://github.com/webpack/changelog-v5/blob/master/README.md 。
webpack 是现代前端开发中最火的模块打包工具,只需要通过简单的配置,便可以完成模块的加载和打包。那它是怎么做到通过对一些插件的配置,便可以轻松实现对代码的构建呢?
本篇文章不会去探讨 webpack5 中所要更新的内容,我相信大多数前端同学对于 webpack 只是会简单的配置,而且现在像 vue-cli、umi 等对于 webpack 都有很好的封装,但其实这样对于我们自己是不太好的。尤其是想针对业务场景去做一些个性化的定制时。只有对 webpack 中的细节足够了解,我们才能游刃有余,本文将从 webpack 现有的大版本 webpack4,带你一步步打造极致的前端开发环境。
安装 webpack 的几种方式
global
(全局):通过 webpack index.js
运行
local
(项目维度安装):通过 npx webpack index.js
运行
避免全局安装 webpack(针对多个项目采用不同的 webpack 版本进行打包的场景),可采用npx
entry(入口) 单一入口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const config = { entry : { main : "./src/index.js" } }; ### 多入口 `` `js // webpack.config.js const config = { entry: { main: "./src/index.js", sub: "./src/sub.js" } };
output(输出) 默认配置 1 2 3 4 5 6 7 8 9 10 11 12 const path = require ('path' );... const config = { output : { filename : 'bundle.js' , path : path.resolve (__dirname, 'dist' ) } }; module .exports = config;
多个入口起点
如果配置创建了多个单独的 “chunk”(例如,使用多个入口起点或使用像 CommonsChunkPlugin 这样的插件),则应该使用占位符(substitutions)来确保每个文件具有唯一的名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const path = require ('path' );{ entry : { main : './src/index.js' , sub : './src/sub.js' }, output : { filename : '[name].js' , path : path.resolve (__dirname, 'dist' ) } }
高级进阶
使用 cdn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const path = require ('path' );{ entry : { main : './src/index.js' , sub : './src/sub.js' }, output : { publicPath : 'http://cdn.example.com' filename : '[name].js' , path : path.resolve (__dirname, 'dist' ) } }
loaders
webpack 可以使用 loader 来预处理文件。这允许你打包除 JavaScript 之外的任何静态资源。
file-loader
file-loader 可以解析项目中的 url 引入(不仅限于 css),根据我们的配置,将图片拷贝到相应的路径,再根据我们的配置,修改打包后文件引用路径,使之指向正确的文件。
默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名。
1 2 3 4 5 6 7 8 9 10 11 12 rules : [ { test : /\.(jpg|png|gif)$/ , use : { loader : "file-loader" , options : { name : "[name]_[hash].[ext]" , outputPath : "images/" } } } ];
url-loader
url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
url-loader 把资源文件转换为 URL,file-loader 也是一样的功能。不同之处在于 url-loader 更加灵活,它可以把小文件转换为 base64 格式的 URL,从而减少网络请求次数。url-loader 依赖 file-loader。
1 2 3 4 5 6 7 8 9 10 11 12 13 rules : [ { test : /\.(jpg|png|gif)$/ , use : { loader : "url-loader" , options : { name : "[name]_[hash].[ext]" , outputPath : "images/" , limit : 204800 } } } ];
css-loader
只负责加载 css 模块,不会将加载的 css 样式应用到 html
importLoaders 用于指定在 css-loader 前应用的 loader 的数量
查询参数 modules 会启用 CSS 模块规范
1 2 3 4 5 6 7 8 module : { rules : [ { test : /\.css$/ , use : ["style-loader" , "css-loader" ] } ]; }
style-loader
负责将 css-loader 加载到的 css 样式动态的添加到 html-head-style 标签中
一般建议将 style-loader 与 css-loader 结合使用
sass-loader 安装 1 yarn add sass-loader node-sass webpack --dev
node-sass 和 webpack 是 sass-loader 的 peerDependency,因此能够精确控制它们的版本。
loader 执行顺序:从下至上,从右至左
通过将 style-loader 和 css-loader 与 sass-loader 链式调用,可以立刻将样式作用在 DOM 元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 module .exports = {... module : { rules : [{ test : /\.scss$/ , use : [{ loader : "style-loader" }, { loader : "css-loader" }, { loader : "sass-loader" }] }] } };
postcss-loader
webpack4 中使用 postcss-loader 代替 autoprefixer,给 css3 样式加浏览器前缀。具体可参考https://blog.csdn.net/u014628388/article/details/82593185
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { test : /\.scss$/ , use : [ 'style-loader' , 'css-loader' , 'sass-loader' , 'postcss-loader' ], } module .exports = { plugins : [ require ('autoprefixer' )({ browsers : ['last 2 versions' ] }), ], };
plugins
plugin 可以在 webpack 运行到某个时刻的时候,帮你做一些事情
HtmlWebpackPlugin
HtmlWebpackPlugin 会在打包结束后,自动生成一个 html 文件,并把打包生成的 js 自动引入到这个 html 文件中
1 2 3 4 5 6 7 8 9 10 const HtmlWebpackPlugin = require ('html-webpack-plugin' );module .exports = {... plugins : [ new HtmlWebpackPlugin ({ template : 'src/index.html' }), ], };
clean-webpack-plugin
clean-webpack-plugin 插件用来清除残留打包文件,特别是文件末尾添加了 hash 之后,会导致改变文件内容后重新打包时,文件名不同而内容越来越多。
新版本中的 clean-webpack-plugin 仅接受一个对象,默认不需要传任何参数。具体可参考https://blog.csdn.net/qq_23521659/article/details/88353708
1 2 3 4 5 6 7 8 9 10 11 const { CleanWebpackPlugin } = require ('clean-webpack-plugin' );module .exports = {... plugins : [ new CleanWebpackPlugin () ], output : { filename : 'bundle.js' , path : path.resolve (__dirname, 'dist' ) }
SplitChunksPlugin
具体概念可参考https://juejin.im/post/5af15e895188256715479a9a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 splitChunks : { chunks : "async" , minSize : 30000 , minChunks : 1 , maxAsyncRequests : 5 , maxInitialRequests : 3 , automaticNameDelimiter : '~' , name : true , cacheGroups : { vendors : { test : /[\\/]node_modules[\\/]/ , priority : -10 }, default : { minChunks : 2 , priority : -20 , reuseExistingChunk : true } } }
将 CSS 提取为独立的文件的插件,对每个包含 css 的 js 文件都会创建一个 CSS 文件,支持按需加载 css 和 sourceMap
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 const MiniCssExtractPlugin = require ("mini-css-extract-plugin" );module .exports = { plugins : [ new MiniCssExtractPlugin ({ filename : "[name].css" , chunkFilename : "[id].css" }) ], module : { rules : [ { test : /\.scss$/ , use : [ { loader : MiniCssExtractPlugin .loader }, { loader : "css-loader" , options : { importLoaders : 2 } }, "sass-loader" , "postcss-loader" ] }, { test : /\.css$/ , use : [ { loader : MiniCssExtractPlugin .loader }, "css-loader" , "postcss-loader" ] } ] } };
OptimizeCSSAssetsPlugin
webpack5 可能会内置 CSS 压缩器,webpack4 需要自己使用压缩器,可以使用 optimize-css-assets-webpack-plugin 插件。 设置 optimization.minimizer 覆盖 webpack 默认提供的,确保也指定一个 JS 压缩器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 const UglifyJsPlugin = require ("uglifyjs-webpack-plugin" );const MiniCssExtractPlugin = require ("mini-css-extract-plugin" );const OptimizeCSSAssetsPlugin = require ("optimize-css-assets-webpack-plugin" );module .exports = { optimization : { minimizer : [ new UglifyJsPlugin ({ cache : true , parallel : true , sourcMap : true }), new OptimizeCSSAssetsPlugin ({}) ] }, plugins : [ new MiniCssExtractPlugin ({ filename : "[name].css" , chunkFilename : "[id].css" }) ], module : { rules : [ { test : /\.css$/ , use : [MiniCssExtractPlugin .loader , "css-loader" ] } ] } };
source map
source map 就是对打包生成的代码与源代码的一种映射,主要是为了方便定位问题和排查问题。devtool 关键有 eval、cheap、module、inline 和 source-map 这几块,具体可参考文档:https://www.webpackjs.com/configuration/devtool/
development 环境参考配置: 'cheap-module-eval-source-map'
production 环境参考配置: 'cheap-module-source-map'
webpack-dev-server
webpack-dev-server 提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。具体可参考https://www.webpackjs.com/guides/development/#%E4%BD%BF%E7%94%A8-webpack-dev-server
接口代理(请求转发)
如果你有单独的后端开发服务器 API,并且希望在同域名下发送 API 请求 ,那么代理某些 URL 会很有用。dev-server 使用了非常强大的 http-proxy-middleware
包。常用于接口请求转发。具体参考https://www.webpackjs.com/configuration/dev-server/#devserver-proxy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 devServer : { contentBase : "./dist" , open : true , hot : true , hotOnly : true , proxy : { "/api" : { target : "https://other-server.example.com" , pathRewrite : {"^/api" : "" }, secure : false , bypass : function (req, res, proxyOptions ) { if (req.headers .accept .indexOf ("html" ) !== -1 ) { console .log ("Skipping proxy for browser request." ); return "/index.html" ; } } } } },
解决单页面路由问题
当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html 通过传入以下启用:
1 historyApiFallback: true;
通过传入一个对象,比如使用 rewrites 这个选项,此行为可进一步地控制:
1 2 3 4 5 6 7 historyApiFallback : { rewrites : [ { from : /^\/$/ , to : "/views/landing.html" }, { from : /^\/subpage/ , to : "/views/subpage.html" }, { from : /./ , to : "/views/404.html" } ]; }
webpack-dev-middleware
webpack-dev-middleware 是一个容器(wrapper),它可以把 webpack 处理后的文件传递给一个服务器(server)。 webpack-dev-server 在内部使用了它,同时,它也可以作为一个单独的包来使用,以便进行更多自定义设置来实现更多的需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const express = require ("express" );const webpack = require ("webpack" );const webpackDevMiddleware = require ("webpack-dev-middleware" );const config = require ("./webpack.config.js" );const complier = webpack (config);const app = express ();app.use ( webpackDevMiddleware (complier, { publicPath : config.output .publicPath }) ); app.listen (3000 , () => { console .log ("server is running" ); });
Hot Module Replacement
模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新各种模块,而无需进行完全刷新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ... const webpack = require ('webpack' );... devServer : { contentBase : './dist' , open : true , hot : true , hotOnly : true }, plugins : [ ... new webpack.HotModuleReplacementPlugin () ],
如果已经通过 HotModuleReplacementPlugin 启用了模块热替换(Hot Module Replacement),则它的接口将被暴露在 module.hot 属性下面。通常,用户先要检查这个接口是否可访问,然后再开始使用它。
1 2 3 4 5 6 if (module .hot ) { module .hot .accept ("./library.js" , function ( ) { }); }
bundle 分析
借助一些官方推荐的可视化分析工具,可对打包后的模块进行分析以及优化
webpack-chart
: webpack 数据交互饼图
webpack-visualizer
: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的
webpack-bundle-analyzer
: 一款分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户
Preloading、Prefetching
prefetch:会等待核心代码加载完成后,页面带宽空闲后再去加载 prefectch 对应的文件;preload:和主文件一起去加载
可以使用谷歌浏览器 Coverage 工具查看代码覆盖率(ctrl+shift+p > show coverage)
使用异步引入 js 的方式可以提高 js 的使用率,所以 webpack 建议我们多使用异步引入的方式,这也是 splitChunks.chunks 的默认值是”async”的原因
使用魔法注释 /_ webpackPrefetch: true _/ ,这样在主要 js 加载完,带宽有空闲时,会自动下载需要引入的 js
使用魔法注释 /_ webpackPreload: true _/,区别是 webpackPrefetch 会等到主业务文件加载完,带宽有空闲时再去下载 js,而 preload 是和主业务文件一起加载的
babel babel 编译 es6、jsx 等
@babel/core babel 核心模块
@babel-preset-env 编译 es6 等
@babel/preset-react 转换 jsx
@babel/plugin-transform-runtime 避免 polyfill 污染全局变量,减少打包体积
@babel/polyfill es6 内置方法和函数转化垫片
@babel/runtime
1 2 3 4 5 6 7 8 9 10 11 module : { rules : [ { test : /\.js$/ , exclude : /node_modules/ , use : { loader : "babel-loader" } } ]; }
新建.babelrc 文件
1 2 3 4 { "presets" : [ "@babel/preset-env" , "@babel/preset-react" ] , "plugins" : [ "@babel/plugin-transform-runtime" ] }
按需引入 polyfill 在 src 下的 index.js 中全局引入@babel/polyfill 并写入 es6 语法,但是这样有一个缺点: 全局引入@babel/polyfill 的这种方式可能会导入代码中不需要的 polyfill,从而使打包体积更大,修改.babelrc 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 `yarn add core-js@2 @babel/runtime-corejs2 --dev` { "presets" : [ [ "@babel/preset-env" , { "useBuiltIns" : "usage" } ] , "@babel/preset-react" ] , "plugins" : [ "@babel/plugin-transform-runtime" ] }
这就配置好了按需引入。配置了按需引入 polyfill 后,用到 es6 以上的函数,babel 会自动导入相关的 polyfill,这样能大大减少打包编译后的体积。
babel-runtime 和 babel-polyfill 的区别
参考https://www.jianshu.com/p/73ba084795ce
babel-polyfill 会”加载整个 polyfill 库”,针对编译的代码中新的 API 进行处理,并且在代码中插入一些帮助函数
babel-polyfill 解决了 Babel 不转换新 API 的问题,但是直接在代码中插入帮助函数,会导致污染了全局环境,并且不同的代码文件中包含重复的代码,导致编译后的代码体积变大。 Babel 为了解决这个问题,提供了单独的包 babel-runtime 用以提供编译模块的工具函数, 启用插件 babel-plugin-transform-runtime 后,Babel 就会使用 babel-runtime 下的工具函数
babel-runtime 适合在组件,类库项目中使用,而 babel-polyfill 适合在业务项目中使用。
高级概念 tree shaking(js)
tree shaking 可清除代码中无用的 js 代码,只支持 import 方式引入,不支持 commonjs 的方式引入 mode 是 production 的无需配置,下面的配置是针对 development 的
1 2 3 4 5 6 7 8 optimization : { usedExports : true } "sideEffects" : false ,
Code Spliting
代码分割,和 webpack 无关
同步代码(需在 webpack.config.js 中配置 optimization)
1 2 3 4 5 6 7 8 9 10 11 import _ from 'lodash' ;console .log (_.join (['a' ,'b' ,'c' ], '****' ))optimization : { splitChunks : { chunks : 'all' } },
异步代码(无需任何配置,但需安装@babel/plugin-syntax-dynamic-import
包)
1 2 3 4 5 6 7 8 9 10 11 12 function getComponent ( ) { return import ("lodash" ).then (({ default : _ } ) => { const element = document .createElement ("div" ); element.innerHTML = _.join (["Jack" , "Cool" ], "-" ); return element; }); } getComponent ().then (el => { document .body .appendChild (el); });
Caching(缓存)
通过使用 output.filename 进行文件名替换,可以确保浏览器获取到修改后的文件。[hash] 替换可以用于在文件名中包含一个构建相关(build-specific)的 hash,但是更好的方式是使用 [contenthash] 替换,当文件内容发生变化时,[contenthash]也会发生变化
1 2 3 4 output : { filename : "[name].[contenthash].js" , chunkFilename : '[name].[contenthash].chunk.js' }
Shimming
webpack 编译器(compiler)能够识别遵循 ES2015 模块语法、CommonJS 或 AMD 规范编写的模块。然而,一些第三方的库(library)可能会引用一些全局依赖(例如 jQuery 中的 $)。这些库也可能创建一些需要被导出的全局变量。这些“不符合规范的模块”就是 shimming 发挥作用的地方
shimming 全局变量(第三方库)(ProvidePlugin 相当于一个垫片)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const path = require ('path' );+ const webpack = require ('webpack' ); module .exports = { entry : './src/index.js' , output : { filename : 'bundle.js' , path : path.resolve (__dirname, 'dist' ) - } + }, + plugins : [ + new webpack.ProvidePlugin ({ + _ : 'lodash' + }) + ] };
细粒度 shimming(this 指向 window)(需要安装 imports-loader 依赖)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const path = require ('path' ); const webpack = require ('webpack' ); module .exports = { entry : './src/index.js' , output : { filename : 'bundle.js' , path : path.resolve (__dirname, 'dist' ) }, + module : { + rules : [ + { + test : require .resolve ('index.js' ), + use : 'imports-loader?this=>window' + } + ] + }, plugins : [ new webpack.ProvidePlugin ({ join : ['lodash' , 'join' ] }) ] };
环境变量
webpack 命令行环境选项 –env 允许您传入任意数量的环境变量。您的环境变量将可访问 webpack.config.js。例如,–env.production 或–env.NODE_ENV=local
1 webpack --env.NODE_ENV=local --env.production --progress
使用环境变量必须对 webpack 配置进行一项更改。通常,module.exports 指向配置对象。要使用该 env 变量,必须转换 module.exports 为函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const path = require ("path" );module .exports = env => { console .log ("NODE_ENV: " , env.NODE_ENV ); console .log ("Production: " , env.production ); return { entry : "./src/index.js" , output : { filename : "bundle.js" , path : path.resolve (__dirname, "dist" ) } }; };
library 打包配置
除了打包应用程序代码,webpack 还可以用于打包 JavaScript library 用户应该能够通过以下方式访问 library:
ES2015 模块。例如 import library from ‘library’
CommonJS 模块。例如 require(‘library’)
全局变量,当通过 script 脚本引入时
我们打包的 library 中可能会用到一些第三方库,诸如 lodash。现在,如果执行 webpack,你会发现创建了一个非常巨大的文件。如果你查看这个文件,会看到 lodash 也被打包到代码中。在这种场景中,我们更倾向于把 lodash 当作 peerDependency。也就是说,用户应该已经将 lodash 安装好。因此,你可以放弃对外部 library 的控制,而是将控制权让给使用 library 的用户。这可以使用 externals 配置来完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var path = require ('path' ); module .exports = { entry : './src/index.js' , output : { path : path.resolve (__dirname, 'dist' ), filename : 'webpack-numbers.js' - } + }, + externals : { + lodash : { + commonjs : 'lodash' , + commonjs2 : 'lodash' , + amd : 'lodash' , + root : '_' + } + } };
对于用途广泛的 library,我们希望它能够兼容不同的环境,例如 CommonJS,AMD,Node.js 或者作为一个全局变量。为了让你的 library 能够在各种用户环境(consumption)中可用,需要在 output 中添加 library 属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var path = require ('path' ); module .exports = { entry : './src/index.js' , output : { path : path.resolve (__dirname, 'dist' ), - filename : 'library.js' + filename : 'library.js' , + library : 'library' }, externals : { lodash : { commonjs : 'lodash' , commonjs2 : 'lodash' , amd : 'lodash' , root : '_' } } };
当你在 import 引入模块时,这可以将你的 library bundle 暴露为名为 webpackNumbers 的全局变量。为了让 library 和其他环境兼容,还需要在配置文件中添加 libraryTarget 属性。这是可以控制 library 如何以不同方式暴露的选项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var path = require ('path' ); module .exports = { entry : './src/index.js' , output : { path : path.resolve (__dirname, 'dist' ), filename : 'library.js' , + library : 'library' , + libraryTarget : 'umd' }, externals : { lodash : { commonjs : 'lodash' , commonjs2 : 'lodash' , amd : 'lodash' , root : '_' } } };
我们还需要通过设置 package.json 中的 main 字段,添加生成 bundle 的文件路径。
1 2 3 4 5 6 { ... "main" : "dist/library.js" , ... }
PWA 打包配置
渐进式网络应用程序(Progressive Web Application - PWA),是一种可以提供类似于原生应用程序(native app)体验的网络应用程序(web app)。PWA 可以用来做很多事。其中最重要的是,在离线(offline)时应用程序能够继续运行功能。这是通过使用名为 Service Workers 的网络技术来实现的 添加 workbox-webpack-plugin 插件,并调整 webpack.config.js 文件:
1 npm install workbox-webpack-plugin --save-dev
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const path = require ('path' ); const HtmlWebpackPlugin = require ('html-webpack-plugin' ); const CleanWebpackPlugin = require ('clean-webpack-plugin' ); + const WorkboxPlugin = require ('workbox-webpack-plugin' ); module .exports = { entry : { app : './src/index.js' , print : './src/print.js' }, plugins : [ new CleanWebpackPlugin (['dist' ]), new HtmlWebpackPlugin ({ - title : 'Output Management' + title : 'Progressive Web Application' - }) + }), + new WorkboxPlugin .GenerateSW ({ + + + clientsClaim : true , + skipWaiting : true + }) ], output : { filename : '[name].bundle.js' , path : path.resolve (__dirname, 'dist' ) } };
注册 Service Worker
1 2 3 4 5 6 7 8 9 10 11 12 import _ from 'lodash' ; import printMe from './print.js' ; + if ('serviceWorker' in navigator) { + window .addEventListener ('load' , () => { + navigator.serviceWorker .register ('/sw.js' ).then (registration => { + console .log ('SW registered: ' , registration); + }).catch (registrationError => { + console .log ('SW registration failed: ' , registrationError); + }); + }); + }
现在来进行测试。停止服务器并刷新页面。如果浏览器能够支持 Service Worker,你应该可以看到你的应用程序还在正常运行。然而,服务器已经停止了服务,此刻是 Service Worker 在提供服务。
TypeScript 打包配置
可参考https://www.webpackjs.com/guides/typescript/
或https://webpack.js.org/guides/typescript/
安装 ts 依赖npm install --save-dev typescript ts-loader
增加 tsconfig.json 配置文件
1 2 3 4 5 6 7 8 9 10 { "compilerOptions" : { "outDir" : "./dist/" , "noImplicitAny" : true , "module" : "es6" , "target" : "es5" , "jsx" : "react" , "allowJs" : true } }
webpack.config.js 添加对 ts/tsx 语法支持(ts-loader)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const path = require ("path" );module .exports = { entry : "./src/index.ts" , module : { rules : [ { test : /\.tsx?$/ , use : "ts-loader" , exclude : /node_modules/ } ] }, resolve : { extensions : [".tsx" , ".ts" , ".js" ] }, output : { filename : "bundle.js" , path : path.resolve (__dirname, "dist" ) } };
当从 npm 安装第三方库时,一定要牢记同时安装这个库的类型声明文件。可以从 TypeSearch 中找到并安装这些第三方库的类型声明文件。如npm install --save-dev @types/lodash
webpack 性能优化
及时更新 node、yarn、webpack 等的版本
在尽可能少的模块上应用 loader
plugin 尽可能精简并确保可靠(选用社区已验证的插件)
resolve 参数合理配置(具体参考https://www.webpackjs.com/configuration/resolve/
)
使用 DllPlugin 提高打包速度
控制包文件大小(tree shaking / splitChunksPlugin)
thread-loader,parallel-webpack,happypack 多进程打包
合理利用 sourceMap
结合stats.json
分析打包结果(bundle analyze)
开发环境内存编译
开发环境无用插件剔除