目录
1:webpack优化配置
webpack4引入了mode模式,可以配置开发和生产模式,并且可以使用一些默认的插件来帮助我们达到想要的效果。
1.1.摇树
这是一种非常有效的减少资源体积的方法,因为最终制作的包中有很多代码不会被使用哈希css,所以可以在打包之前进行处理,将未使用的代码去掉。
前提:不管是自己的还是第三方的,必须是模块化的,而且必须是基于ES6的import导出导出导入方式。 生产模式默认会启动Tree-shaking,主要依赖TerserPlugin插件。
原理:从index.js开始,看看引用了什么,进一步分析所有引入的包或模块中都引用了哪些模块或其他包,最后留下所有需要的东西。
由于Tree-shaking的实现是基于一定的规则的,所以在JS中,可能会涉及到改变全局作用域(窗口对象)。 如果去掉这个抖动,代码中就会出现问题。 这种情况下,需要使用 sideEffects 来告诉 webpack 哪些文件不需要摇动
在 package.json 中配置 sideEffects
"sideEffects": [
"*.css"
]
注意Babel默认配置的影响
在 babel.config.js 中,presets 会将常用的 babel 插件做了一个集合。但是它有一个问题:ES6 模块句型在转码的时候会被转换成其他句型。 我们想要保留 ES6 的模块化句型,因此需要添加 module: false 以便 Tree-shaking 可以工作。
module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: false,
targets: {
browsers: ['>0.25%'],
},
useBuiltIns: 'usage',
bugfixes: true,
},
],
'@babel/preset-react',
],
plugins: ['@babel/plugin-proposal-class-properties', '@babel/plugin-transform-runtime'],
}
1.2.JS压缩
简洁的 webpack 插件
支持ES6(Webpack4)的压缩。 terser无论是销量还是功效都比uglifyjs要好。 因此,生产模式下将使用terser作为默认压缩插件。 它的主要作用是减少JS文件的大小。
1.3. 范围改进
/**************** util.js ********************/
export default 'Hello,Webpack';
/**************** index.jsx ******************/
import str from './util';
console.log(str);
/***************** 没有 scope hoisting, webpack 打包后 *******************/
[
(function (module, __webpack_exports__, __webpack_require__) {
var __WEBPACK_IMPORTED_MODULE_0__util_js__ = __webpack_require__(1);
console.log(__WEBPACK_IMPORTED_MODULE_0__util_js__["a"]);
}),
(function (module, __webpack_exports__, __webpack_require__) {
__webpack_exports__["a"] = ('Hello,Webpack');
})
]
/************************************/
/***************** 有 scope hoisting, webpack 打包后 ********************/
[
(function (module, __webpack_exports__, __webpack_require__) {
var util = ('Hello,Webpack');
console.log(util);
})
]
/************************************/
如果未启用范围改进,这两个模块将被分为单独的模块。 当其中一个依赖另一个时,就会要求依赖的模块进来,然后调用需要的模块; 范围改进启用后,会做合并分析,当发现有这些依赖的调用时,会尝试将依赖合并到调用中,最终会变得更加精简。
1.4.Babel优化配置
1. 必要时引用polyfill
Polyfill是为了兼容老的浏览器来实现新的功能或者新的规范。因为这个比较大,我们需要把涉及到的东西都介绍一遍,但是我们可能会用到其中较小的一部分。 这种情况下,我们需要在 babel.config.js 中配置 useBuiltIns: 'usage' 。
2、按需引入辅助功能
如需复用 babel 转码后的辅助功能,请在 babel.config.js 中配置 @babel/plugin-transform-runtime 插件
3.根据目标浏览器按需转换代码
在preset-env中配置target: { browsers: ['>0.25%'], },即支持市场份额超过0.25%的浏览器
2:webpack的依赖优化
使用webpack打包之后,你会得到一定的优化,但是你也可以在webpack打包过程中进行一定的优化。
2.1.noParse(不解析)
module: {
noParse: /lodash/, // loadsh比较独立
noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/, // vue-cli里的配置
}
2.2.Dll插件
将我们复用的库提取出来,变成引用方法,这样我们就不用每次都重新构建库,这样可以大大加快创建过程。
/* package.json */
{
"scripts": {
"dll-build": "cross-env NODE_ENV=production webpack --config webpack.dll.config.js"
}
}
/* webpack.dll.config.js */
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'production',
entry: {
react: ['react', 'react-dom'],
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, 'dll'),
library: '[name]',
},
plugins: [
new webpack.DllPlugin({
name: '[name]',
path: path.resolve(__dirname, 'dll/[name].manifest.json'),
}),
],
}
/* webpack.config.js */
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
module.exports = smp.wrap({
plugins: [
/* 动态链接库引用 */
new DllReferencePlugin({
manifest: require(`${__dirname}/dll/react.manifest.json`),
}),
],
}
三:Webpack细节优化
代码分割
Webpack代码拆分方法:
1.提取我们代码中重用的东西
2.将我们的业务逻辑与第三方依赖分离
webpack处理文件路径时,在Unix系统中仍然包含/,在Windows系统中仍然包含,所以要在其中添加[\/]。
chunks:有效值为all、async、initial,initial是同步加载,async是异步加载
minChunks:拆分模块之前必须共享的最小块数
module.exports = smp.wrap({
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
name: 'vendor',
test: /[\/]node_modules[\/]/,
minSize: 0,
minChunks: 1,
priority: 10,
chunks: 'initial',
},
common: {
name: 'common',
test: /[\/]src[\/]/,
chunks: 'all',
minSize: 0,
minChunks: 2,
},
},
},
},
})
<Suspense fallback={Loading...}>
四:Webpack资源压缩
webpack4模式
4.1.Terser压缩JS
模式:'生产'行代码相当于下面编写的所有代码
// webpack.production.config.js
module.exports = {
mode: 'production',
performance: {
hints: 'warning',
},
output: {
pathinfo: false,
},
optimization: {
namedModules: false,
namedChunks: false,
nodeEnv: 'production',
flagIncludedChunks: true,
occurrenceOrder: true,
sideEffects: true,
usedExports: true,
concatenateModules: true,
splitChunks: {
hidePathInfo: true,
minSize: 30000,
maxAsyncRequests: 5,
maxInitialRequests: 3,
},
noEmitOnErrors: true,
checkWasmTypes: true,
minimize: true,
},
plugins: [
new TerserPlugin(/* ... */),
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
],
}
4.2.mini-css-extract-plugin 压缩CSS
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
chunkFilename: '[id].[chunkhash:8].css',
}),
new OptimizeCssAssetsPlugin({
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
},
canPrint: true,
}),
]
}
4.3.HtmlWebpackPlugin-minify 压缩 HTML
{
collapseWhitespace: true, // 移除空格、换行符
keepClosingSlash: true, // 保留元素的末尾斜杠
removeComments: true, // 移除注释
removeRedundantAttributes: true, // 移除冗余属性(默认值)
removeScriptTypeAttributes: true, // 移除script标签中的type属性
removeStyleLinkTypeAttributes: true, // 移除style标签中的type属性
useShortDoctype: true // 使用html5短的描述方式
}
五:基于webpack的资源持久化缓存
哈希的特点:离散唯一值。 如果内容不变,计算值也不变。
contenthash:能够区分同一资源的CSS和JS文件的哈希值。 将根据内容生成哈希值。 当仅更改 JS 时,CSS 重新打包后会保持原来的 hash 值。部署更新时,可以充分利用浏览器缓存,保证用户体验和更新平滑过渡。
module.exports = {
output: {
path: `${__dirname}/build`,
filename: '[name].[hash].bundle.js',
chunkFilename: '[name].[chunkhash:8].bundle.js',
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
chunkFilename: '[id].[contenthash:8].css',
}),
]
}
六:基于webpack的应用程序规模测量与分析
常用工具:
Webpack 图表
通过:webpack --profile --json > stats.json 生成stats.json 文件,然后将此文件上传到本网站。
这个可能还不够详细。 如果你想进一步分析,可以使用bundle-analyzer工具。 这里我们使用source-map-explorer来进一步分析。
npm i source-map-explorer
此分析不是基于我们的捆绑文件,而是基于源映射。 需要生成源映射。
/* package.json */
{
"scripts": {
"analyze": "source-map-explorer 'build/*.js'"
},
}
/* webpack.config.js */
module.exports = smp.wrap({
mode: 'production',
devtool: 'source-map'
}
官方推荐的bundle-analyzer可以得到可视化的图表,但与source-map-explorer相比,只能给出大概的视图,缺少一些具体的内容(大小)。
速度分析可以使用speed-measure-webpack-plugin。 运行 npm run build 以查看所有插件和所有加载器的使用情况。
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
module.exports = smp.wrap({...})
七:React按需加载的实现方法
代码分割最初是为了解决请求过大的问题。 通过请求哈希css,将整个包加载到主页。 此时网络开销比较小,包体积较大,所以下载时间较长; 我们可以将较大的包拆成几个大包,而小包只能在调用时才会被加载(按需加载)。如果将拆分级别定义为组件,那么所有组件都会被拆分为一个独立的模块,并且会有很多束或块。 那么就需要发起多个请求,开销会更大。
通常最合理的做法是基于路由来做按需加载。只有当我们页面上的某些组件在不同路由的页面上复用时,我们才会将组件单独拆解。
import loadable from '@loadable/component'
// 使用React-Loadable动态加载组件
const LoadableAbout = loadable(() => import('./About.jsx'), {
fallback: 'loading...',
})
class App extends React.Component {
render() {
return (
)
}
}