前言
前端性能优化有多重要?试想如果一个网页要5秒才打开,你会等吗?提高前端性能才能保证用户体验和留存率。该篇文章会从项目的代码、打包、http层面谈谈前端优化方案。
准备
下面我将用自己的笔记本项目做例子,去优化前端性能。(PS:前端用React+Antd,对比都是没有使用浏览器缓存的情况下做的)
开发角度
1. 资源压缩
在项目中使用图片、gif等资源前,应该先进行压缩,这里推荐一个图片压缩网站Tinypng
这里只用了少量资源比较,资源多的话可以节省不少带宽。
2. 图片渐进加载
先加载一张较小的图片,等大图加载完毕再进行替换,替换的时候用transition
将小图慢慢过渡为透明。
// 组件加载完毕执行
const loadBg = () => {
const img = new Image()
img.src = require('@assets/img/bg.png') // 大图
img.onload = () => {
setBgImg(img.src)
}
}
return(
<div
className={styles.bgImgs}
style={{
backgroundImage: `url(${bgImg})`
}}
>
<img
src={require('@assets/img/bg-small.png')} // 小图
style={{
opacity: Number(!bgImg)
}}
/>
<div className={styles.bgImgsMask} />
</div>
)
3. 代码分割
react lazy 文档或react-loadable,通过代码分割,只加载当前路由的资源,其他路由的资源等待跳转到对应路由再加载。
const Home = lazy(() => import(/* webpackChunkName: "Home" */ '@views/Home'))
const Login = lazy(() => import(/* webpackChunkName: "Login" */ '@views/Login'))
const NotFound = lazy(() => import(/* webpackChunkName: "NotFound" */ '@views/NotFound'))
const ShareArticle = lazy(() => import(/* webpackChunkName: "ShareArticle" */ '@views/ShareArticle'))
<Suspense fallback={<PageLoading Icon={<img src={require('@assets/gifs/loading-pen.gif')} />} />}>
<Switch>
<Route path="/login" exact={true}>
<Login />
</Route>
<Route path="/share-article">
<ShareArticle />
</Route>
<Route path="/" exact={true}>
<Home />
</Route>
<Route>
<NotFound />
</Route>
</Switch>
</Suspense>
可以看到资源大小小了很多,这是登录页面,只包含Login
的资源和antd Button
的资源。
4. 使用iconfont的在线图标资源
不需要下载到本地,用script标签引用资源,整理图标方便,存在哪些图标一目了然,避免图标重复出现,而且提高打包速度和包大小。
5. Antd按需加载
文档
6. 开发注意事项
- 通用代码封装成组件、尽量细分解耦。
- 长列表可以选择用react-virtualized方案。
- 组件之间不传没用的参数,考虑好是否需要用展开符。
- shouldComponentUpdate避免重复渲染。
- 不可变传参,应该先定义好变量,再将变量传给组件。
打包角度
1. 资源压缩
通过css-minimizer-webpack-plugin压缩css资源。
通过terser-webpack-plugin压缩js资源,webpack5自带。
2. 防止js阻塞页面渲染
// js插入到body,或者使用defer
new HtmlWebpackPlugin({
template: 'build/tpl/index.html',
inject:'body',
scriptLoading:'defer'
})
3. 分包
因为之前用了lazy
异步加载 ,webpack会自动对这些组件分包。然后下面我将一些公共的单独打出来,减少包的体积。
splitChunks: {
chunks: 'all',
cacheGroups: {
// 禁用默认缓存
default: false,
vendor: {
name: 'vendor',
// 包含同步和异步
chunks: 'all',
// 最少被引用两次
minChunks: 2,
// 优先级
priority: 10,
// 最小大小
minSize: 0,
test: /[\\/]node_modules[\\/]/
},
reactBase: {
name: 'reactBase',
test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom)[\\/]/,
priority: 11,
minChunks: 1,
minSize: 0
},
mobxBase: {
name: 'mobxBase',
test: /[\\/]node_modules[\\/](mobx|mobx-react|mobx-react-router)[\\/]/,
priority: 12,
minChunks: 1,
minSize: 0
},
lodash: {
name: 'lodash',
test: /[\\/]node_modules[\\/](lodash)[\\/]/,
priority: 13,
minChunks: 1,
minSize: 0
}
}
}
4. 图片内联到html中
用url-loader
配置小于指定大小的图片资源通过Data URL
方式内联到html中。