前端项目优化,由于项目越做越大。发现这个项目打包越来越慢,每次启动都需要三分钟左右。实在无法忍受。
1.分析耗时模块
- SpeedMeasurePlugin
- BundleAnalyzerPlugin
SMP 可以分析出 Webpack 整个打包过程中在各个 loader 和 plugin 上耗费的时间,这将会有助于找出构建过程中的性能瓶颈。
BAP可以生成代码分析报告,可以直观地分析打包出的文件有哪些,及它们的大小、占比情况、各文件 Gzipped
后的大小、模块包含关系、依赖项等,对应做出优化,从而帮助提升代码质量和网站性能。
安装
npm install --save-dev speed-measure-webpack-plugin(webpack-bundle-analyzer) or yarn add -D speed-measure-webpack-plugin(webpack-bundle-analyzer)
vue.config.js 配置
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
if (process.env.NODE_ENV === 'development') { // 只在开发环境使用
config
.plugin('webpack-bundle-analyzer')
.use(BundleAnalyzerPlugin.BundleAnalyzerPlugin)
config
.plugin('speed-measure-webpack-plugin')
.use(SpeedMeasurePlugin)
}
2.路由懒加载
开启路由懒加载 : 使用import
{
path: '/dashboard/dataOverView',
component: () => import('@/views/dashboard/dataOverView'),
name: 'dataOverView',
meta: {
title: '数据概况',
permission: 'datacenter_new_overview',
},
}
3.关闭productionSourceMap
开启会生成会生成map文件,无需生成。
module.exports = {
// 关闭 js 编译后生成的 .map 文件
productionSourceMap: false,
}
map文件作用:项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错。 有了map就可以像未加密的代码一样,准确的输出是哪一行哪一列有错。
4.使用resolve
使用resolve配置解析模块,帮助webpack快速准确的找到这些文件
chainWebpack: (config) => {
// 开发生产共同配置别名
config.resolve.alias.set('@', path.join(__dirname, 'src')) // 配置@ 符号为src文件
}
5. 删除预加载
预获取 prefetch: 在浏览器加载完必要的资源后,空闲时就会去获取可能需要的资源。
预加载 preload: 预先加载当前页面可能需要的资源, 它会与必要资源并行请求。
使用这两个会使首页加载的时候多了很多网络请求,加载变得很慢
if (process.env.NODE_ENV === 'production') { // 正式环境删除预加载
// 删除预加载 针对请求 删除预加载 数进行优化
config.plugins.delete('prefetch')
config.plugins.delete('preload')
}
6.开启gzip压缩
CompressionPlugin插件能够通过压缩算法,将前端打包好的资源文件进一步压缩,生成指定的、体积更小的压缩文件,让浏览器能够更快的加载资源。需要和后端一起配合使用
安装CompressionPlugin插件
npm install --save-dev compression-webpack-plugin
or
yarn add -D compression-webpack-plugin
vue.config.js 配置
configureWebpack: {
plugins: [
new CompressionPlugin({
test: /\.(js|css|html|svg)$/,
threshold: 10240,
deleteOriginalAssets: false,
})],
},
nginx 服务端配置
生成压缩后的文件,不能直接使用,需要服务端配置才可以使用,而且发现打包生成的“dist/index.html”首页内,也没有直接引用这些“.gz”格式的文件。
而实现的关键,其实就是让服务端向浏览器发送“Content-Encoding=gzip”这个响应头,并把对应的“.gz”格式文件发送给浏览器,让浏览器通过“gzip”编码格式来解析资源。
在nginx.conf,http模块内配置“gzip_static on”即可。
http {
gzip_static on;
}
7.优化loadsh,减轻打包体积
lodash-webpack-plugin 插件做的事,就是在 webpack 的 afterResolve 钩子中,把某些 lodash 模块的资源路径替换掉,牺牲一些不常用的接口用法,达到减少打包体积的目的。它的原理是将我们用到的方法(和间接用到的内部方法)使用更简单的方法替代。具体的方法对应关系,参照源代码中的mapping.js。
安装lodash-webpack-plugin插件
npm install --save-dev lodash-webpack-plugin
or
yarn add -D lodash-webpack-plugin
使用插件,记住要加上shorthands,否则会报predicate的错误
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin')
configureWebpack: {
plugins: [
new LodashModuleReplacementPlugin({
shorthands: true,
}), // shorthands 处理 predicate报错问题
],
},
8.使用cdn加速
准备cdn 文件
不建议引入第三方的vue。引入后页面路由导航会失效,需要修改为a标签才能跳转。所以建议还使用wepack的引入的vue。
以下推荐国外比较稳定的两个 CDN,国内还没发现哪一家比较好,目前还是建议下载到本地。
-
Staticfile CDN(国内): https://cdn.staticfile.org/
-
BootCDN:https://www.bootcdn.cn/
-
unpkg:https://unpkg.com/, 会保持和 npm 发布的最新的版本一致。
-
jsdelivr : https://cdn.jsdelivr.net/
const CDNWEB = 'https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M' // 使用字节的文件库
let CND_RESOURCE = {
js: [
`${CDNWEB}/vue-router/3.0.3/vue-router.min.js`,
`${CDNWEB}/vuex/3.0.1/vuex.min.js`,
`${CDNWEB}/axios/0.18.0/axios.min.js`,
`${CDNWEB}/moment.js/2.24.0/moment.min.js`,
`${CDNWEB}/moment.js/2.24.0/locale/zh-cn.js`,
`${CDNWEB}/xlsx/0.17.4/xlsx.full.min.js`,
]
}
配置externals
凡是声明在externals
中的第三方依赖包,最终都不会被打包。通过排除这些包减小js/chunk-vendors.******.js
文件体积。
configureWebpack: {
externals: {
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios',
moment: 'moment',
xlsx: 'XLSX',
},
}
在public/index.html文件配置
使用 webpack
中自带的插件 html插件进行配置,在 index.html
中增加判断,是否使用 CDN, htmlWebpackPlugin.options
使用的是vue.config
中的config.plugin('html')
的插件属性。
在vue.config.js 配置, 通过自带 htmlWebpackPlugin插件可以访问这些文件
chainWebpack: (config) => {
config.plugin('html').tap((args) => {
args[0].cdn = CND_RESOURCE
return args
})
}
}
public/index.html,在body 里面引入
<% htmlWebpackPlugin.options.cdn.js.forEach(function(item){ if(item){ %>
<script src="<%= item %>" crossorigin="anonymous" type="text/javascript"></script>
<% }}) %>