Class 模型 + 跨组件状态(@Observed)+ 网络请求封装 + 本地存储全部是鸿蒙 Next/Stage 模型标准写法


0. 整体结构

model/
  ├─ User.ets          # Class 模型
  ├─ UserService.ets   # 网络请求封装
  ├─ UserStorage.ets   # 本地存储封装
pages/
  ├─ Index.ets         # 主页面
  ├─ UserCard.ets      # 子组件(跨组件状态演示)

1. model/User.ets

Class + Interface + 可观察对象(跨组件刷新)

// 接口约束
export interface IUser {
  id: number;
  name: string;
  age: number;
  vipLevel: number;
}

// 跨组件状态必须加 @Observed
@Observed
export class User implements IUser {
  id: number;
  name: string;
  age: number;
  vipLevel: number;

  constructor(id: number, name: string, age: number, vipLevel: number) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.vipLevel = vipLevel;
  }

  // 业务方法
  isAdult(): boolean {
    return this.age >= 18;
  }

  isVip(): boolean {
    return this.vipLevel > 0;
  }
}

2. model/UserService.ets

Class 封装网络请求

import http from '@ohos.net.http';
import { User } from './User';

export class UserService {
  // 模拟获取用户列表
  static async getUserList(): Promise<User[]> {
    return new Promise((resolve) => {
      // 真实项目替换为接口地址
      setTimeout(() => {
        const data = [
          new User(1, "张三", 20, 2),
          new User(2, "李四", 16, 0),
          new User(3, "王五", 25, 1)
        ];
        resolve(data);
      }, 500);
    });
  }

  // 真实网络请求示例
  static async fetchUserFromNet(): Promise<User> {
    let httpRequest = http.createHttp();
    let resp = await httpRequest.request(
      "https://mock-api.example.com/user",
      { method: http.RequestMethod.GET }
    );

    let json = JSON.parse(resp.result.toString());
    return new User(json.id, json.name, json.age, json.vipLevel);
  }
}

3. model/UserStorage.ets

Class 封装本地存储(偏好设置)

import dataPreferences from '@ohos.data.preferences';
import { User } from './User';

const PREF_NAME = 'user_pref';

export class UserStorage {
  // 保存用户
  static async saveUser(user: User): Promise<void> {
    let pref = await dataPreferences.getPreferences(this.PREF_NAME);
    await pref.put('user', JSON.stringify(user));
    await pref.flush();
  }

  // 读取用户
  static async loadUser(): Promise<User | null> {
    try {
      let pref = await dataPreferences.getPreferences(this.PREF_NAME);
      let str = await pref.get('user', '');
      if (!str) return null;
      let obj = JSON.parse(str);
      return new User(obj.id, obj.name, obj.age, obj.vipLevel);
    } catch (e) {
      return null;
    }
  }

  // 清除
  static async clearUser(): Promise<void> {
    let pref = await dataPreferences.getPreferences(this.PREF_NAME);
    await pref.clear();
  }
}

4. pages/UserCard.ets

子组件 + @ObjectLink 跨组件状态同步

import { User } from '../model/User';

@Component
struct UserCard {
  // 跨组件共享对象,必须用 @ObjectLink
  @ObjectLink user: User;

  build() {
    Column() {
      Text(`姓名:${this.user.name}`)
      Text(`年龄:${this.user.age}`)
      Text(`VIP:${this.user.vipLevel}`)
        .fontColor(this.user.isVip() ? Color.Green : Color.Grey)

      Button("年龄 +1")
        .onClick(() => {
          // 修改后,主页面也会同步刷新!
          this.user.age += 1;
        })
    }
    .width('100%')
    .backgroundColor(0xf8f8f8)
    .padding(12)
    .borderRadius(10);
  }
}

export default UserCard;

5. pages/Index.ets

主页面整合所有功能

import { User } from '../model/User';
import { UserService } from '../model/UserService';
import { UserStorage } from '../model/UserStorage';
import UserCard from './UserCard';

@Entry
@Component
struct Index {
  // 可观察对象,支持跨组件刷新
  @State currentUser: User = new User(0, "加载中...", 0, 0);
  @State userList: User[] = [];

  async aboutToAppear() {
    // 1. 从本地加载
    let localUser = await UserStorage.loadUser();
    if (localUser) {
      this.currentUser = localUser;
    }

    // 2. 从网络加载列表
    let list = await UserService.getUserList();
    this.userList = list;
  }

  build() {
    Column({ space: 12 }) {
      Text("三合一实战")
        .fontSize(26)
        .fontWeight(FontWeight.Bold);

      // 跨组件子组件
      UserCard({ user: this.currentUser });

      // 保存到本地
      Button("保存当前用户到本地")
        .onClick(async () => {
          await UserStorage.saveUser(this.currentUser);
        });

      Divider().margin(10);

      // 网络请求列表
      List({ space: 8 }) {
        ForEach(this.userList, (user) => {
          ListItem() {
            Column() {
              Text(user.name)
              Text(`年龄:${user.age}`)
              Text(`成年:${user.isAdult()}`)
                .fontColor(user.isAdult() ? Color.Green : Color.Red)
            }
            .width('100%')
            .backgroundColor(0xf2f2f2)
            .padding(10)
            .borderRadius(8);
          }
        })
      }
    }
    .padding(20)
    .width('100%')
  }
}

6. 这三个功能分别对应什么?

① Class + @Observed + @ObjectLink

  • 实现跨组件共享状态
  • 子组件改数据 → 主页面自动刷新
  • 鸿蒙官方推荐的组件通信方案

② Class 封装网络请求

  • 把接口逻辑统一放 Service
  • 结构清晰、便于维护、统一处理异常

③ Class 封装本地存储

  • 偏好设置、用户信息、配置都存在这
  • 重启 App 依然存在
功能 作用
AppStorage 鸿蒙全局状态池,所有组件可读写,跨页面 / 跨组件共享
PersistentStorage 把 AppStorage 中的指定数据持久化到本地,APP 重启后数据不丢
@Observed + @ObjectLink 组件间对象级状态同步
Class 模型 封装数据和业务逻辑,结合状态管理实现响应式

完整代码(新增 / 修改部分标红)

1. 新增:model/GlobalUserModel.ets(全局状态 + 持久化)

import { AppStorage, PersistentStorage } from '@ohos.data.storage';
import { User } from './User';

// 1. 初始化全局默认用户
const defaultGlobalUser = new User(999, "全局默认用户", 18, 3);

// 2. 将全局用户注册到 AppStorage(全局状态池)
AppStorage.setOrCreate('globalUser', defaultGlobalUser);

// 3. 将 globalUser 持久化到本地(APP重启不丢)
PersistentStorage.persistProp('globalUser', defaultGlobalUser);

// 4. 封装全局用户的读写方法(方便调用)
export class GlobalUserModel {
  // 获取全局用户
  static getGlobalUser(): User {
    return AppStorage.get('globalUser') as User;
  }

  // 更新全局用户
  static updateGlobalUser(user: User): void {
    AppStorage.set('globalUser', user);
  }

  // 修改全局用户属性(响应式)
  static updateGlobalUserProp(key: keyof User, value: any): void {
    const user = this.getGlobalUser();
    user[key] = value;
    AppStorage.set('globalUser', user); // 触发全局刷新
  }

  // 清空全局用户
  static clearGlobalUser(): void {
    AppStorage.set('globalUser', defaultGlobalUser);
  }
}

2. 修改:model/User.ets(支持持久化序列化)

// 接口约束
export interface IUser {
  id: number;
  name: string;
  age: number;
  vipLevel: number;
}

// 跨组件状态必须加 @Observed
@Observed
export class User implements IUser {
  id: number;
  name: string;
  age: number;
  vipLevel: number;

  constructor(id: number, name: string, age: number, vipLevel: number) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.vipLevel = vipLevel;
  }

  // 业务方法
  isAdult(): boolean {
    return this.age >= 18;
  }

  isVip(): boolean {
    return this.vipLevel > 0;
  }

  // 🔴 新增:序列化方法(持久化必备)
  toJSON() {
    return {
      id: this.id,
      name: this.name,
      age: this.age,
      vipLevel: this.vipLevel
    };
  }

  // 🔴 新增:反序列化方法(从本地/全局读取后还原为Class实例)
  static fromJSON(json: any): User {
    return new User(json.id, json.name, json.age, json.vipLevel);
  }
}

3. 新增:pages/GlobalUserPage.ets(全局状态演示页面)

import { GlobalUserModel } from '../model/GlobalUserModel';
import { User } from '../model/User';
import { LocalStorage } from '@ohos.data.storage';

@Entry
@Component
struct GlobalUserPage {
  // 🔴 从AppStorage订阅全局用户(响应式)
  @Link globalUser: User = GlobalUserModel.getGlobalUser();

  build() {
    Column({ space: 12 }) {
      Text("全局状态 + 持久化演示")
        .fontSize(26)
        .fontWeight(FontWeight.Bold);

      // 展示全局用户信息
      Text(`全局用户:${this.globalUser.name}`)
      Text(`全局用户年龄:${this.globalUser.age}`)
      Text(`VIP等级:${this.globalUser.vipLevel}`)
        .fontColor(this.globalUser.isVip() ? Color.Gold : Color.Grey);

      // 修改全局用户属性(所有页面同步刷新)
      Button("全局用户年龄+1")
        .onClick(() => {
          GlobalUserModel.updateGlobalUserProp('age', this.globalUser.age + 1);
        });

      // 修改全局用户名称
      Button("修改全局用户名称为'鸿蒙全局用户'")
        .onClick(() => {
          GlobalUserModel.updateGlobalUserProp('name', '鸿蒙全局用户');
        });

      // 重置全局用户
      Button("重置全局用户")
        .backgroundColor(Color.Red)
        .onClick(() => {
          GlobalUserModel.clearGlobalUser();
        });

      // 跳回主页面(验证跨页面同步)
      Navigator({ target: 'pages/Index' }) {
        Text("返回主页面查看全局状态同步")
          .fontSize(16)
          .fontColor(Color.Blue);
      }
    }
    .padding(20)
    .width('100%')
  }
}

4. 修改:pages/Index.ets(集成全局状态 + 持久化)

import { User } from '../model/User';
import { UserService } from '../model/UserService';
import { UserStorage } from '../model/UserStorage';
import UserCard from './UserCard';
import { GlobalUserModel } from '../model/GlobalUserModel'; // 🔴 新增

@Entry
@Component
struct Index {
  // 可观察对象,支持跨组件刷新
  @State currentUser: User = new User(0, "加载中...", 0, 0);
  @State userList: User[] = [];
  // 🔴 订阅全局用户(和GlobalUserPage同步)
  @Link globalUser: User = GlobalUserModel.getGlobalUser();

  async aboutToAppear() {
    // 1. 从本地加载
    let localUser = await UserStorage.loadUser();
    if (localUser) {
      this.currentUser = localUser;
    }

    // 2. 从网络加载列表
    let list = await UserService.getUserList();
    this.userList = list;
  }

  build() {
    Column({ space: 12 }) {
      Text("三合一+全局状态+持久化实战")
        .fontSize(26)
        .fontWeight(FontWeight.Bold);

      // 🔴 新增:展示全局用户信息(和GlobalUserPage同步)
      Text(`【全局共享】${this.globalUser.name} (年龄:${this.globalUser.age})`)
        .fontSize(18)
        .backgroundColor(0xe0f7fa)
        .padding(8)
        .borderRadius(6);

      // 跳转到全局用户页面
      Navigator({ target: 'pages/GlobalUserPage' }) {
        Text("进入全局用户页面")
          .fontSize(16)
          .fontColor(Color.Blue);
      }

      Divider().margin(10);

      // 跨组件子组件
      UserCard({ user: this.currentUser });

      // 保存到本地
      Button("保存当前用户到本地")
        .onClick(async () => {
          await UserStorage.saveUser(this.currentUser);
        });

      Divider().margin(10);

      // 网络请求列表
      List({ space: 8 }) {
        ForEach(this.userList, (user) => {
          ListItem() {
            Column() {
              Text(user.name)
              Text(`年龄:${user.age}`)
              Text(`成年:${user.isAdult()}`)
                .fontColor(user.isAdult() ? Color.Green : Color.Red)
            }
            .width('100%')
            .backgroundColor(0xf2f2f2)
            .padding(10)
            .borderRadius(8);
          }
        })
      }
    }
    .padding(20)
    .width('100%')
  }
}

5. 补充:修改 model/UserStorage.ets(适配 Class 反序列化)

import dataPreferences from '@ohos.data.preferences';
import { User } from './User';

const PREF_NAME = 'user_pref';

export class UserStorage {
  // 保存用户
  static async saveUser(user: User): Promise<void> {
    let pref = await dataPreferences.getPreferences(PREF_NAME);
    await pref.put('user', JSON.stringify(user)); // 用toJSON序列化
    await pref.flush();
  }

  // 读取用户(🔴 新增反序列化)
  static async loadUser(): Promise<User | null> {
    try {
      let pref = await dataPreferences.getPreferences(PREF_NAME);
      let str = await pref.get('user', '');
      if (!str) return null;
      let obj = JSON.parse(str);
      return User.fromJSON(obj); // 还原为Class实例
    } catch (e) {
      return null;
    }
  }

  // 清除
  static async clearUser(): Promise<void> {
    let pref = await dataPreferences.getPreferences(PREF_NAME);
    await pref.clear();
  }
}

关键功能演示步骤

  1. 全局状态同步
    • 打开 Index 页面,看到 “全局用户:全局默认用户(年龄:18)”
    • 点击 “进入全局用户页面”,点击 “全局用户年龄 + 1”,返回 Index 页面 → 年龄变为 19(跨页面同步)
  2. 持久化验证
    • 在全局用户页面修改名称为 “鸿蒙全局用户”,关闭 APP 重启 → 名称依然是 “鸿蒙全局用户”(PersistentStorage 生效)
  3. 本地存储 + Class 反序列化
    • 保存 currentUser 到本地,重启 APP → 加载的是本地保存的用户(不是默认值)

核心知识点总结

1. AppStorage + @Link 全局状态

  • AppStorage 是鸿蒙的全局状态池,相当于 “内存级全局变量”
  • @Link 用于组件订阅 AppStorage 中的数据,数据变化时组件自动刷新
  • 封装成GlobalUserModel类,统一管理全局用户的读写,符合 “单一职责” 原则

2. PersistentStorage 持久化

  • 把 AppStorage 中的指定 key(如globalUser)持久化到本地
  • Class 必须实现toJSON/fromJSON方法,否则持久化后会丢失 Class 的方法(变成普通对象)

3. 完整的状态管理链路

Class模型(User)→ 本地存储(UserStorage)→ 全局状态(GlobalUserModel)→ 组件(@Link/@ObjectLink)→ UI自动刷新

最后:鸿蒙状态管理最佳实践

  1. 局部组件内状态 → 用@State
  2. 父子组件间状态 → 用@Observed + @ObjectLink
  3. 跨页面 / 全局状态 → 用AppStorage + @Link + PersistentStorage
  4. 所有数据模型优先用 Class 封装,不要用普通对象(可封装业务方法,易维护)
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐