拆分代码 + 动态加载 + 预加载,减少首屏资源,提升首屏性能及应用体验

文章介绍了如何通过结合webpack插件和React组件实现高度定制的按需加载和预加载策略,以减少首屏资源大小,提升首屏加载速度,同时解决常规按需加载可能导致的加载耗时问题,确保更好的用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

github 原文地址

我们看一些针对《如何提升应用首屏加载体验》的文章,提到的必不可少的措施,便是减少首屏幕加载资源的大小,而减少资源大小必然会想到按需加载措施。本文提到的便是一个基于webpack 插件 react 组件实现的一套研发高度自定义、组件按需加载的资源预加载方案. 简单来说是为了通过配置 webpack 插件及少量业务代码即可实现Code Splitting + 组件懒加载 + 组件预加载。

🧐 为什么要做这么一套预加载方案?它存在的必要性在哪里?🧐

常规组件按需加载方案缺点

  • React.lazy 组件按需加载 - 组件渲染时加载组件资源
react.lazy(()=>import('xxxx/component'))

优点:拆分组件代码,按需加载, 减少首屏的资源加载大小及数量,提升页面首屏渲染速度。

  • import 动态加载 - 执行代码 import() 时加载组件资源
useEffect(() => {
  import('xxxx/component').then((loadScript) => {
    
  })
}, [])

优点:拆分组件代码,开发者可以更细粒度地控制组件按需加载的时机。

共有缺点:

代码拆分后,组件资源异步加载存在耗时,当组件资源特别大或网络不稳定时都有可能会出现 loading 时间过长导致组件迟迟无法渲染到视图上,以致于影响用户体验

如图是我们项目中实际出现的场景之一:

image.png

image.png

由于资源加载存在近4s的耗时,组件渲染被延迟,这种情况下,便导致了我们虽然通过减少了首屏资源提升了首屏加载体验,但却让用户在后续使用过程中出现了体验断层,甚至是页面白屏的情况,这对用户而言是不能接受的用户体验。

且这种情况并非网络不好或资源过大等极端情况下才会出现,随着应用使用量的上升,该情况会多次出现,影响用户体验,以下为网络波动的场景之一:
image.png

那么如果要保证一个spa应用的后续交互体验,那么就是不拆包,要么就需要引入组件预加载机制。
预加载的必要性:让被懒加载的组件资源提前进行对应的资源请求,而不是渲染时请求以减少组件渲染时间,保证应用不会因为组件拆包影响用户体验

react.lazy有一个局限就是必须放在<Suspense>组件内,无法独立渲染<LazyComponent />
image.png

为什么不是react-lodable?

其实 react 社区提供的 react-lodable 解决了以上两个问题:

  • 不强依赖 <Suspense>,可独立渲染<LazyComponent />
  • 提供了preload预加载方案,减少异步加载耗时,保证用户体验。
    image.png

但是有个问题是模块过多时,侵入式的代码也变多了,且看起来重复且冗余,同时被预加载的模块并没有进行统一管理,后续维护也不会很方便,不直观。

那么我们在 webpack 编译层面是可以获取到打包chunk的详细信息的,是不是可以在 plugin 层面对按需加载的chunk进行统一维护,同时减少侵入式代码,于是便有了此方案 route-resource-preload, 其具备的特性:

  1. 拆分模块按需加载,减少应用首屏资源请求大小及数量,提升加载体验.
    image.png
    dynamic 是基于 import()做的一个封装函数。
    image.png

  2. 支持组件资源批量自动预加载,同时支持自定义触发时机,如hover到某个组件上、某组件渲染时、出现在视图内时。( Component / Module-Federation / UMD / Svg / Png 等静态资源).

    自动预加载步骤:

    2.1 构建时添加插件
    image.png
    2.2 基于暴露的 dynamic API 对组件进行动态加载(拆包)并渲染, 同时可基于<PreloadLink>进行自动预加载。
    image.pngimage.png

  3. 支持手动调用预加载, 类 react-lodable 的方式,但支持批量.
    单个组件手动预加载
    image.png 多个组件手动预加载
    image.png

  4. 支持React <Suspense>,但不依赖。

  5. 完备的 typescript 类型推导.
    image.png

DEMO演示

在线体验地址
react.lazy 正常拆包并加载效果.gif
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

route-resource-preload 拆包并预加载效果.gif
preoload.gif

正常懒加载(react.lazy)普通组件 及 Module-Federation

WX20230424-113712.png

route-resource-preload 预加载普通组件 及 Module-Federation

WX20230424-113747.png

加载耗时如下:

资源正常懒加载 - react.lazy (ms)预加载 (ms)
普通组件 (单个资源文件)1841
Module-Federation 组件 (6个资源文件)4058

从表中可以看出,预加载显着提升了组件的加载速度,尤其是对于复杂的组件,加载速度的提升更为明显。 这说明在复杂的业务场景下,预加载可以显着提升组件加载速度和用户体验.

方案&流程介绍

该方案基于 @route-resource-preload/webpack-plugin@route-resource-preload/react, 分别对应构建时运行时:

构建时流程图:
image.png

构建时 通过 dynamic API 及 webpack plugin 对模块进行拆包的同时,还会将preloadKey(开发者自定义的预加载标识)import-module-url(import 模块路径)chunk(output产物)三者之间的关系以json形式进行保存,并允许应用端访问。
image.png

生成的JSON文件:
image.png

JSON:
image.png

开发者基于 JSON,可以判断出可被预加载的chunk已配置预加载的chunk具体有哪些,同时也能知道插件中配置的预加载标识preloadKeychunk间的映射关系。

运行时流程图:
image.png
运行时 则是基于构建出的json,开发者通过设置Preloader 或者是<PreloadLink>preloadKey,对应的相关资源将被预加载,并基于 dynamic API 渲染组件。

项目效果演示

1. 真实用户场景打开 Modal( Modal基于 webpack module federation 引入)体验模拟
  • 无预加载时:点击按钮后,拉取对应的拆包资源及远程 module federation 组件资源,请求完成后渲染组件,存在体验卡顿,如下图。
    iShot_2023-07-20_18.25.23.gif
  • 有预加载时:hover到某个区域/某个组件渲染时(开发者自定义)即可触发资源预加载,点击按钮后立即渲染组件,不存在体验卡顿,如下图。
    iShot_2023-07-20_18.34.29.gif
2. 离线场景体验模拟

为了对比效果(有/无预加载)更加直观,以下将采用离线网络的场景下进行展示。

  • 无预加载时:按需加载在离线网络环境下会无法正常渲染,导致白屏。
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  • 有预加载时:按需加载在离线网络环境下,页面渲染体验正常,即实现拆包按需加载的用户体验等同于未拆包

预加载机制存在的必要性

  1. Any code can be split: 通过以上的预加载机制,实现应用内 Any code can be split(一切代码都可以被拆包),且能保证不影响用户体验,让开发者没有了因为单页面资源过大影响应用性能的烦恼,SPA(单页面应用) 也可以拥有极致的首屏幕加载体验和交互体验,🐟与🐻掌兼得。
  2. module federation(模块联邦) 组件预加载: 对于 webpack 的 module federation(模块联邦)而言,由于 module federation 打包出来的资源默认采用了按需动态加载的方案,因此当我们渲染一个比较大的 module federation 组件时,也会存在体验卡顿的情况,这时对该 module federation 组件进行预加载便可解决该体验问题。
  3. 革新开发者对组件懒加载的了解,减少开发者心智负担:开发者可以简单粗暴地基于页面维度对某个路由渲染的组件进行懒加载,不需要再从组件纬度去分析是因为哪个组件资源包过大导致的体验问题。
<think>好的,用户想了解如何优化UniApp应用加载速度。我需要结合已有的引用资料,给出结构清晰的建议。先,引用[1]提到性能优化、代码优化和用户体验优化,这些方面应该涵盖加载的优化点。引用[2]主要讲路由跳转,可能涉及懒加载或分包策略。 先,加载慢可能因为资源过大,所以需要代码压缩和分包加载。UniApp默认打包成单一文件,分包可以按需加载。然后,静态资源优化,比如图片压缩和懒加载减少初始请求量。接着,预加载和缓存策略,利用浏览器缓存和Vue的keep-alive。再者,路由跳转优化,引用[2]提到的插件可能帮助管理页面栈,减少初始化时间。白优化,使用骨架加载动画提升体验。最后,服务端优化,比如CDN和HTTP/2,以及监控调试,确保优化效果。 需要检查每个点是否有相关引用支持,比如分包在引用[1]的代码优化部分,预加载可能在用户体验优化里。确保每个建议都基于引用内容,并自然地添加引用标识。最后生成相关问题,覆盖优化方法、分包实现、资源优化、路由插件、缓存策略和性能监控。</think>### UniApp加载优化技巧及最佳实践 #### 一、代码优化 1. **分包加载策略** 将非页面拆分为独立分包,使用`subPackages`配置实现按需加载。主包体积建议控制在2MB以内,分包最大不超过4MB[^1] ```javascript // pages.json配置示例 { "subPackages": [ { "root": "subpackage1", "pages": [ {"path": "pageA", "style": {}}, {"path": "pageB", "style": {}} ] } ] } ``` 2. **组件/页面懒加载** 使用`uni.requireSubPackage()`动态加载非关键组件,结合`v-if`条件渲染: ```vue <template> <view> <heavy-component v-if="showComponent" /> </view> </template> <script> export default { data() { return { showComponent: false } }, mounted() { uni.requireSubPackage({ root: 'subpackage1', success: () => { this.showComponent = true } }) } } </script> ``` #### 二、资源优化(关键实践) 1. **图片压缩方案** - 使用TinyPNG在线工具批量压缩 - 配置webpack插件`image-webpack-loader` - 采用WebP格式(支持率>90%) 2. **字体文件处理** ```css /* 示例:字体子集化 */ @font-face { font-family: 'CustomFont'; src: url('/fonts/custom-subset.woff2') format('woff2'); unicode-range: U+4E00-9FFF; /* 仅包含中文常用字 */ } ``` #### 三、渲染优化 1. **骨架实现** 创建`skeleton.vue`组件: ```vue <template> <view class="skeleton"> <view class="skeleton-header animated-bg"></view> <view class="skeleton-content"> <view v-for="i in 5" :key="i" class="skeleton-line"></view> </view> </view> </template> <style> .animated-bg { animation: shimmer 2s infinite linear; background: linear-gradient( to right, #f6f7f8 0%, #edeef1 20%, #f6f7f8 40%, #f6f7f8 100% ); } </style> ``` 2. **预加载策略** 在`App.vue`中预加载关键数据: ```javascript export default { onLaunch() { this.preloadData() }, methods: { async preloadData() { const cache = uni.getStorageSync('preloadCache') if (!cache) { await this.$store.dispatch('fetchInitialData') } } } } ``` #### 四、网络优化 1. **HTTP/2配置示例** ```nginx server { listen 443 ssl http2; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/privkey.pem; location / { root /var/www/your-app; index index.html; http2_push /static/js/main.bundle.js; http2_push /static/css/main.css; } } ``` 2. **CDN缓存策略** 建议设置静态资源缓存时间: ``` Cache-Control: public, max-age=31536000 ``` #### 五、性能监控 推荐使用uni-app性能分析插件: ```javascript // 在main.js中集成 import performanceMonitor from './utils/performance-monitor' Vue.use(performanceMonitor, { metrics: ['fcp', 'lcp', 'cls'], reportInterval: 5000 }) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值