在数字化时代,家庭记录和分享宝宝的成长经历变得前所未有的便捷。一款好的成长日记应用不仅能够帮助父母捕捉和保存宝宝的每一个重要瞬间,还能让他们与家人和亲朋好友共同分享这些难忘的时刻。

今天,笔者将深入探讨一款专为家庭设计的成长日记应用——小确忆——从开发理念到实现细节的全过程。本篇开发笔记将聚焦于我如何通过敏捷开发方法及使用 nutpi/axios 三方库组件来提高开发效率,确保应用不仅功能强大,还能迅速响应用户需求,提升用户体验。

背景与小目标

小确忆的应用开发始于我们的一个简单的构想:为父母提供一个安全、私密且易于操作的平台,让他们可以记录下宝宝成长中的每一个美好瞬间,无论是日记、照片还是视频。通过这款应用,家庭成员可以随时随地查看宝宝的成长历程,共同见证宝宝的点滴变化,分享成长的喜悦。我们的目标是打造一个用户友好的成长日记应用,让每一个家庭都能轻松地留住宝宝成长的每一刻。

开发方法

在开发过程中,我们采用了敏捷开发的方法。敏捷开发强调快速迭代和持续改进,能够使我们在开发周期内频繁交付功能模块,及时获取用户反馈并进行调整。这种方法帮助我们快速响应变化,确保应用能够满足用户的真实需求。

在这里插入图片描述

技术选型

为了简化网络通信部分的开发,我们选择了 nutpi/axios 作为我们的网络库。nutpi/axios 是一个最大的鸿蒙开发者组织坚果派专门为 HarmonyOS NEXT 设计的模块化封装的 axios 网络库,它的强大功能和便捷性极大地提升了我们的开发效率。通过使用 nutpi/axios,我们能够以极简的方式完成复杂的网络请求,处理认证、文件上传与下载、错误处理等场景,使得代码更加清晰、简洁。

接下来,我们将详细介绍小确忆的应用开发过程,希望这篇开发笔记能够为其他开发者提供一些有价值的参考和启发。

应用介绍

小确忆——留住宝宝成长的每一刻,是一款专为家庭设计的成长日记应用。这款应用能够让爸爸妈妈、爷爷奶奶及亲朋好友共同见证宝宝的点滴变化,记录下宝宝成长中的每一个美好瞬间,让每一天的美好都有迹可循,安全私密地存放,无论身处何地都能第一时间分享和感受宝宝成长的喜悦。小确忆,珍藏家人的小确幸,汇聚宝宝成长的无数回忆。

应用市场下载体验地址:小确忆

在这里插入图片描述

敏捷开发如何提高效率

敏捷开发是一种迭代式开发方法,通过频繁交付和持续改进,来提高项目质量和开发效率。小确忆应用在开发过程中采用了敏捷开发的方法,使得开发更加高效快捷。

使用nutpi/axios三方库组件提高开发效率

nutpi/axios 是一个专门为 HarmonyOS NEXT 设计的模块化封装的 axios 网络库,它使得网络通信变得更加简单和高效。

nutpi/axios 三方库地址:nutpi/axios

主要特性
  1. 极简使用:通过一行代码即可完成网络请求,无需进行复杂的配置。
  2. 开箱即用:提供便捷的客户端创建方法,能够快速上手。
  3. 认证支持:内置 Token 认证机制,自动处理登录状态。
  4. 文件操作:支持文件上传和下载,并且带有进度监听。
  5. 错误处理:智能错误处理,自动重试机制。
  6. 请求拦截:灵活的请求和响应拦截器。
  7. 类型安全:完整的 TypeScript 类型支持。
  8. HarmonyOS 原生:专为 HarmonyOS NEXT 优化。
  9. 纯净库设计:库文件纯净,配置完全外部化。

安装 nutpi/axios

使用 ohpm 安装 nutpi/axios 十分简单,只需在命令行中运行以下命令:

ohpm install @nutpi/axios

如何使用 nutpi/axios

为了更好地使用 nutpi/axios,我们可以在 utils 目录下单独封装一个 axiosClient.ts 文件。这样做的好处是便于对客户端的超时时间和拦截器进行配置。

封装代码
// utils/axiosClient.ts
import { AxiosHttpRequest, HttpPromise, AxiosRequestHeaders, AxiosError, AxiosHeaders, FileUploadConfig } from './axiosHttp.ets';
import { Log } from './logutil';
import { promptAction } from "@kit.ArkUI";

// 默认配置
const DEFAULT_CONFIG = {
  timeout: 10 * 1000,
  checkResultCode: true,
  showLoading: true,
  headers: new AxiosHeaders({
    'Content-Type': 'application/json'
  }) as AxiosRequestHeaders
};

// 默认拦截器配置
const DEFAULT_INTERCEPTORS = {
  requestInterceptor: async (config: any) => {
    Log.debug('Request:', `${config.method} ${config.url}`);
    Log.debug('Request Params:', JSON.stringify(config.params));
    Log.debug('Request Data:', JSON.stringify(config.data));

    // 添加认证token
    const token = AppStorage.get('Authorization');
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`;
    }

    return config;
  },
  requestInterceptorCatch: (err: any) => {
    Log.error("Request Error:", err.toString());
    return err;
  },
  responseInterceptor: (response: any) => {
    Log.debug('Response:', JSON.stringify(response.data));

    if (response.status === 200) {
      const checkResultCode = response.config.checkResultCode;
      if (checkResultCode && response.data.errorCode && response.data.errorCode !== 0) {
        Log.error('API Error:', response.data.errorMsg);
        return Promise.reject(response);
      }
      return Promise.resolve(response);
    } else {
      return Promise.reject(response);
    }
  },
  responseInterceptorCatch: (error: any) => {
    Log.error("Response Error:", error.toString());
    handleError(error);
    return Promise.reject(error);
  }
};

// 错误处理函数
function handleError(error: any) {
  if (error instanceof AxiosError) {
    Log.error('Axios Error:', error.message);
  } else if (error?.response?.status) {
    switch (error.response.status) {
      case 401:
        Log.error('请授权,请重新登录');
        break;
      case 403:
        Log.error('登录过期,请重新登录');
        break;
      case 404:
        Log.error('请求的资源不存在');
        break;
      default:
        Log.error('网络请求失败:', error.response.data?.message || error.message);
    }
  }
}

// 显示Toast提示
function showToast(msg: string) {
  Log.debug(msg);
  promptAction.showToast({ message: msg });
}

// 显示加载对话框
function showLoadingDialog(msg: string) {
  Log.debug(msg);
  promptAction.showToast({ message: msg });
}

// 隐藏加载对话框
function hideLoadingDialog() {
  // 实现隐藏加载对话框的逻辑
}

借助 nutpi/axios 库的便捷能力,小确忆应用的网络部分实现变得异常简单。例如,一个获取时光轴列表的接口可以轻松地用一行代码实现:

// 获取时光轴列表
export const getTimeLineList =
  (page: number, pageSize: number, id: number): HttpPromise<GetTimeLineListResp> => axiosClient.get({
    url: '/app/timeline/',
    params: { page: page, pageSize: pageSize, childId: id }
  });

同样地,文件上传接口也可以非常简洁地封装:

// 上传照片
export const upLoadPhoto = (config: FileUploadConfig): HttpPromise<UploadPhotoResp> => axiosClient.uploadFile(config)

export const upMultipleLoadPhoto =
  (config: FileUploadConfig): HttpPromise<UploadMultiplePhotoResp> => axiosClient.uploadFile(config)
统一的文件上传函数
export async function uploadFiles(imagePaths: string | string[]): Promise<string[]> {
  try {
    // 统一转换为数组格式
    const pathsArray = Array.isArray(imagePaths) ? imagePaths : [imagePaths];

    if (!pathsArray || pathsArray.length === 0) {
      throw new Error('请选择图片');
    }

    // 创建 UploadFile 数组
    const uploadFiles: UploadFile[] = [];

    for (let i = 0; i < pathsArray.length; i++) {
      const imagePath = pathsArray[i];
      if (!imagePath) {
        console.warn(`跳过无效的图片路径,索引: ${i}`);
        continue;
      }

      const arrayBuffer = await getImageArrayBuffer(imagePath);

      const uploadFile: UploadFile = {
        fileName: `image_${i + 1}.jpg`,
        mimeType: 'image/jpeg',
        buffer: arrayBuffer,
      };
      uploadFiles.push(uploadFile);
    }

    if (uploadFiles.length === 0) {
      throw new Error('没有有效的图片文件');
    }

    const uri = '/app/upload/image';
    const config: FileUploadConfig = axiosClient.createUploadConfig(
      uri,
      uploadFiles.length === 1 ? uploadFiles[0] : uploadFiles, // 单个文件传对象,多个文件传数组
      {
        context: AppUtil.getContext(),
        onUploadProgress: (progressEvent: AxiosProgressEvent) => {
          const percentCompleted = Math.round((progressEvent.loaded * 100) / (progressEvent?.total ?? 1));
          const uploadType = uploadFiles.length === 1 ? '上传' : '批量上传';
          console.log(`${uploadType}进度: ${percentCompleted}%`);
        }
      }
    );

    // 根据文件数量选择不同的上传接口
    if (uploadFiles.length === 1) {
      // 单个文件上传
      const res = await upLoadPhoto(config);
      console.log('单个文件上传成功:', res.data.data.url);
      return [res.data.data.url];
    } else {
      // 批量文件上传
      const res = await upMultipleLoadPhoto(config);
      console.log('批量上传成功:', JSON.stringify(res.data.data));

      // 处理后端返回的数据格式
      const urls: string[] = res.data.data.map((item: UploadPhotoItem) => item.url);
      console.log('提取的URL数组:', urls);
      return urls;
    }
  } catch (error) {
    console.error('文件上传失败:', error);
    return ['文件上传失败'];
  }
}

通过使用 nutpi/axios,小确忆 应用的网络请求部分变得高效简洁,极大提升了开发效率。

结论

通过采用敏捷开发方法和使用 nutpi/axios 三方库,小确忆 应用不仅实现了高效开发,还能够快速响应用户需求,极大地提高了用户体验。希望这篇笔记能够为其他开发者提供一些启发和帮助。

Logo

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

更多推荐