本文同步发表于 微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

Web组件支持加载方式:

加载方式 适用场景 特点
网络页面 在线内容、Web应用 需要网络权限,支持动态加载
本地页面 离线内容、启动页 加载速度快,无网络依赖
HTML文本 动态内容、富文本 无需文件,直接加载HTML字符串

二、加载网络页面

2.1 网络页面加载

import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct WebNetworkPage {
  controller: webview.WebviewController = new webview.WebviewController();

  build() {
    Column() {
      // 1. 初始加载网络页面
      Web({ 
        src: 'https://www.example.com', 
        controller: this.controller 
      })
    }
  }
}

2.2 动态切换网络页面

@Entry
@Component
struct DynamicWebPage {
  controller: webview.WebviewController = new webview.WebviewController();
  
  // 重要:src参数不能通过@State变量动态更改!
  // 必须使用loadUrl()方法

  build() {
    Column() {
      // 切换页面的按钮
      Button('加载新页面')
        .onClick(() => {
          try {
            // 使用loadUrl动态加载新页面
            this.controller.loadUrl('https://www.example1.com');
          } catch (error) {
            const err = error as BusinessError;
            console.error(`加载失败!错误码: ${err.code}, 消息: ${err.message}`);
          }
        })
        .margin(10)
      
      // 显示Web组件
      Web({ 
        src: 'https://www.example.com',  // 初始页面
        controller: this.controller 
      })
    }
  }
}

2.3 网络权限配置

必须module.json5中配置网络访问权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

三、加载本地页面

3.1 加载rawfile目录下的文件

项目结构
src/
├── main/
│   ├── ets/
│   │   └── pages/
│   │       └── WebLocalPage.ets
│   └── resources/
│       └── rawfile/
│           ├── local.html
│           ├── local1.html
│           └── styles.css
代码实现
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct WebLocalPage {
  controller: webview.WebviewController = new webview.WebviewController();

  build() {
    Column() {
      // 切换本地页面的按钮
      Button('切换到local1.html')
        .onClick(() => {
          try {
            // 使用$rawfile加载rawfile目录下的文件
            this.controller.loadUrl($rawfile('local1.html'));
          } catch (error) {
            const err = error as BusinessError;
            console.error(`加载失败!错误码: ${err.code}, 消息: ${err.message}`);
          }
        })
        .margin(10)
      
      // 初始加载本地页面
      Web({ 
        src: $rawfile('local.html'),  // 加载rawfile/local.html
        controller: this.controller 
      })
    }
  }
}
本地HTML文件示例

local.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <!-- 加载本地CSS文件 -->
    <link rel="stylesheet" href="resource://rawfile/styles.css">
</head>
<body>
    <h1>欢迎使用本地页面</h1>
    <p>这是一个本地HTML文件示例</p>
    <button onclick="showAlert()">点击测试</button>
    
    <script>
    function showAlert() {
        alert('本地页面JavaScript正常运行!');
    }
    </script>
</body>
</html>

styles.css:

body {
    font-family: 'Arial', sans-serif;
    padding: 20px;
    background-color: #f5f5f5;
}

h1 {
    color: #333;
    text-align: center;
}

3.2 加载沙箱路径下的文件

全局上下文管理类
// GlobalContext.ets - 全局状态管理
export class GlobalContext {
  private constructor() {}
  
  private static instance: GlobalContext;
  private _objects = new Map<string, Object>();

  // 获取单例实例
  public static getContext(): GlobalContext {
    if (!GlobalContext.instance) {
      GlobalContext.instance = new GlobalContext();
    }
    return GlobalContext.instance;
  }

  // 获取对象
  getObject(key: string): Object | undefined {
    return this._objects.get(key);
  }

  // 设置对象
  setObject(key: string, objectClass: Object): void {
    this._objects.set(key, objectClass);
  }
}
在Ability中设置沙箱路径
// EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { GlobalContext } from './GlobalContext';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    // 保存沙箱路径到全局上下文
    GlobalContext.getContext().setObject("filesDir", this.context.filesDir);
    
    console.info("沙箱路径: " + GlobalContext.getContext().getObject("filesDir"));
    
    // 可选:创建并写入测试HTML文件
    this.createTestHtmlFile();
  }
  
  // 创建测试HTML文件
  private async createTestHtmlFile() {
    const fs = this.context.filesDir;
    const filePath = fs + '/index.html';
    
    const htmlContent = `
    <!DOCTYPE html>
    <html>
    <body>
        <h1>沙箱中的HTML文件</h1>
        <p>这是一个存储在应用沙箱中的文件</p>
    </body>
    </html>`;
    
    // 使用文件管理API写入文件
    // 实际开发中需要使用@kit.FileManagementKit
  }
}
加载沙箱文件
import { webview } from '@kit.ArkWeb';
import { GlobalContext } from './GlobalContext';

@Entry
@Component
struct SandboxWebPage {
  controller: webview.WebviewController = new webview.WebviewController();
  
  // 构建沙箱文件URL
  private sandboxUrl: string = '';
  
  aboutToAppear() {
    // 获取沙箱路径并构建文件URL
    const filesDir = GlobalContext.getContext().getObject('filesDir') as string;
    if (filesDir) {
      this.sandboxUrl = 'file://' + filesDir + '/index.html';
      console.info('沙箱文件URL: ' + this.sandboxUrl);
    }
  }

  build() {
    Column() {
      // 加载沙箱中的文件,必须开启fileAccess权限
      Web({ 
        src: this.sandboxUrl, 
        controller: this.controller 
      })
      .fileAccess(true)  // 开启文件访问权限
    }
  }
}

四、加载HTML格式的文本数据

4.1 使用loadData()方法

适用于动态生成或从网络获取的HTML内容。

import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct HtmlTextPage {
  controller: webview.WebviewController = new webview.WebviewController();

  build() {
    Column() {
      Button('加载HTML文本')
        .onClick(() => {
          try {
            // 定义HTML内容
            const htmlContent = `
            <html>
            <head>
                <style>
                    body { 
                        font-family: sans-serif; 
                        padding: 20px;
                        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                        color: white;
                    }
                    h1 { text-align: center; }
                    .card {
                        background: rgba(255,255,255,0.1);
                        padding: 20px;
                        border-radius: 10px;
                        margin: 20px 0;
                    }
                </style>
            </head>
            <body>
                <h1>动态HTML内容</h1>
                <div class="card">
                    <h3>欢迎使用</h3>
                    <p>这是通过loadData加载的动态HTML内容</p>
                    <p>当前时间: ${new Date().toLocaleString()}</p>
                </div>
            </body>
            </html>`;
            
            // 加载HTML文本数据
            this.controller.loadData(
              htmlContent,    // HTML字符串
              'text/html',    // MIME类型
              'UTF-8'         // 编码格式
            );
            
          } catch (error) {
            const err = error as BusinessError;
            console.error(`加载失败!错误码: ${err.code}, 消息: ${err.message}`);
          }
        })
        .margin(10)
      
      // 初始显示一个页面
      Web({ 
        src: $rawfile('default.html'), 
        controller: this.controller 
      })
    }
  }
}

4.2 使用data URL方式

更简洁的直接加载方式。

@Entry
@Component
struct DataUrlWebPage {
  controller: webview.WebviewController = new webview.WebviewController();
  
  // 直接使用data URL
  htmlStr: string = 'data:text/html,' + encodeURIComponent(`
    <html>
    <body style="background:#f0f0f0;padding:20px;">
        <h2 style="color:#333;">Data URL加载示例</h2>
        <p>这是通过data URL直接加载的HTML内容</p>
        <ul>
            <li>优点:无需额外文件</li>
            <li>缺点:URL长度有限制</li>
        </ul>
    </body>
    </html>`);

  build() {
    Column() {
      Web({ 
        src: this.htmlStr, 
        controller: this.controller 
      })
    }
  }
}

4.3 处理大量HTML内容

当加载大量HTML时,建议设置baseUrl参数:

// 加载大量HTML时使用
this.controller.loadData(
  largeHtmlContent,
  'text/html',
  'UTF-8',
  'data'  // baseUrl参数,避免URL过长问题
);

五、使用resource协议加载资源

5.1 resource协议

resource://协议专门用于访问应用资源目录中的文件,是加载本地资源的推荐方式。

import { webview } from '@kit.ArkWeb';

@Entry
@Component
struct ResourceWebPage {
  controller: webview.WebviewController = new webview.WebviewController();

  build() {
    Column() {
      Button('加载resource资源')
        .onClick(() => {
          try {
            // 使用resource协议加载rawfile目录下的文件
            this.controller.loadUrl('resource://rawfile/page2.html');
          } catch (error) {
            console.error(`加载失败!错误码: ${error.code}, 消息: ${error.message}`);
          }
        })
        .margin(10)
      
      // 初始使用resource协议加载
      Web({ 
        src: 'resource://rawfile/page1.html', 
        controller: this.controller 
      })
    }
  }
}

5.2 HTML中引用本地资源

在HTML文件中,可以使用resource://协议引用CSS、JS、图片等本地资源:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <!-- 加载本地CSS -->
    <link rel="stylesheet" href="resource://rawfile/styles/main.css">
    
    <!-- 加载本地JavaScript -->
    <script src="resource://rawfile/scripts/app.js"></script>
</head>
<body>
    <h1>使用resource协议</h1>
    
    <!-- 加载本地图片 -->
    <img src="resource://rawfile/images/logo.png" alt="Logo">
    
    <!-- 加载本地字体 -->
    <style>
    @font-face {
        font-family: 'CustomFont';
        src: url('resource://rawfile/fonts/custom.ttf');
    }
    body {
        font-family: 'CustomFont', sans-serif;
    }
    </style>
</body>
</html>

六、总结

特性 网络页面 本地页面(rawfile) 本地页面(沙箱) HTML文本
加载速度 依赖网络 快速 快速 最快
网络需求 必需 无需 无需 无需
内容更新 实时更新 需发版更新 运行时可更新 动态生成
安全性 有风险 安全 安全 安全
适用场景 在线内容 静态资源 动态生成文件 富文本展示

常用路径写法

// 正确的路径写法
const paths = {
  // 网络路径
  network: 'https://www.example.com',
  
  // 本地rawfile路径
  localRawfile: $rawfile('page.html'),
  localResource: 'resource://rawfile/page.html',
  
  // 沙箱路径
  sandbox: 'file:///data/storage/el2/base/haps/entry/files/page.html',
  
};

Logo

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

更多推荐