uni-app开发微信小程序项目开发说明(vue3+ts)

happy young...大约 6 分钟技术vuets小程序

项目构建及工程化说明

  1. 初始化uni-app项目,vue3+ts:
npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project 

(如命令行创建失败,请直接访问 giteeopen in new window下载模板)

  1. 添加.gitignore / .editorconfig 文件配置 (略)

  2. 引入eslint及prettier规范项目代码 (略)

  3. 引入微信小程序相关ts类定义 npm install @types/wechat-miniprogram

  4. 自动化UT测试搭建 参考cli项目自动化测试open in new windowvitestopen in new window使用说明。

    a. 当前的测试方案使用的Vitest(vue3与vite的生态配合较好)。项目自动化测试仅支持单元测试。对于组件测试,目前uni-app并无文档支持,在使用@vue/test-utils尝试组件测试时,依赖的@dcloudio包会报错,怀疑是uni-app的编译过程对vue的运行环境进行了包装。 对于E2E测试,需要配合HbuilderX或微信开发者工具,无法在流水线上做自动化测试,暂未进一步研究。

    b. 实际项目中对src下的所有ts文件中的业务函数进行测试用例覆盖,vue3引入了组合式函数,可以将绝大部分业务逻辑的处理提取到vue文件之外复用。数据及业务逻辑基本可以被UT测试覆盖到。网络请求和依赖的环境api,通过addTestingRecord/mockWxApi的方式构造场景,完成用例验证。

    c. 基于第2点,vue文件里的ts代码,仅与视图及交互相关。核心的业务代码逻辑,请以组合式函数的方式提取到service层。

  5. 持续集成的基本指令 参考uni-app持续集成open in new window 当前使用的vue-cli方式打包。 项目本身支持使用HBuilderX cli打包及发行。但无法在流水线上使用。其依赖HBuilderX软件的安装,因此使用npm指令打包归档。然后通过。

    # 编译
    npm i
    npm run test:vite
    npm run build:mp-weixin
    packageName=${WORKSPACE_SERVICENAME}_$versionNumber
    # 打包
    cd dist/build/mp-weixin/
    zip -r  $packageName.zip ./*
    
  6. 微信小程序上传发布 当前由开发人员,在本地通过IDE上传。该操作需要外网访问权限,由开发人员做信息安全备案后操作,微信小程序的上架,还需微信官方审核及运营人员做发布操作。 小程序协同工作和发布open in new window

使用vue-cli命令初始化项目的考虑:

  1. 使用HBuilderX开发工具初始化的代码,无法使用其他ide(vscode)开发uni-app。
  2. HbuilderX的开发体验相较Vscode较差, Vscode的开发使用范围更广,相关插件及工程开发的支持更好。
  3. 使用vue-cli初始化的代码,仍然可以通过HbuildX进行开发。
  4. 对于小程序及H5的开发场景,不依赖其内置的app及uniCloud相关的运行调试工具。

新人上手

  1. 开发工具安装:
  1. 微信开发者工具配置:
  • 扫码登录微信开发者工具, 需要有外网权限。 spes国内接入站点貌似无法联通,可尝试在海外接入站点切换。
  • 点击“设置 =》 安全设置”,打开服务端口。 点击“设置 =》 代理设置”,选择使用系统代理。
  • 点击“视图 =》 调试器”,打开调试器, 点击调试器视图上的 “Mock”,启用Mock。 (后续开发可mock部分请求,mock数据在项目的/mock目录下)
  1. HbuilderX开发启动:
  • HbuilderX ide内导入项目目录
  • 点击“运行 =》 运行到小程序模拟器 =》 微信开发者工具”
  • 修改项目文件,保存后自动启动差量编译,并在微信开发者工具上预览实时效果。
  1. vscode开发启动:
  • vscode ide内导入项目目录
  • 执行指令 npm install
  • 执行指令 npm run dev:mp-weixin
  • 进入/dist/dev/mp-weixin目录,通过微信开发者工具导入该编译后的项目目录
  • 修改项目文件,保存后自动启动差量编译,并在微信开发者工具上预览实时效果。

开发说明

  1. 项目基于vue 3开发。相关语法特性参见 uni-app教程open in new window; vue3 官网open in new window; vue3中,组合式api对typescript有更好的支持。

  2. 该项目当前使用uni-app模式进行开发,但仅涉及微信小程序形态。因此,项目中的http请求、登录、支付都使用的wx自有api。uni-app也内置了类型支持。后期如果需要基于此项目支持其他平台的开发,再在api/service层做适配。

  3. vue3 + 组合式api + ts的开发生态下,vscode里有新的插件以支持更友好的开发, 请卸载Vetur插件以安装Volar

  4. 环境区分及联调:可以根据wx.getAccountInfoSync().miniProgram.envVersion区分微信小程序是开发版还是体验版还是发布版。以使用不同的环境配置

项目版本节奏说明

角色权限申请:

  • 开发人员—— 申请 “开发者”角色
  • 运营人员—— 申请 “运营者”角色
  • 测试人员—— 申请 “体验人员”角色 (企业主体微信最多添加50名体验人员)

版本节点对应:

  • 开发阶段——使用IDE本地开发,上传开发版本现网验证,开发版本仅开发人员可访问
  • 版本转测——选择开发版本转为体验版本 (开发人员触发)
  • 测试阶段——体验版本体验人员可访问,对接微信现网环境
  • 众测发布——选择开发版本转为体验版本 (开发人员触发)
  • 灰度发布——选择体验版本提交审核,由微信平台审核(运营人员触发)
  • 灰度活动——提交分阶段发布(运营人员触发)

关键控制点: 灰度发布 和 灰度活动。 需要有项目组发布评审会的评估确认邮件,才能触发运营人员提交发布。

附 mock代码:

// mockHttpData.ts
type ResponseFun = (request?: any, header?: Map<string, string>) => HttpDataType.ApiResType;

interface MockRecord {
  url: string;
  method?: string;
  response: HttpDataType.ApiResType | ResponseFun;
}

const baseMockData = [
  {
    url: 'urlpath',
    response() {
      return {
        resultDesc: 'success',
        resultCode: '000000',
      } as HttpDataType.ApiResType;
    },
  }
] as Array<MockRecord>;

let testMockData = [] as Array<MockRecord>;

function existAndMatch(method1: any, method2: any) {
  return !(method1 != null && method2 != null && method1 != method2);
}

export function resetTestingData() {
  testMockData = [];
}
export function addTestingRecord(record: MockRecord) {
  testMockData.unshift(record);
}

export function mockRequest(url: string, req: any, method?: string, header?: Map<string, string>): HttpDataType.ApiResType | null {
  let apiData = null;
  const mockData = [...testMockData, ...baseMockData];
  for (const mockRecord of mockData) {
    if (url.includes(mockRecord.url) && existAndMatch(method, mockRecord.method)) {
      apiData = typeof mockRecord.response === 'function' ? mockRecord.response(req, header) : mockRecord.response;
      break;
    }
  }
  return apiData;
}
// mockApiData.ts
import { vi } from 'vitest';
import { mockRequest } from './mockHttpData';

type Record<K extends keyof any, T> = {
  [P in K]: T;
};

const wx = {
  getAccountInfoSync: () => {
    return {
      miniProgram: {
        envVersion: 'develop',
      },
    } as WechatMiniprogram.AccountInfo;
  },
  login: async () => {
    return {
      code: new Date().getTime(),
    };
  },
  request: async (requestOption: WechatMiniprogram.RequestOption<WechatMiniprogram.IAnyObject>,
  ) => {
    const apiData = mockRequest(requestOption.url, requestOption.data, requestOption.method, requestOption.header as Map<string, string>);
    if (apiData == null) {
      const failData = { errno: -1, errMsg: 'this api( ' + requestOption.url + " ) doesn't mock, pls add mock data" };
      console.error(failData);
      requestOption.fail && requestOption.fail(failData);
    }
    const responseData = {
      data: apiData,
      statusCode: 200,
      header: {
        'content-type': 'application/json; charset=utf-8',
      },
    } as WechatMiniprogram.RequestSuccessCallbackResult<any>;
    requestOption.success && requestOption.success(responseData);
  },
} as Record<string, Function>;
vi.stubGlobal('wx', wx);

let changedApiRecords = new Map<string, Function>();

export function mockWxApi(methodName: string, apiFun: Function) {
  if (!changedApiRecords.has(methodName)) {
    changedApiRecords.set(methodName, wx[methodName]);
  }
  Object.defineProperty(wx, methodName, { value: apiFun });
  vi.stubGlobal('wx', wx);
}
export function restMockWxApi() {
  changedApiRecords.forEach((apiFun, methodName) => {
    Object.defineProperty(wx, methodName, { value: apiFun });
  });
  changedApiRecords = new Map<string, Function>();
  vi.stubGlobal('wx', wx);
}
上次编辑于:
贡献者: Yangxi
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.14.6