前端性能面试题(山月)
prefetch 与 preload 的区别是什么
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 286(opens new window)
Author
回答者: shfshanyue(opens new window)
<link rel="prefetch" href="style.css" as="style" />
<link rel="preload" href="main.js" as="script" />
preload
加载当前路由必需资源,优先级高。一般对于 Bundle Spliting 资源与 Code Spliting 资源做 preloadprefetch
优先级低,在浏览器 idle 状态时加载资源。一般用以加载其它路由资源,如当页面出现 Link,可 prefetch 当前 Link 的路由资源。(next.js 默认会对 link 做懒加载+prefetch,即当某条 Link 出现页面中,即自动 prefetch 该 Link 指向的路由资源
prefetch - Prefetch the page in the background. Defaults to true. Any that is in the viewport (initially or through scroll) will be preloaded.
更多信息可参考以下链接:
- 用 preload 预加载页面资源(opens new window)
- Using Preload and Prefetch in Your HTML to Load Assets(opens new window)
实现一个 once 函数,记忆返回结果只执行一次
更多描述
类似于 lodash.once
const f = (x) => x;
const onceF = once(f);
//=> 3
onceF(3);
//=> 3
onceF(4);
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 406(opens new window)
Author
回答者: shfshanyue(opens new window)
简单实现如下:
function once(f) {
let result;
let revoked = false;
return (...args) => {
if (revoked) return result;
const r = f(...args);
revoked = true;
result = r;
return r;
};
}
测试一下
> const f = () => {console.log('call'); return 3;}
< undefined
> once_f = once(f)
< (...args) => {
if (revoked) return result
const r = f(...args)
revoked = true
result = r
}
// 第一次调用
> once_f()
< call
< 3
// 第二次调用,没有打印 call
> once_f()
< 3
once(opens new window) 是社区使用最广泛的一个库,代码实现与上大同小异,然而每月下载量可达上亿,比 vue/react/angular 三者一个月的下载量加起来还要高一倍
如何压缩前端项目中 JS 的体积
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 644(opens new window)
Author
回答者: shfshanyue(opens new window)
- terser(opens new window) 或者 uglify(opens new window),及流行的使用 Rust 编写的
swc
压缩混淆化 JS。 gzip
或者brotli
压缩,在网关处(nginx)开启- 使用
webpack-bundle-analyzer
分析打包体积,替换占用较大体积的库,如moment
->dayjs
- 使用支持 Tree-Shaking 的库,对无引用的库或函数进行删除,如
lodash
->lodash/es
- 对无法 Tree Shaking 的库,进行按需引入模块,如使用
import Button from 'antd/lib/Button'
,此处可手写babel-plugin
自动完成,但不推荐 - 使用 babel (css 为 postcss) 时采用
browserlist
,越先进的浏览器所需要的 polyfill 越少,体积更小 - code spliting,路由懒加载,只加载当前路由的包,按需加载其余的 chunk,首页 JS 体积变小 (PS: 次条不减小总体积,但减小首页体积)
- 使用 webpack 的 splitChunksPlugin,把运行时、被引用多次的库进行分包,在分包时要注意避免某一个库被多次引用多次打包。此时分为多个 chunk,虽不能把总体积变小,但可提高加载性能 (PS: 此条不减小总体积,但可提升加载性能)
Author
回答者: 1689851268(opens new window)
压缩的具体操作
- 去除多余字符,eg:空格,换行、注释
- 压缩变量名,函数名、属性名
- 使用更简单的表达,eg:合并声明、布尔值简化
如何优化 React 项目的性能
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 645(opens new window)
Author
回答者: shfshanyue(opens new window)
- 避免不必要的渲染,shouldComponentUpdate、React.memo、React.useMemo、React.useCallback。
- 代码分割,React.lazy 动态加载组件
- 使用
react-query
,对请求响应进行缓存、重发等,避免多次请求,减少网络 IO 消耗及优化渲染次数 - 使用
useDebounce
,对值及事件处理函数进行防抖,避免状态频繁变动,优化渲染次数 - 使用
useImmer
如何提高首屏渲染时间?
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 688(opens new window)
Author
回答者: shfshanyue(opens new window)
TODO
Author
回答者: illumi520(opens new window)
- 对于 pv 量比较高的页面,比如 b 站等流量图也比较大的,采用 ssr 采用 ssr 如何优化性能
- 性能瓶颈在于 react-dom render/hydrate 和 server 端的 renderToString
- 尽量减少 dom 结构, 采用流式渲染,jsonString 一个对象,而不是 literal 对象
- server 去获取数据
- 不同情况不同分析,减少主线程阻塞时间
- 减少不必要的应用逻辑在服务端运行
- 减少依赖和包的体积
- 利用 webpack 的 contenthash 缓存
- 重复依赖包处理,可以采用 pnpm
- 采用 code splitting,减少首次请求体积
- 减少第三方依赖的体积
- FP (First Paint) 首次绘制 FCP (First Contentful Paint) 首次内容绘制 LCP (Largest Contentful Paint) 最大内容渲染 DCL (DomContentloaded) FMP(First Meaningful Paint) 首次有效绘制 L (onLoad) TTI (Time to Interactive) 可交互时间 TBT (Total Blocking Time) 页面阻塞总时长 FID (First Input Delay) 首次输入延迟 CLS (Cumulative Layout Shift) 累积布局偏移 SI (Speed Index) 一些性能指标可以监控性能
4.网络 prefetch cdn
网站性能优化中,如何对小图片进行优化
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 705(opens new window)
Author
回答者: shfshanyue(opens new window)
TODO
Author
通过整合小图片到一张精灵图,减少请求图片次数。
Author
回答者: illumi520(opens new window)
base64 或者搞成 svg