为一篇 HarmonyOS7 技术教程绘制一张 sketch-notes 风格的信息图,主题是 We

前言

Web 组件放进页面,只是第一步。真正到了业务里,页面几乎不可能永远只加载一个固定地址。活动页会切换链接,帮助中心会跳不同文档,调试阶段也经常要在多个测试地址之间来回切。

为一篇 HarmonyOS7 ArkUI 教程绘制一张 sketch-notes 风格的框架图,展示

所以这篇更重要的,不是“加载网页”本身,而是 URL 切换时的状态管理。什么时候开始加载,进度怎么显示,按钮点完后页面怎么反馈,这些细节不处理,用户会觉得页面是卡住了,不是正在加载。

这个案例的核心能力

案例设计得很直接:一边维护当前 URL,一边模拟加载进度,再提供几个快捷按钮切换地址。它实际上是在讲一套最小网页加载流程。

里面最值得关注的是三个状态:

  • currentUrl:当前要加载的网页地址
  • loadProgress:当前进度
  • isLoading:是否处于加载中

有了这三个状态,页面就能把“我现在正在打开哪个地址”和“它到底加载到哪一步了”表达出来。

完整代码

import { DEMO_BG_COLOR, DEMO_CARD_COLOR, DEMO_THEME_COLOR, DEMO_SUBTEXT_COLOR } from './types'

@Entry
@Component
struct WebUrlLoadingFlow {
  @State isShow: boolean = true
  @State currentUrl: string = 'https://www.example.com'
  @State loadProgress: number = 0
  @State isLoading: boolean = false

  loadUrl(url: string): void {
    this.currentUrl = url
    this.isLoading = true
    this.loadProgress = 0

    const timer = setInterval(() => {
      if (this.loadProgress < 100) {
        this.loadProgress += 10
      } else {
        this.isLoading = false
        clearInterval(timer)
      }
    }, 200)
  }

  build() {
    Column() {
      if (this.isShow) {
        Scroll() {
          Column({ space: 16 }) {
            Text('Web 加载 URL').fontSize(16).fontColor(DEMO_THEME_COLOR).fontWeight(FontWeight.Bold)

            Column() {
              Row() {
                Text('当前: ').fontSize(12).fontColor('#666666')
                Text(this.currentUrl).fontSize(12).fontColor(DEMO_THEME_COLOR).maxLines(1)
              }.margin({ bottom: 8 })

              if (this.isLoading) {
                Progress({ value: this.loadProgress, total: 100, type: ProgressType.Linear })
                  .width('100%')
                  .color(DEMO_THEME_COLOR)
              }

              Column() {
                Text('Web View').fontSize(16).fontColor('#FFFFFF')
                Text(this.currentUrl).fontSize(11).fontColor('#FFFFFF80').margin({ top: 4 })
              }
              .width('100%')
              .height(180)
              .backgroundColor('#444444')
              .borderRadius(10)
              .justifyContent(FlexAlign.Center)
            }
            .backgroundColor(DEMO_CARD_COLOR)
            .padding(12)
            .borderRadius(12)

            Column() {
              Text('快捷切换 URL').fontSize(13).fontColor('#4D96FF').margin({ bottom: 8 })
              Row({ space: 6 }) {
                Button('百度')
                  .onClick(() => { this.loadUrl('https://www.baidu.com') })
                  .fontSize(12)
                  .backgroundColor('#4D96FF')

                Button('腾讯')
                  .onClick(() => { this.loadUrl('https://www.tencent.com') })
                  .fontSize(12)
                  .backgroundColor('#6BCB77')

                Button('本地')
                  .onClick(() => { this.loadUrl('about:blank') })
                  .fontSize(12)
                  .backgroundColor('#FFA500')
              }.width('100%').justifyContent(FlexAlign.SpaceEvenly)
            }
            .backgroundColor('#E8F4FD')
            .padding(12)
            .borderRadius(12)

            Column({ space: 6 }) {
              Text('URL 加载说明').fontSize(14).fontColor(DEMO_THEME_COLOR).fontWeight(FontWeight.Bold)
              Text('支持 https:// http:// 网络协议').fontSize(12).fontColor('#333333')
              Text('支持加载本地 HTML 文件').fontSize(12).fontColor('#333333')
              Text('.onPageBegin/onPageEnd 监听加载状态').fontSize(12).fontColor('#333333')
              Text('.onProgressChange 获取加载百分比').fontSize(12).fontColor('#333333')
              Text('WebController 提供前进/后退/刷新方法').fontSize(12).fontColor('#333333')
            }
            .width('100%')
            .backgroundColor('#F0F0FF')
            .padding(14)
            .borderRadius(12)
          }
          .width('100%')
        }
      }

      Text('WebUrlLoadingFlow - 加载不同网页地址')
        .fontSize(12)
        .fontColor(DEMO_SUBTEXT_COLOR)
        .margin({ top: 12 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(DEMO_BG_COLOR)
    .padding(16)
  }
}

为一篇关于 HarmonyOS7 网页地址切换的技术文章绘制一张 sketch-notes 风格流程

loadUrl() 这个方法为什么是全篇重点

这个案例最核心的代码,其实就这一段:

loadUrl(url: string): void {
  this.currentUrl = url
  this.isLoading = true
  this.loadProgress = 0

  const timer = setInterval(() => {
    if (this.loadProgress < 100) {
      this.loadProgress += 10
    } else {
      this.isLoading = false
      clearInterval(timer)
    }
  }, 200)
}

你可以把它理解成一次完整的“网页切换事务”。

先更新地址,再告诉页面“我要开始加载了”,然后把旧进度清零,最后通过定时器模拟新的加载过程。虽然这里不是实际的 Web 回调,但状态切换逻辑已经搭好了。

真实项目里,你完全可以把 setInterval 这套模拟逻辑,换成 onPageBeginonProgressChangeonPageEnd 驱动。

为什么要把地址显示出来

案例顶部有一行很朴素的当前地址展示:

Row() {
  Text('当前: ')
  Text(this.currentUrl).maxLines(1)
}

这一行看起来不复杂,但在调试和运维页面里特别有用。

因为 Web 内容是黑盒感很强的区域,你如果不把当前地址显式展示出来,切换失败时很难第一时间判断是路由没变、状态没更新,还是网页本身打不开。

尤其做内部工具、配置后台、嵌套调试页时,这种“把上下文直接给出来”的做法,能省很多排查时间。

进度条为什么值得单独做

很多 Web 容器页的问题,不是慢,而是“用户不知道它是不是还活着”。

案例在 isLoadingtrue 时显示进度条:

if (this.isLoading) {
  Progress({ value: this.loadProgress, total: 100, type: ProgressType.Linear })
    .width('100%')
    .color(DEMO_THEME_COLOR)
}

这类加载反馈最重要的价值,不是告诉用户精确百分比,而是告诉用户:页面在处理,不是没反应。

如果你后面接的是实际 Web 组件,这块完全可以绑定 onProgressChange 回传的数据。只要用户看到进度在动,耐心就会多很多。

按钮切换 URL 的结构为什么好用

案例用了三个按钮模拟不同来源:

Button('百度').onClick(() => { this.loadUrl('https://www.baidu.com') })
Button('腾讯').onClick(() => { this.loadUrl('https://www.tencent.com') })
Button('本地').onClick(() => { this.loadUrl('about:blank') })

这个写法其实非常适合你在开发期快速验证 Web 页面行为。比如:

  • 外网地址能不能正常打开
  • 不同域名切换时状态有没有残留
  • 空白页、本地页、异常页时加载态是否正常收口

尤其最后这个 about:blank,虽然只是空白页,但很适合测试切换逻辑是不是稳定。

关键代码怎么迁移到真实 Web 组件

如果你要把这个案例真正变成可运行网页容器,思路很简单。

第一步,定义 WebController

第二步,把中间占位区域换成真正的 Web({ src, controller })

第三步,用加载回调接管模拟定时器:

  • onPageBegin 时设置 isLoading = true
  • onProgressChange 时更新 loadProgress
  • onPageEnd 时设置 isLoading = false
  • onErrorReceive 时关闭加载态并提示错误

也就是说,这个案例讲的不是“假数据页面”,而是一套你后面能直接换成真逻辑的页面框架。

这类页面最常见的坑

第一个坑,是重复点击切换按钮时没有处理前一个加载流程。结果进度条和地址状态打架,页面会出现错乱。

第二个坑,是加载失败后忘了把 isLoading 关掉。用户看到的就是一根永远不结束的进度条。

第三个坑,是 URL 太长又没有做 maxLines(1),界面会被地址撑坏。

第四个坑,是只考虑线上地址,不考虑本地页和空白页调试。实际开发阶段,这两种地址非常有用。

这个案例适合用在哪些业务里

协议页、帮助中心、运营落地页、嵌套控制台、企业内部混合页面,都很适合用这套结构。

尤其当你需要“在一个原生壳子里切不同网页内容”时,状态和反馈比视觉更重要。因为用户点完按钮后,第一需求从来不是炫酷,而是确定系统在响应。

写在最后

网页切换这件事,表面看只是改一个 src,实际做得稳不稳,全看你有没有把状态流理顺。地址、加载中、进度,这三个状态处理好,用户体验就不会差到哪里去。

等你把这套流程跑顺,再往后做前进、后退、刷新,甚至做 JS 桥接,思路都会清楚很多。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐