首屏加载时间是一个衡量网页性能和用户体验的关键指标,这个问题无论是在面试中还是在项目开发中都占有极其高的权重。它指的是从用户开始请求网页到网页的第一屏内容完全渲染完成并对用户可见的时间。首屏 指的是用户不滚动页面时看到的那部分内容,通常是访问网站或应用时最先展示给用户的信息区域。
首屏加载时间的长短直接影响用户的第一印象和留存率,因为用户往往对加载速度较慢的网站或应用有较低的耐心。如果首屏加载时间过长,用户可能会感到不耐烦,甚至在内容完全加载前离开,这样就增加了跳出率,降低了用户体验。
白屏时间是指从用户发起页面请求(比如点击一个链接或在浏览器地址栏输入网址)到页面开始出现第一批可视化内容(不是完全的空白)之间的时间。在这段时间内,用户面对的是一片空白屏幕,因此称之为“白屏时间”。白屏时间主要受网络速度、服务器响应速度和初步渲染所需的资源大小等因素影响。
代码分割
在结合 Webpack 和 React 的项目中,代码分割(Code Splitting)是一种重要的性能优化手段,特别是对于首屏加载时间的优化。代码分割可以将一个大的 bundle 文件拆分成多个小的 chunks(块),这样可以按需加载,减少首次加载的时间,加快首屏显示。
Webpack 的 SplitChunksPlugin 可以用来自动分割公共模块和第三方库。通过合理配置 optimization.splitChunks 选项,可以把公共依赖提取到单独的 chunk 中,避免重复打包,减少首屏加载的体积。
optimization: { splitChunks: { chunks: 'all', // 可以是`async`(仅分割异步加载模块),`initial`(仅分割初始加载模块),或`all`(两者都分割) minSize: 20000, // 生成chunk的最小体积(以字节为单位) minChunks: 1, // 在分割之前,模块被共享的最少次数 maxAsyncRequests: 30, // 按需加载时的最大并行请求数 maxInitialRequests: 30, // 入口点的最大并行请求数 automaticNameDelimiter: '~', // 默认情况下,webpack将使用块的来源和名称生成名称(例如vendors~main.js) cacheGroups: { // 缓存组可以继承和/或覆盖splitChunks.*的任何选项 vendors: { test: /[\/]node_modules[\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true } } } }
在 React 中,可以使用动态导入的方法来,利用 import()语法实现动态导入,Webpack 会自动将这些导入的模块分割成新的 chunk。对于 React 组件,可以结合 React.lazy 进行按需加载。
const LazyComponent = React.lazy(() => import("./LazyComponent"));
使用 Webpack 的魔法注释来实现更细致的控制,比如命名 chunk、预加载和预获取。
const LazyComponent = React.lazy(() => import(/* webpackChunkName: "lazy-component" */ "./LazyComponent") ); // 预加载 const AnotherComponent = React.lazy(() => import(/* webpackPrefetch: true */ "./AnotherComponent") ); // 预获取 const YetAnotherComponent = React.lazy(() => import(/* webpackPreload: true */ "./YetAnotherComponent") );
为动态导入的组件提供加载状态,可以使用 Suspense 组件来包裹懒加载的组件,并指定一个 fallback 加载指示器:
对于使用 React Router 之类的路由库的应用,可以在路由配置中应用代码分割,为每个路由页面实现懒加载:
import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import React, { Suspense, lazy } from "react"; const Home = lazy(() => import("./routes/Home")); const About = lazy(() => import("./routes/About")); const App = () => ( Loading...
}>
);