目录
一:拯救移动端图标 - SVG
1.1.从 PNG 到 IconFont
1.2.从 IconFont 到 SVG
二:使用 Flexbox 优化布局
三:优化资源的加载顺序
3.1.Preload
3.2.Prefetch
四:预渲染页面
五:Windowing(窗口化)提高列表性能
六:使用骨架组件减少布局移动(Layout Shift)
IconFont 的几个常用网站:
iconfont-阿里巴巴矢量图标库、IcoFont、Font Awesome
Iconfont 相较于 PNG 的优势:
因为网站会有很多图标,如果使用 PNG,每个图标都需要单独设计一个文件,这样浏览器就需要对每一个文件进行加载。虽然可以使用雪碧图来规避请求数量,但是这就需要关心 icon 的定位了。iconfont 就是一套字体,就只需要获取一套字体即可
PNG 需要关注图片的宽度、比例,以避免失真、变形
Iconfont 缺陷:
这时人们就在想有没有什么方式类似 HTML,把这个图片描述出来,这样一方面可以显示图标,另一方面还能让搜索引擎理解这个代表什么意思。这时人们就把一项老技术 svg 翻出来了
这里在 React 中使用 svg-sprite-loader 对 svg 进行处理
首先采用 @svgr/webpack 支持 svg 作为组件引用
npm install -D @svgr/webpack
之后在 webpack.config.js 里配置即可
module.exports = smp.wrap({
module: {
rules: [
{
test: /\.svg$/,
use: ['@svgr/webpack'],
},
],
},
})
在页面里直接当组件使用
class About extends Component {
render() {
return (
)
}
}
svg优势:
关于 flex 布局,更多细节请参考我的另一篇博文:
Flex—弹性布局详解_不想学习的打工人的博客
我们设置元素的 display: flex,这个元素就会变成 flex 容器
Flexbox 优势:
使用 Preload 和 Prefetch 改变浏览器默认的资源加载优先级
提前加载较晚出现,但对当前页面非常重要的资源
对于字体而言比较特殊,需要设置 crossorigin="anonymous"
提前加载后续页面或后续路由所需的资源,优先级低
总结:对于那些在当前页面使用的资源可以利用 Preload,而对一些可能在将来某些页面中使用的资源可以利用 Prefetch。从加载优先级上看,Preload 会提升请求优先级,而 Prefetch 会把资源的优先级防止最低,当浏览器空闲时采取加载
webpack 提前预加载处理,只需要加上一行注释
import(/* webpackPrefetch: true */ './path/to/LoginModal.js')
import(/* webpackPreload: true */ 'ChartingLibrary')
预渲染页面有点类似于我们使用的服务端渲染(SSR),通过这项技术可以帮助我们在打包的时候将单页应用的页面进行提前渲染,这样可以加快用户看到首屏的时间
预渲染的作用:
下面以 react-snap 插件为例进行介绍
react-snap
首先,先对插件进行安装
npm install -D react-snap
然后在 package.json 中增加一条 scipts,这里可以通过 npm 一个钩子函数,在 build 完成后,自动触发 postbuild
{
"scripts": {
"build": "cross-env NODE_ENV=production webpack",
"postbuild": "react-snap"
}
}
如果使用 react 做 SSR,它会对页面渲染进行修改,通常使用 ReactDOM.render,对主节点上相关元素进行渲染,如果主节点已经有元素了,就不需要触发 ReactDOM.render
let root = document.getElementById('main')
if (root.hasChildNodes()) {
ReactDOM.hydrate( , root)
} else {
ReactDOM.render( , root)
}
最后,在 package.json 中,可以配置内联样式,避免明显的样式闪动(FOUC)
"reactSnap": {
"inlineCss": true, // 内联样式,避免明显的样式闪动
}
windowing 的作用:
安装:
npm i -D react-window
以一个二维列表为例,进行使用:
import { FixedSizeGrid, FixedSizeList } from 'react-window'
import model from './model'
import React from 'react'
const items = []
for (let i = 0; i < 100; i++) {
items.push(model.map(m => ))
}
const Row = ({ index, style }) => {
let styleExt = {
...style,
borderBottom: '1px solid #fff',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}
return {items[index]}
}
class ListComponent extends React.Component {
listRef = React.createRef()
scrollToRow = rowNum => () => {
if (rowNum <= 0 || rowNum > items.length) return
this.listRef.current.scrollToItem(rowNum)
}
render() {
return (
/* 一维列表List */
{Row}
/* 二维列表Grid */
{/*
{Row}
*/}
)
}
}
export default ListComponent
当相关组件数据还没有完全加载时,如果样式没有控制好,会导致组件没有完全撑开,当样式加载好之后,组件的布局会发生变化,对周围的组件也会造成影响,这个性能消耗比较高,我们应该尽量避免
骨架组件也叫 Skeleton 或 Placeholder(占位符),用来占位和提升用户感知性能,可以在 Google DevTools 里键入 ctrl + shift + p,输入 Layout Shift Regions 查看是否发生布局移动
安装插件
npm i -D react-placeholder
使用
import ReactPlaceholder from 'react-placeholder'
class Contact extends Component {
render() {
const { ready } = this.state
const imageStyle = !ready ? { display: 'none' } : {}
let cardMedia = (
)
return (
}>
/* ... */
{!ready && cardMedia}
)
}
}