欢迎光临
我们一直在努力

HarmonyOS Web 组件 loadData 白屏:这个坑折磨了我两小时

前言

说实话,我一开始真没想到这个问题能卡我这么久。 需求很简单:页面上有个输入框让用户输入 HTML 片段,下面一个 Web 组件实时渲染这段 HTML。用 HarmonyOS 的 WebviewController.loadData 就能实现,我心想这能有多复杂? 结果编译跑起来一看——Web 区域一片空白。输入框正常,加载按钮正常,唯独渲染区是白的。没有报错,没有崩溃,就是安静地空着。 搞了两个多小时,终于找到了原因。写篇文章记录一下,希望你不用再踩这个坑。

怎么用 Web 组件加载 HTML 片段

效果演示

Web 组件加载 HTML 片段的标准做法是:

  • 把 HTML 片段包装成完整文档
  • 用 WebviewController.loadData 加载
  • 这个思路完全正确,代码写出来也像那么回事:

    // 把 HTML 片段包装成完整文档
    private wrapHtml(content: string): string {
    return `<!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
    body { margin: 0; padding: 16px; font-family: sans-serif; color: #333; }
    </style>
    </head>
    <body>
    <div class="container">
    ${content}</div>
    </body>
    </html>
    `

    }

    // 加载 HTML
    private doLoad() {
    const html = this.wrapHtml(this.htmlInput)
    this.controller.loadData(html, 'text/html', 'UTF-8')
    }

    看起来没毛病对吧?结果一跑,Web 区就是白屏。


    坑一:src 空字符串不稳定

    第一个问题出在 Web 组件的 src 属性。

    我一开始写的是 src: ''(空字符串),然后在 onPageEnd 里触发 loadData。问题是:空字符串在部分设备上不会触发 onPageEnd 回调。

    Web 内核根本没有完成初始化,loadData 调用的时序就乱了。

    修复方案很简单——换成 'about:blank':

    Web({
    src: 'about:blank', // 用 about:blank,不用空字符串
    controller: this.controller
    })
    .onPageEnd(() => {
    this.doLoad()
    })

    about:blank 是标准空白页,Web 内核加载流程完整,onPageEnd 必然触发。

    坑二:onPageEnd 的死循环风险

    这个问题藏得比较深。

    loadData 执行完成后,页面状态变了,onPageEnd 会再次被触发。如果你在 onPageEnd 里直接调 doLoad(),就会形成循环:

    onPageEnd → doLoad → loadData完成 → onPageEnd → doLoad → …

    还好 HarmonyOS 的 Web 内核不会真的无限循环,但这种时序问题会导致一些奇怪的行为。

    用一个标志位只让首次 onPageEnd 执行加载:

    private isFirstLoad: boolean = true

    Web({ src: 'about:blank', controller: this.controller })
    .onPageEnd(() => {
    if (this.isFirstLoad) {
    this.isFirstLoad = false
    this.doLoad()
    }
    })


    坑三:loadData 只传 3 个参数会白屏(最关键的坑)

    上面两个坑修完之后,Web 区域依然是一片空白。

    没有报错,没有异常,就这么安静地空着。

    排查了很久,最后发现问题出在这里:

    // 这样写会白屏
    this.controller.loadData(html, 'text/html', 'UTF-8')

    // 正确写法,要传 5 个参数
    this.controller.loadData(html, 'text/html', 'UTF-8', ' ', ' ')

    HarmonyOS 的 loadData 完整签名是:

    loadData(data: string, mimeType: string, encoding: string,
    baseUrl?: string, historyUrl?: string): void

    后两个参数 baseUrl 和 historyUrl 是可选的,但它们决定了 Web 内核如何解析 HTML 内容中的某些字符。

    不传这两个参数时,CSS 里所有的 # 十六进制颜色值(#007DFF、#333、#666)全部解析失败,页面直接白屏。

    注意:后两个参数必须传空格字符串 ' ',不能是空字符串 '',效果完全不同。空格字符串等价于 "about:blank" 的 baseUrl 效果,是官方推荐的占位写法。


    最终完整代码

    修完之后的核心逻辑长这样:

    import web_webview from '@ohos.web.webview'
    import promptAction from '@ohos.promptAction'
    import { BusinessError } from '@ohos.base'

    @Entry
    @Component
    struct LoadDataPage {
    controller: web_webview.WebviewController = new web_webview.WebviewController()
    // 首次加载标志 —— 只在 Web 内核 ready 后执行一次
    private isFirstLoad: boolean = true
    @State htmlInput: string = '<h2>默认标题</h2><p>默认内容,可以修改后重新加载~</p>'

    // 把 HTML 片段包装成完整文档
    private wrapHtml(content: string): string {
    return `<!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
    body { margin: 0; padding: 16px; font-family: -apple-system, sans-serif; color: #333; }
    .container { max-width: 100%; word-break: break-word; }
    </style>
    </head>
    <body>
    <div class="container">
    ${content}</div>
    </body>
    </html>
    `

    }

    // 执行加载
    private doLoad() {
    const html = this.wrapHtml(this.htmlInput)
    try {

    this.controller.loadData(html, 'text/html', 'UTF-8', ' ', ' ')
    } catch (e) {
    const err = e as BusinessError
    console.error('[LoadDataPage] loadData 失败 code=' + err.code + ' msg=' + err.message)
    promptAction.showToast({ message: '加载失败:' + err.message, duration: 2000 })
    }
    }

    build() {
    Column() {
    // 顶部栏
    Row() {
    Text('loadData 示例')
    .fontSize(20)
    .fontWeight(FontWeight.Bold)
    .fontColor(Color.White)
    }
    .width('100%')
    .height(56)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#007DFF')

    // HTML 输入区
    Column({ space: 8 }) {
    Text('输入 HTML 内容(支持片段):')
    .fontSize(14)
    .fontColor('#666')
    .width('100%')

    TextArea({ placeholder: '输入HTML片段…', text: this.htmlInput })
    .onChange((value: string) => { this.htmlInput = value })
    .width('100%')
    .height(80)
    .backgroundColor('#F5F5F5')
    .borderRadius(8)
    }
    .width('100%')
    .padding(16)

    // 加载按钮
    Button('重新加载 HTML')
    .width('100%')
    .height(44)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#007DFF')
    .onClick(() => this.doLoad())

    Divider().margin({ top: 12, bottom: 12 })

    // Web 渲染区域标题
    Text('渲染结果:')
    .width('100%')
    .padding({ left: 16 })
    .fontSize(14)
    .fontColor('#666')

    Web({

    src: 'about:blank',
    controller: this.controller
    })
    .javaScriptAccess(true)
    .domStorageAccess(true)
    .fileAccess(true)
    .mixedMode(MixedMode.All)
    .width('100%')
    .layoutWeight(1)

    .onPageEnd((event) => {
    if (this.isFirstLoad) {
    this.isFirstLoad = false
    console.info('[LoadDataPage] Web 内核就绪,执行首次 loadData')
    this.doLoad()
    }
    })
    .onErrorReceive((event) => {
    console.error('[LoadDataPage] 页面错误:' + event?.error?.getErrorInfo())
    })
    .onHttpErrorReceive((event) => {
    console.warn('[LoadDataPage] HTTP 错误:' + event?.response?.getResponseCode())
    })
    }
    .width('100%')
    .height('100%')
    }
    }

    踩坑总结

    这次一共踩了 3 个坑,按重要性排序:

    #问题根因修复
    1 Web 区白屏 loadData 只传 3 个参数,CSS # 颜色值解析失败 传 5 个参数,后两个加空格
    2 首次加载不触发 src: '' 空字符串在部分设备上不稳定 改为 src: 'about:blank'
    3 潜在的循环调用 onPageEnd 里直接调 doLoad() 加 isFirstLoad 标志控制

    第一个坑是最恶心的,因为它完全不报错,就这么静悄悄地白着,排查起来非常费劲。

    写在最后

    HarmonyOS 的文档有时候写得太简略了,loadData 的可选参数背后藏着这么大的一个坑,完全没有说明。CSDN 上有人提到过这个问题的 解决方案,但我没在任何官方文档里找到相关说明。 建议:如果你的 HTML 内容里包含 CSS(基本上都会包含),直接用 5 参数版本的 loadData,别犹豫。 有问题欢迎评论区交流。

    赞(0)
    未经允许不得转载:171主机测评 » HarmonyOS Web 组件 loadData 白屏:这个坑折磨了我两小时
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址