Skip to content

Latest commit

 

History

History
157 lines (103 loc) · 9.88 KB

20201120.md

File metadata and controls

157 lines (103 loc) · 9.88 KB

云音乐搭建平台性能优化实践

https://zhuanlan.zhihu.com/p/296265057

XRN 平台是云音乐音乐内容组产出的一个可视化的搭建页面平台,通过拖拽组件与设置组件,XRN 可以快速生成同时能在 React Native 与 Web 端渲染的页面。

图

打包阶段优化

Web 端

1. 减少重复打包

  • xrn-common: RN 和 web 公用
  • xrn-webview: web 容器

借助 https://github.com/danvk/source-map-explorer 定位重复打包的库 图

发现后的解决方案:

  • 对于版本兼容性导致的问题,我们可以通过协调库的版本,来使得引入库保持一致。
  • 对于引入路径不一致的起因,我们可以配置 Webpack 中的 resolve.alias 字段来指定统一的引用路径从而解决重复打包的问题。

2. 代码剪枝

虽然 webpack 有 tree-shaking,但条件比较苛刻,要求都是 es6 模块写法。

坑: react-native-web 的剪枝

虽然可以借助 https://www.npmjs.com/package/babel-plugin-react-native-web 修改组件引入路径

// 修改前
import { View } from 'react-native';
// 修改后
import View from 'react-native-web/dist/exports/View'`

但如果我们后续引入的第三方库中含有 require('react-native-web'),就会再次将 react-native-web 整个包引入项目。

在这种情况下,直接引入第三方库的源码往往会是一个更好的选择

这个问题(Tree-Shaking is too fragile)也同样被 Expo Web 所提及。

3. Polyfill 优化

使用 @babel/preset-env 插件,并配置 useBuiltIns: 'usage' 选项,这样就可以实现按需打包 polyfill

图

4. 动态引入

通过动态引入的 import 语法,现代打包工具例如 Webpack, Rollup 可以将代码打包成多个 js 文件模块,当页面真正需要某个模块时,才会通过异步请求的方式去获取这个 js 文件。

使用 React.lazy 与 Suspense 这两个 API,可以对我们组件代码进行动态引入,精确加载每个页面内所需要的组件,从而大大减小页面渲染时请求的 js 文件体积。除了组件,我们更可以直接使用 import 语法来动态加载第三方库或者 js 文件,进一步减小首屏的 js 文件体积。

需要注意的是,动态引入也可能会造成性能上损失。动态引入大量小体积的 js 文件,会造成不必要的网络开销,甚至会带来负优化的效果。因此在优化时我们需要权衡异步请求数、异步请求文件体积等因素来决定是否真正需要使用动态引入。

5. 更多

我们也可以使用 Chrome DevTools 提供的 Coverage Tab,来找到 jsbundle 中每一行未被使用的代码。

除此之外,Web Dev 也提供了更多关于这项优化的建议。

React Native 端

1. 拆包

XRN 在 RN 端构建时拆分了业务包与基础包 。 业务包包含了 XRN 特有的组件与业务逻辑,而基础包包含了云音乐所有 RN 应用共享的依赖库(例:react, react-native, react-navigation),XRN 的 jsbundle 体积在拆包后下降了 36%。

2. 预加载

app 启动时,预加载业务包,避免首次打开的耗时。

此外在打开 RN 的应用前,也会预热一个加载完基础包的容器,来进一步地提升打开速度。

图

渲染阶段优化

1. 图片懒加载

在 React Native 端并没有原生支持图片懒加载的方式,更多的是使用 FlatList 去模拟图片懒加载。

FlatList 本是一个双端都兼容的方案,但它作为一个滚动容器在渲染内容时不够灵活,并且不适用于较为复杂的 DOM 结构与多种的定位方式,因此 XRN 最终的方案为基于 ScrollView 实现的图片懒加载机制。原理如下:

<ScrollView
    onScroll={handleScroll}>
    <Component1>
        { ... }
    </Component1>
    <Component2>
        <LazyLoadImage
            height={300}
            width={300} />
    </Component2>
</ScrollView>

LazyLoadImage 为一个自定义组件,它会在组件渲染后调用 React Native 提供的 measureLayout 方法去确定自己在 ScrollView 内的位置(X / Y轴坐标),通过监听 onScroll 事件、对比页面的滚动高度,图片组件便能确定是否需要渲染。当图片处于可视区域外不需要渲染时,渲染相同宽高的占位组件来保持页面高度一致并提升用户体验。

2. 列表懒加载

图

列表懒加载与图片不同的是,React Native 的图片往往在渲染前就能知道高度及宽度的数据,可以直接使用这组数据进行占位,使得可视区域外的图片省去了大量渲染的耗时。

而对于列表来说,由于不同的列表承载的是不同的组件类型(例:单曲、歌单、艺人),也就无法在渲染前获得其准确高度来进行占位。

这里 XRN 采取的策略是:先渲染单行列表,等获得了单行高度后再去渲染余下的列表内容,通过这种方式在不渲染实际内容的情况下对可视区外的列表行进行占位。

3. 首屏渲染

XRN 会优先渲染首屏可见的内容,同时延迟渲染不可见的内容。当用户进入页面时,首屏渲染的内容高度将被限制在一屏半的高度内。

如何获得每个组件的高度数据?

图

XRN 会在页面搭建时根据 Web 端的组件渲染结果估算出页面中每个组件的高度(如上图所示),这样在页面渲染时就可以根据这些高度数据来决定首屏渲染的组件数量,从而将页面高度控制在一屏半左右的范围内。

4. 渐进式渲染

图

对于剩余的页面组件 XRN 会通过 setTimeout 的方式来分次进行加载。

5. 减少重复渲染

重复渲染是一个常见但是不容忽视的问题,React Native 的核心贡献者 Mike Grabowski 也在他的优化建议中提到了这点。

图

如上图所示的,我们可以通过 React Developer Tools 浏览器插件中包含的 react profiler 来查找与定位每个组件的重复渲染原因、渲染次数及具体耗时。当定位到了问题组件及查明了重复渲染原因后,我们可以改写 shouldComponentUpdate 来主动控制组件渲染逻辑,也可以用 useMemo, useCallback, React.memo 等方法来阻止组件 props 中的引用变更来减少重复渲染。

小结

完成了以上的性能优化点后,XRN Web 页面的 Lighthouse 分数从初始的 50.3 分(基于 Lighthouse 5.5.0 / 198 个线上页面),提升到 80.4 分(基于 Lighthouse 5.5.0 / 618 个线上页面)的成绩。在 React Native 端的页面加载速度也提升了 40%。相信也能为各位在做 Web 或者 RN 的性能优化时带来一些有价值的参考点。

新闻

1.NodeJS 发布 12.19.1、14.15.1、15.2.1 修复 DNS 安全问题

  • 2020-11-16, Version 12.19.1 'Erbium' (LTS), @BethGriggs
  • 2020-11-16, Version 14.15.1 'Fermium' (LTS), @BethGriggs
  • 2020-11-16, Version 15.2.1 (Current), @targos

https://github.com/nodejs/node/releases/tag/v15.2.1

This is a security release.

Vulnerabilities fixed:

CVE-2020-8277: Denial of Service through DNS request (High). A Node.js application that allows an attacker to trigger a DNS request for a host of their choice could trigger a Denial of service by getting the application to resolve a DNS record with a larger number of responses.

2.Version 11 of Angular Now Available

https://blog.angular.io/version-11-of-angular-now-available-74721b7952f7

Angular 11 正式发布:加入 webpack 5,升级至 TS 4.0,不再支持 IE 9 和 10

3.2020 Google 开发者大会:Android 和 Flutter 有哪些更新?

链接地址

4.Babylon.js 4.2: Simplicity Reimagined

https://babylonjs.medium.com/babylon-js-4-2-simplicity-reimagined-965f88d0fad

5.No more free work from Marak - Pay Me or Fork This

https://github.com/Marak/faker.js/issues/1046