HarmonyOS( Beta5版)鸿蒙开发:应用冷启动与加载绘制首页

news2024/9/20 16:51:58

应用冷启动即当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用。

应用冷启动过程大致可分成以下四个阶段:应用进程创建&初始化、Application&Ability初始化、Ability生命周期、加载绘制首页

加载绘制首页不仅是应用冷启动的四个阶段之一,还是首帧绘制最重要的阶段。而它可以分为三个阶段:加载页面、测量和布局、渲染。本文从这三个阶段入手,分成下面三个场景进行案例优化。

减少加载页面时间

减少加载页面时间可以通过按需加载、减少自定义组件生命周期耗时两种方法来实现。

按需加载

按需加载可以避免一次性初始化和加载所有元素,从而使首帧绘制时加载页面阶段的创建列表元素时间大大减少,从而提升性能表现。

案例:每一个列表元素都被初始化和加载,为了突出效果,方便观察,设定数组中的元素有1000个,使其在加载页面阶段创建列表内元素耗时大大增加。

@Entry
@Component
struct AllLoad {
  @State arr: String[] = Array.from(Array<string>(1000), (val,i) =>i.toString());
  build() {
    List() {
      ForEach(this.arr, (item: string) => {
        ListItem() {
          Text(`item value: ${item}`)
            .fontSize(20)
            .margin({ left: 10 })
        }
      }, (item: string) => item.toString())
    }
  }
}

优化:LazyForEach替换ForEach,避免一次性初始化和加载所有元素。

class BasicDataSource implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: string[] = [];

  public totalCount(): number {
    return 0;
  }

  public getData(index: number): string {
    return this.originDataArray[index];
  }

  // 注册数据改变的监听器
  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  // 注销数据改变的监听器
  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  // 通知组件重新加载所有数据
  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    })
  }

  // 通知组件index的位置有数据添加
  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    })
  }

  // 通知组件index的位置有数据有变化
  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    })
  }

  // 通知组件删除index位置的数据并刷新LazyForEach的展示内容
  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    })
  }

  // 通知组件数据有移动
  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
    })
  }
}

class MyDataSource extends BasicDataSource {
  private dataArray: string[] = Array.from(Array<string>(1000), (val, i) => i.toString());

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): string {
    return this.dataArray[index];
  }

  public addData(index: number, data: string): void {
    this.dataArray.splice(index, 0, data);
    this.notifyDataAdd(index);
  }

  public pushData(data: string): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

@Entry
@Component
struct SmartLoad {
  private data: MyDataSource = new MyDataSource();

  build() {
    List() {
      LazyForEach(this.data, (item: string) => {
        ListItem() {
          Text(`item value: ${item}`)
            .fontSize(20)
            .margin({ left: 10 })
        }
      }, (item:string) => item)
    }
  }
}

使用SmartPerf Host工具抓取优化前后的性能数据进行对比。

优化前页面Build耗时:

reduce-redundant-operations-when-render-first-frame-all-load

优化后页面Build耗时:

reduce-redundant-operations-when-render-first-frame-smart-load

从trace图可以看出,使用ForEach时在Build阶段会创建所有元素,Build耗时65ms290μs,改为使用LazyForEach后Build耗时减少到745μs,性能收益明显。

减少自定义组件生命周期时间

LoadPage阶段需要等待自定义组件生命周期aboutToAppear的高耗时任务完成, 导致LoadPage时间大量增加,阻塞主线程后续的布局渲染,所以自定义组件生命周期的耗时任务应当转为Worker线程任务,优先绘制页面,避免启动时阻塞在startWindowIcon页面。

案例:自定义组件生命周期存在高耗时任务,阻塞主线程布局渲染。

@Entry
@Component
struct TaskSync {
  @State private text: string = '';
  private count: number = 0;

  aboutToAppear() {
    this.text = 'hello world';
    this.computeTask(); // 同步任务
  }

  build() {
    Column({space: 10}) {
      Text(this.text).fontSize(50)
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }

  computeTask() {
    this.count = 0;
    while (this.count < 100000000) {
      this.count++;
    }
    this.text = 'task complete';
  }
}

优化:自定义组件生命周期的耗时任务转为Worker线程任务,优先绘制页面,再将Worker子线程结果发送到主线程并更新到页面。

// TaskAsync.ets
import worker from '@ohos.worker';

@Entry
@Component
struct TaskAsync {
  @State private text: string = '';
  private workerInstance:worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/worker.ets');

  aboutToAppear() {
    // 处理来自子线程的消息
    this.workerInstance.onmessage = (message)=> {
      console.info(`message from worker: ${JSON.stringify(message)}`);
      this.text = JSON.parse(JSON.stringify(message)).data;
      this.workerInstance.terminate();
    }
    this.text = 'hello world';
    // 执行Worker线程任务
    this.computeTaskAsync();
  }

  build() {
    Column({space: 10}) {
      Text(this.text).fontSize(50)
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
  private async computeTaskAsync(){
    // 发送消息到子线程
    this.workerInstance.postMessage('hello world')
  }
}
// worker.ets
import worker from '@ohos.worker';

let parentPort = worker.workerPort;

function computeTask(count: number) {
  while (count < 100000000) {
    count++;
  }
  return 'task complete';
}
// 处理来自主线程的消息
parentPort.onmessage = (message) => {
  console.info(`onmessage: ${JSON.stringify(message)}`);
  // 发送消息到主线程
  parentPort.postMessage(computeTask(0));
}

使用SmartPerf Host工具抓取优化前后的性能数据进行对比。

优化前loadpage耗时:

reduce-redundant-operations-when-render-first-frame-task-sync

优化后loadpage耗时:

reduce-redundant-operations-when-render-first-frame-task-async

从trace图可以看出,优化前加载页面时loadpage耗时2s778ms807μs,其中主要耗时函数为自定义组件的生命周期函数aboutToAppear,将aboutToAppear中的耗时操作放到worker子线程中执行后,loadpage耗时减少到4ms745μs,页面加载时间大幅减少。

减少布局时间

减少布局时间可以通过异步加载和减少视图嵌套层次两种方法来实现。

异步加载

同步加载的操作,使创建图像任务需要在主线程完成,页面布局Layout需要等待创建图像makePixelMap任务的执行,导致布局时间延长。相反,异步加载的操作,在其他线程完成,和页面布局Layout同时开始,且没有阻碍页面布局,所以页面布局更快,性能更好。但是,并不是所有的加载都必须使用异步加载,建议加载尺寸较小的本地图片时将syncLoad设为true,因为耗时较短,在主线程上执行即可。

案例:使用Image组件同步加载高分辨率图片,阻塞UI线程,增加了页面布局总时间。

@Entry
@Component
struct SyncLoadImage {
  @State arr: String[] = Array.from(Array<string>(100), (val,i) =>i.toString());
  build() {
    Column() {
      Row() {
        List() {
          ForEach(this.arr, (item: string) => {
            ListItem() {
              Image($r('app.media.4k'))
                .border({ width: 1 })
                .borderStyle(BorderStyle.Dashed)
                .height(100)
                .width(100)
                .syncLoad(true)
            }
          }, (item: string) => item.toString())
        }
      }
    }
  }
}

优化:使用Image组件默认的异步加载方式加载图片,不阻塞UI线程,降低页面布局时间。

@Entry
@Component
struct AsyncLoadImage {
  @State arr: String[] = Array.from(Array<string>(100), (val,i) =>i.toString());
    build() {
      Column() {
        Row() {
          List() {
            ForEach(this.arr, (item: string) => {
              ListItem() {
                Image($r('app.media.4k'))
                  .border({ width: 1 })
                  .borderStyle(BorderStyle.Dashed)
                  .height(100)
                  .width(100)
              }
            }, (item: string) => item.toString())
          }
        }
      }
  }
}

使用SmartPerf Host工具抓取优化前后的性能数据进行对比。

优化前布局耗时:

reduce-redundant-operations-when-render-first-frame-image-sync

优化后布局耗时:

reduce-redundant-operations-when-render-first-frame-image-async

在优化前的trace图中可以看到,同步加载的每一张图片在参与布局时都会执行CreateImagePixelMap去创建图像,导致页面布局时间过长,FlushLayoutTask阶段耗时346ms458μs。图像使用异步加载进行优化后,页面布局时不再执行创建图像的任务,FlushLayoutTask阶段耗时减少到了2ms205μs,页面布局更快。

减少视图嵌套层次

视图的嵌套层次会影响应用的性能。通过减少不合理的容器组件,可以使布局深度降低,布局时间减少,优化布局性能,提升用户体验。

案例:通过Grid网格容器一次性加载1000个网格,并且额外使用3层Flex容器模拟不合理的深嵌套场景使布局时间增加。

@Entry
@Component
struct Depth1 {
  @State number: Number[] = Array.from(Array<number>(1000), (val, i) => i);
  scroller: Scroller = new Scroller();

  build() {
    Column() {
      Grid(this.scroller) {
        ForEach(this.number, (item: number) => {
          GridItem() {
            Flex() {
              Flex() {
                Flex() {
                  Text(item.toString())
                    .fontSize(16)
                    .backgroundColor(0xF9CF93)
                    .width('100%')
                    .height(80)
                    .textAlign(TextAlign.Center)
                    .border({width:1})
                }
              }
            }
          }
        }, (item:string) => item)
      }
      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
      .columnsGap(0)
      .rowsGap(0)
      .size({ width: '100%', height: '100%' })
    }
  }
}

优化:通过Grid网格容器一次性加载1000个网格,去除额外的不合理的布局容器,降低布局时间。

@Entry
@Component
struct Depth2 {
  @State number: Number[] = Array.from(Array<number>(1000), (val, i) => i);
  scroller: Scroller = new Scroller();

  build() {
    Column() {
      Grid(this.scroller) {
        ForEach(this.number, (item: number) => {
          GridItem() {
            Text(item.toString())
              .fontSize(16)
              .backgroundColor(0xF9CF93)
              .width('100%')
              .height(80)
              .textAlign(TextAlign.Center)
              .border({width:1})
          }
        }, (item:string) => item)
      }
      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
      .columnsGap(0)
      .rowsGap(0)
      .size({ width: '100%', height: '100%' })
    }
  }
}

使用SmartPerf Host工具抓取优化前后的性能数据进行对比。

优化前布局耗时:

reduce-redundant-operations-when-render-first-frame-view-nested-layout

优化后布局耗时:

reduce-redundant-operations-when-render-first-frame-view-unnested-layout

根据trace图对比优化前后的布局时长,优化前FlushLayoutTask阶段耗时11ms48μs,优化后FlushLayoutTask耗时减少到5ms33μs,布局时间明显减少。

减少渲染时间

减少渲染时间可以通过条件渲染替代显隐控制的方法来实现。

条件渲染

使用Visibility、if条件判断都可以控制元素显示与隐藏,但是初次加载时使用visibility隐藏元素也会创建对应组件内容,因此加载绘制首页时,如果组件初始不需要显示,建议使用条件渲染替代显隐控制,以减少渲染时间。关于条件渲染和显隐控制更多内容可以参考合理选择条件渲染和显隐控制。

案例:初次渲染通过visibility属性隐藏Image组件,为了突出效果,方便观察,设置Image的数量为1000个。

@Entry
@Component
struct VisibilityExample {
  private data: number[] = Array.from(Array<number>(1000), (val, i) => i);

  build() {
    Column() {
      // 隐藏不参与占位
      Text('None').fontSize(9).width('90%').fontColor(0xCCCCCC)
      Column() {
        ForEach(this.data, () => {
          Image($r('app.media.4k'))
            .width(20)
            .height(20)
        })
      }
      .visibility(Visibility.None)
    }.width('100%').margin({ top: 5 })
  }
}

优化:通过条件渲染替代显隐控制。

@Entry
@Component
struct IsVisibleExample {
  @State isVisible: boolean = false;
  private data: number[] = Array.from(Array<number>(1000), (val, i) => i);

  build() {
    Column() {
      // 隐藏不参与占位
      Text('None').fontSize(9).width('90%').fontColor(0xCCCCCC)
      if (this.isVisible) {
        Column() {
          ForEach(this.data, () => {
            Image($r('app.media.4k'))
              .width(20)
              .height(20)
          })
        }
      }
    }.width('100%').margin({ top: 5 })
  }
}

使用SmartPerf Host工具抓取优化前后的性能数据进行对比。

优化前页面Build耗时:

reduce-redundant-operations-when-render-first-frame-visibility-build

优化前render_service首帧耗时:

reduce-redundant-operations-when-render-first-frame-visibility-rs

优化后Build耗时:

reduce-redundant-operations-when-render-first-frame-ifelse-build

优化后render_service首帧耗时:

reduce-redundant-operations-when-render-first-frame-ifelse-rs

说明:在App泳道找到页面加载后第一个ReceiveVsync,其中的Trace标签H:MarshRSTransactionData携带参数transactionFlag,在render_service泳道找到相同transactionFlag的标签H:RSMainThread::ProcessCommandUni,其所属的ReceiveVsync时长就是render_service首帧耗时。

从trace图可以看出,优化前使用Visibility.None隐藏图片后在Build阶段仍然有Image元素创建,Build耗时82ms230μs,使用if else隐藏图片后Build阶段耗时减少到660μs,显著减少页面加载耗时。同时优化前应用的render_service首帧耗时为10ms55μs,而优化后减少到了1ms604μs,渲染时间明显减少。

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:


 鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频学习教程+学习PDF文档

HarmonyOS Next 最新全套视频教程

  纯血版鸿蒙全套学习文档(面试、文档、全套视频等)              

​​

 

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2085683.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常用Pandas操作(笔记整理)

目录 一、常用 1、创建DataFrame&#xff08;数据导入&#xff09; 2. 查看数据前⼏⾏&#xff08;head&#xff09; 3. 查看数据后⼏⾏&#xff08;tail&#xff09; 4. 查看数据基本信息&#xff08;info&#xff09; 5. 使⽤ value_counts 计算唯⼀值的频率 6. 描述性…

SpringBoot 3.x+Mybatis Plus多数据源极简配置

1. 创建项目 创建一个名为mybatis-plus-demo的项目&#xff0c;使用MavenJDK17。不会的请看 IntelliJ IDEA快速创建Spring Boot项目&#xff0c;最终项目结构&#xff0c;如下图。 2. 编写代码 根据最终项目结构&#xff0c;从下往上依次列出各个文件的代码。 2.1 pom.xml…

迷雾大陆攻略:VMOS云手机流派辅助和技能加持助力!

在《迷雾大陆》这款游戏中&#xff0c;选择一个合适的流派和技能加点至关重要。使用VMOS云手机&#xff0c;玩家可以享受到专属定制的云手机&#xff0c;内置游戏安装包&#xff0c;无需重新下载安装游戏。同时&#xff0c;VMOS云手机能够24小时不间断运行&#xff0c;自动完成…

STM32(F103ZET6)第二十课:FreeRtos操作系统的应用

目录 调试方式一、任务堆栈溢出检测二、任务管理方式三、二值信号量(任务同步)四、计数信号量五、互斥信号量六、队列 调试方式 问题&#xff1a;传感器数据获取问题&#xff0c;有的DHT11能获取到&#xff0c;有的获取不到 两种方式&#xff1a;调优先级或者进临界区&#xf…

Docker原理及实例

目录 一 Docker简介及部署方法 1.1 Docker简介 1.1.1 什么是docker&#xff1f; 1.1.2 docker在企业中的应用场景 1.1.3 docker与虚拟化的对比 1.1.4 docker的优势 2 部署docker 2.1 容器工作方法 2.2 部署第一个容器 2.2.1 配置软件仓库 2.2.2 安装docker-ce并启动服…

排序算法见解(2)

1.快速排序 1.1基本思想&#xff1a; 快速排序是通过一趟排序将待排序的数据分割成独立的两部分&#xff0c;其中一部分的所有数据都比另一部分的所有数据都要小&#xff0c;然后再按此方法对这两部分数据分别进行快速排序&#xff0c;整个排序过程可以递归进行&#xff0c;以…

如何构建Java SpringBoot文献检索系统:高效管理学术资料,掌握数据库核心技术

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

搭上“跨境电商”便车,中国家居在海外越杀越勇

文&#xff5c;新熔财经 作者&#xff5c;楷楷 从去年至今&#xff0c;已有多家跨境家居企业谋求IPO&#xff0c;包括近期启动IPO辅导的圣奥科技&#xff1b;今年4月正式向港交所递交招股说明书的傲基科技&#xff1b;去年11月启动A股IPO的跨境家居品牌杰西亚&#xff0c;还有…

vulhub xxe靶机通关教程

首先我们进行端口扫描 扫出来端口之后去尝试得出地址为183 发现robots.txt文件 由此我们就得到了两个目录 我们先进入xxe目录&#xff0c;并开始登录抓包 并进入重放器在xml里面构造语句 <!DOCTYPE r [ <!ELEMENT r ANY > <!ENTITY admin SYSTEM "php://filt…

【在Linux世界中追寻伟大的One Piece】传输层协议UDP

目录 1 -> 传输层 2 -> 端口号 2.1 -> 端口号范围划分 2.2 -> 知名端口号 3 -> UDP协议 3.1 -> UDP协议端格式 3.2 -> UDP的特点 3.2.1 -> 面向数据报 3.3 -> UDP的缓冲区 3.4 -> UDP使用注意事项 3.5 -> 基于UDP的应用层协议 1 -…

Vue实战:轻松掌握输入框@功能实现技巧

成员列表 创建 实现成员列表的方式比较简单&#xff0c;其实就是一个列表&#xff0c;一个简单的v-for循环就可以搞定&#xff0c;点击时将当前选择的成员项回调给父组件。 新增一个AtPop.vue文件&#xff1a; <template> <div class"at-pop-index">…

DIFFUSION 系列笔记| Latent Diffusion Model、Stable Diffusion基础概念、数学原理、代码分析、案例展示

目录 Latent Diffusion Model LDM 主要思想 LDM使用示例 LDM Pipeline LDM 中的 UNET 准备时间步 time steps 预处理阶段 pre-process 下采样过程 down sampling 中间处理 mid processing 上采样 upsampling 后处理 post-process LDM Super Resolution Pipeline…

海康视觉二次开发学习笔记8-从回调函数获取结果

回调函数使用方法 通常在方案或流程执行完成后,就可以获取到流程运行的结果.运行一次流程后,我们就可以获取到流程的渲染结果以及流程的数据结果.那么使用通讯或硬件进行外部触发时,如何获取结果呢? 这种时候就要用到回调函数. 1. 注释原获取结果代码 2. 注册回调函数 在构…

【文献及模型、制图分享】数字技术力量下传统村落景观修复演进的特征与机制研究——以岳阳市张谷英村为例(GIS空间分析、点云提取)

文献介绍 景观修复作为弘扬中华优秀传统文化的重要方式&#xff0c;如何在乡村数字化新时代背景下&#xff0c;把握传统村落景观修复的数字赋能&#xff0c;已成为推动中华优秀传统文化创造性转化与创新性发展亟需解决的科学问题。运用深度访谈、GIS空间分析、点云数据提取等方…

html+css+js网页设计 婚庆类型12个页面

htmlcssjs网页设计 婚庆类型12个页面 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#xf…

FreeRTOS 低功耗模式

正如STM32的裸机编程一样&#xff0c;FreeRTOS通用提供了低功耗模式。 前面说的很明白&#xff0c;FreeRTOS的低功耗模式实际上还是基于STM32的低功耗模式指令进入睡眠模式来实现的&#xff0c;并且只要中断来临&#xff0c;就会退出低功耗&#xff0c;FreeRTOS的系统时钟是最底…

猫头虎分享:Python库 Statsmodels 的简介、安装、用法详解入门教程

猫头虎分享&#xff1a;Python库 Statsmodels 的简介、安装、用法详解入门教程 &#x1f42f; 引言 &#x1f3af; 今天猫头虎带您 深入探讨 Statsmodels 这个在数据分析和统计建模领域非常重要的Python库。最近有粉丝在评论区问道&#xff1a;“猫哥&#xff0c;如何使用 St…

USB端点

USB端点 各端点使用循环冗余校验&#xff08;CRC&#xff09;来检测传输中发生的错误。 根据 USB 规范&#xff0c;设备端点是 USB 设备中一个独特的可寻址部分&#xff0c;它作为主机和设备间通信流的信息源或库。 USB 枚举和配置一节介绍了设备向默认地址做出响应的步骤。 枚…

能进大厂的自动化测试面试题

前言 每次到金九银十都避免不了要聊一聊面试题了&#xff0c;如今九月已经是中下旬了&#xff0c;马上就要到十月份了&#xff0c;还在投简历找工作的小伙伴可以看看我这几天发的文章&#xff0c;最近发的都是面试题&#xff0c;如果需要笔者教一下大家怎么写简历的小伙伴可以…

光伏高压并网升压箱变

在当今能源领域的变革浪潮中&#xff0c;光伏能源以其清洁、可再生的显著优势&#xff0c;成为了备受瞩目的焦点。而光伏高压并网升压箱变&#xff0c;则是实现光伏电能顺利接入高压电网的核心设备。 光伏高压并网升压箱变宛如光伏能源系统中的一位“大力士”&#xff0c;承担着…