《应用导航设计:裂变式路由风暴来袭》——HarmonyOS开发项目时的Navigation路由奇妙使用

文章目录

  • 应用导航设计
    • 引言
    • 概述
    • 场景示例
    • 基本实现
    • 推荐方案
      • 路由管理模块的实现
      • 页面跳转实现
    • 业务实现中的关键点
      • 动态加载
      • 路由栈管理

应用导航设计

引言

在大型应用开发中,如何高效地设计应用导航,处理多模块间的路由跳转与解耦,始终是一个关键挑战。随着应用规模的扩大,业务模块增多,各模块间的 UI 组件相互跳转需求频繁出现,传统的导航设计往往导致模块间相互依赖、耦合严重,影响开发效率、可维护性以及应用的启动速度。本文将深入探讨应用导航设计的优化方案,从华为官方提供的基于 Navigation 的路由设计基础出发,剖析其存在的问题,并详细阐述推荐的路由功能抽取与模块解耦方案,帮助开发者更好地应对大型应用导航设计中的复杂问题,提升应用开发的整体质量与效率。

概述

大型应用开发中,应用可能包含不同的业务模块,每个模块由不同的业务团队负责开发。该场景采用一个Navigation下多个har/hsp的架构,其中一个模块对应一个har/hsp。当多个har/hsp的UI组件存在相互跳转的业务需求时,将出现模块间相互依赖的问题。如“A.har”、“B.har”和“C.har”模块拥有不同的组件,各组件间的路由跳转形成了一个环形链路,导致三个har模块相互耦合,如图所示:

img

针对该场景,华为官方也提供了一套基于Navigation的路由设计的方案实现多模块路由管理和模块间解耦。并在该基础上,通过动态注册路由的方式,解决页面加载多个UI组件时启动速度变慢问题。Navigation组件的使用方法可以见

在哪里?在哪里!设置到底在哪里!!!HarmonyOS移动应用开发——设置组件导航(Navigation组件、Tabs组件实现页面导航)

场景示例

假定工程包含harA和harB两个业务模块,harA模块打包编译为A.har,harB模块打包编译为B.har。在实际业务中,harA模块中的A1组件需要跳转到harB模块中的B1组件,项目关系如下图所示。例如登录场景,登录界面和登录后的主要页面是两个独立的模块,用户在登录结束之后,就可以直接进入首页开始使用app相关内容。

img

基本实现

假设我们现在已经拥有了har.A:default和A1:MaiPage,以及har.B:homepage和B1:HomePageView,和har.C:mine和C1:MineView

具体实现步骤如下

  1. 在defaule模块中的MaiPage页面组件中开发Navigation组件,并关联与之对应的NavPathStack路由栈,示例代码如下:

    @Component
    struct MaiPage {// 创建NavPathStack路由栈对象@Provide('MainPagePathStack') MainPagePathStack: NavPathStack = new NavPathStack();build() {// Navigation组件关联NavPathStack对象Navigation(this.MainPagePathStack) {// ...}}
    }
    
  2. 在defaule模块的oh-package.json5文件中添加Mine模块和homepage模块的依赖,示例代码如下:

    "dependencies": {// 添加依赖"homepage": "file:../../features/homepage","mine": "file:../../features/mine"
    }
    
  3. 在defaulet模块的mainpage中借助Tabs组件实现页面的跳转的逻辑,完整实例代码如下:

    //default模块的mainpage组件
    import { HomePageView } from 'homepage'
    import { MineView } from 'mine';
    import { TabBarType } from '../model/TabBarModel';
    import { CustomTabBar } from '../components/CustomTabBar';@Entry
    @Component
    struct MainPage {// 创建NavPathStack路由栈@Provide('MainPagePathStack') MainPagePathStack: NavPathStack = new NavPathStack();@Provide('minePathStack') minePathStack: NavPathStack = new NavPathStack();@Provide('homePagePathStack') homePagePathStack: NavPathStack = new NavPathStack();@State currentIndex: TabBarType = TabBarType.MAINPAGEbuild() {// Navigation关联NavPathStack对象Navigation(this.MainPagePathStack) {Flex({ direction: FlexDirection.Column }) {Tabs({ index: this.currentIndex }) {TabContent() {//通过Tabs组件跳转页面,同时在该页面也应该注册HomePageView()}TabContent() {MineView()}}.layoutWeight(1).barHeight(0).scrollable(false).onChange((index) => {this.currentIndex = index;})CustomTabBar({ currentIndex: $currentIndex })}.width('100%')}.hideTitleBar(true).mode(NavigationMode.Stack)}
    }
    
    //homepage模块的HomePageView组件
    @Component
    export struct HomePageView {
    //接收相关路由@Consume('homePagePathStack') homePagePathStack: NavPathStack;build() {Navigation(this.homePagePathStack) {Row() {Text('这里是首页')}.width('100%').height('100%')}}
    }
    
    //mine模块的MineView组件
    import { UserInfoView } from "./UserInfoView";
    import { MineHeaderView } from "./MineHeaderView";@Component
    export struct MineView {
    //接收相关路由@Consume('minePathStack') minePathStack: NavPathStack;build() {Navigation(this.minePathStack) {Row() {Column() {MineHeaderView().margin({ top: 10 })UserInfoView().margin({ top: 20 })}.justifyContent(FlexAlign.Start).alignItems(HorizontalAlign.Center).width('100%')}.alignItems(VerticalAlign.Top).width('100%').height('100%')}}
    }
    

当前方案主要存在以下问题:

  • 模块无法独立编译且存在开发态模块间循环依赖问题。
  • 子模块的页面跳转也可能出现过于耦合的情况。
  • 因为使用了Tabs组件,所以跳转的组件是平行跳转的,而在真正的项目开发中,在TabContent中也一定会再次跳转。

所以官方还给出了routerMap路由表的方式:

  1. 在harA模块中的A1页面组件中开发Navigation组件,并关联与之对应的NavPathStack路由栈,示例代码如下:

    @Component
    struct A1 {// 创建NavPathStack路由栈对象@State harARouter: NavPathStack = new NavPathStack();build() {// Navigation组件关联NavPathStack对象Navigation(this.harARouter) {// ...}}
    }
    
  2. 在harA模块的oh-package.json5文件中添加harB模块的依赖,并且把harB模块中需要跳转的B1组件添加到harA模块的Navigation组件路由表中,示例代码如下

    "dependencies": {// 添加对harB的依赖"@ohos/harb": "file:../harB"
    }

    在harA模块的A1组件中的routerMap路由表中,添加harB模块的B1组件,示例代码如下:

    import { B1 } from '@ohos/harb';
    struct A1 {@State harARouter: NavPathStack = new NavPathStack();@BuilderrouterMap(builderName: string, param: object) {if (builderName === 'B1') {B1() // 在routerMap中添加需要跳转的harB模块的B1页面}}build() {Navigation(this.harARouter) {// ...}.navDestination(this.routerMap) // Navigation关联上routerMap路由表}
    }
    
  3. 在harA模块的Navigation组件中添加跳转到harB模块的B1页面的逻辑,完整示例代码如下:

    import { B1 } from '@ohos/harb';struct A1 {// 创建NavPathStack路由栈@State harARouter: NavPathStack = new NavPathStack();@BuilderrouterMap(builderName: string, param: object) {if (builderName === 'B1') {B1() // 在routerMap中添加需要跳转的harB模块的B1页面}}build() {// Navigation关联NavPathStack对象Navigation(this.harARouter) {Button('跳转到HarB的B1页面').onClick(() => {// 跳转到已在路由表注册的harB模块的B1页面this.harARouter.pushPathByName('B1', null);})}.navDestination(this.routerMap) // Navigation关联上routerMap路由表}
    }
    

当前基本方案主要存在以下问题:

  • 使用Navigation时,所有路由页面需要主动通过import方式逐个导入当前页面,并存入页面路由表routerMap中。
  • 主动使用import的方式需显性指定加载路径,造成开发态模块耦合严重。
  • 模块无法独立编译,且存在开发态模块间循环依赖问题。

为解决上述问题,推荐如下方案。

推荐方案

将路由功能抽取成单独的模块并以har包形式存在,命名为RouterModule。RouterModule内部对路由进行管理,对外暴露RouterModule对象供其他模块使用。由于Entry.hap是应用必备的主入口,利用该特性考虑将主入口模块作为其他业务模块的依赖注册中心,在入口模块中使用Navigation组件并依赖其他业务模块。业务模块仅依赖RouterModule,业务模块中的路由统一委托到RouterModule中管理,实现业务模块间的解耦。按照推荐方案,上述场景各模块依赖关系如下:

img

此方案中,各模块的依赖关系如下:

  • Entry.hap、A.har和B.har均依赖了RouterModule.har;
  • Entry.hap在工程配置中依赖了A.har和B.har;
  • 对于业务开发团队之间,A.har在工程和源码上无需依赖B.har的库,实现了业务模块间的解耦。

路由管理模块的实现

RouterModule模块包含全局的路由栈和路由表信息。路由栈是NavPathStack对象,该对象与Entry.hap的Navigation组件绑定,RouterModule通过持有NavPathStack管理Navigation组件的路由信息。路由表builderMap是Map结构,以key-vaule的形式存储了需要路由的页面组件信息,其中key是自定义的唯一路由名,value是WrappedBuilder对象,该对象包裹了路由名对应的页面组件。RouterModule模块结构如下:

img

RouterModule模块的实现主要包含以下步骤:

  1. 定义路由表和路由栈。
export class RouterModule {// WrappedBuilder支持@Builder描述的组件以参数的形式进行封装存储static builderMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();// 初始化路由栈,需要关联Navigation组件static routerMap: Map<string, NavPathStack> = new Map<string, NavPathStack>();// ...
}
  1. 路由表增加路由注册和路由获取方法,业务har模块通过路由注册方法将需要路由的页面组件委托给RouterModule管理。
// 通过名称注册路由栈
public static registerBuilder(builderName: string, builder: WrappedBuilder<[object]>): void {RouterModule.builderMap.set(builderName, builder);
}// 获取路由表中指定的页面组件
public static getBuilder(builderName: string): WrappedBuilder<[object]> {const builder = RouterModule.builderMap.get(builderName);if (!builder) {Logger.info('not found builder ' + builderName);}return builder as WrappedBuilder<[object]>;
}

​ 3.路由表增加路由跳转方法,业务har模块通过调用该方法并指定跳转信息实现模块间路由跳转。

public static async push(router: RouterModel): Promise<void> {const harName = router.builderName.split('_')[0];await import(harName).then((ns: ESObject): Promise<void> => ns.harInit(router.builderName));RouterModule.getRouter(router.routerName).pushPath({ name: router.builderName, param: router.param });
}

页面跳转实现

路由管理模块RouterModule实现之后,需要使用RouterModule模块实现业务模块harA的页面跳转到业务模块harB的页面功能。主要步骤如下:

  1. 在工程主入口模块Entry.hap中引入RouterModule模块和所有需要进行路由注册的业务har模块。

    "dependencies": {"@ohos/routermodule": "file:../RouterModule","@ohos/hara": "file:../harA","@ohos/harb": "file:../harB","@ohos/harc": "file:../harC"
    }
    
  2. 在工程主入口模块Entry.hap中配置build-profile.json5文件,在该文件中修改packages字段,将需要进行路由注册的业务har模块写入配置。

    {// ..."buildOption": {"arkOptions": {"runtimeOnly": {"sources": [],"packages": ["@ohos/hara","@ohos/harb","@ohos/harc"]}}},
    }
    
  3. 在工程主入口模块的首页Navigation组件上关联RouterModule模块的路由栈和路由表。其中RouterModule.createRouter()与RouterModule.getBuilder()方法的实现。

    @Entry
    @Component
    struct EntryHap {@State entryHapRouter: NavPathStack = new NavPathStack();aboutToAppear() {if (!this.entryHapRouter) {this.entryHapRouter = new NavPathStack();}RouterModule.createRouter(RouterNameConstants.ENTRY_HAP, this.entryHapRouter);};@BuilderrouterMap(builderName: string, param: object) {// Obtain the WrappedBuilder object based on the module name, create a page through the builder interface, and import the param parameter.RouterModule.getBuilder(builderName).builder(param);};build() {Navigation(this.entryHapRouter) {// ...}.title('NavIndex').navDestination(this.routerMap);}
    }
    
  4. 在harB中声明需要跳转的页面,并且调用registerBuilder接口将页面注册到RouterModule模块的全局路由表上。以下注册逻辑会在harB的B1页面被首次加载时触发。

    // harB模块的B1页面
    @Builder
    export function harBuilder(value: object) {NavDestination() {Column() {// ...}// ...}// ...
    }// 在页面首次加载时触发执行
    const builderName = BuilderNameConstants.HARB_B1;
    // 判断表中是否已存在路由信息,避免重复注册
    if (!RouterModule.getBuilder(builderName)) {// 通过系统提供的wrapBuilder接口封装@Builder装饰的方法,生成harB1页面builderlet builder: WrappedBuilder<[object]> = wrapBuilder(harBuilder);// 注册harB1页面到全局路由表RouterModule.registerBuilder(builderName, builder);
    }
    
  5. 在harA模块中的A1页面调用RouterModule模块的push方法实现跳转到harB的B1页面。当harB的B1页面被首次通过push方法跳转时,会动态加载B1页面,并且触发步骤4中B1页面的路由注册逻辑,把B1页面注册到RouterModule的全局路由表builderMap中。

    @Builder
    export function harBuilder(value: object) {NavDestination() {Column() {// ...Button($r("app.string.to_harb_pageB1"), { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {buildRouterModel(RouterNameConstants.ENTRY_HAP, BuilderNameConstants.HARB_B1);})}.width('100%').height('100%')}.title('A1Page').onBackPressed(() => {RouterModule.pop(RouterNameConstants.ENTRY_HAP);return true;})
    }
    

    上述方案,当在entry模块页面上点击跳转到harA模块的页面时序图如下:

img

业务实现中的关键点

动态加载

上述方案实现解耦的关键是使用了动态加载的方式和自执行的函数。针对该解决方案可以根据业务需求进一步优化和封装。例如harB中需要注册多个页面:B1、B2、B3。改进方式如下:

  1. 在harB的对外导出类Index.ets中定义加载时的初始化函数harInit,该函数对harB中需要注册路由的页面组件进行加载管理,被调用时将根据不同的路径动态加载不同的页面。
export function harInit(builderName: string): void {// 根据routerModule中路由表的key值动态加载要跳转的页面的相对路径switch (builderName) {case BuilderNameConstants.HARB_B1:import("./src/main/ets/components/mainpage/B1");break;case BuilderNameConstants.HARB_B2:import("./src/main/ets/components/mainpage/B2");break;case BuilderNameConstants.HARB_B3:import("./src/main/ets/components/mainpage/B3");break;default:break;}
}
  1. 优化RouterModule模块中的push方法。为了便于路由跳转时能携带更多信息,增加路由信息类RouterModel作为push时的入参。在push方法中通过该参数获取跳转页面所在的包名、路由名和所需的参数信息。通过包名成功加载har 模块后,根据路由名builderName调用har 模块index页面上定义的harInit函数,实现har模块内多页面的动态加载。
// 路由信息类,便于跳转时传递更多信息
export class RouterModel {// 路由页面别名,形式为${包名}_${页面名}builderName: string = "";// 路由栈名称routerName: string = "";// 需要传入页面的参数param?: object = new Object();
}// 创建路由信息,并放到路由栈表中
export function buildRouterModel(routerName: string, builderName: string, param?: object) {let router: RouterModel = new RouterModel();router.builderName = builderName;router.routerName = routerName;router.param = param;RouterModule.push(router);
}

路由栈管理

有些业务场景中存在需要使用多个Navigation组件的情况,该场景下需要在RouterModule中管理多个与Navigation组件对应的路由栈NavPathStack对象。此时,可以在RouterModule模块中建立一个路由栈表,以key-value的形式存储多个Navigation组件对应的的路由栈,以此实现多路由栈的管理。增加路由栈后,RouterModule中的路由方法都需要先通过routerName获取到路由栈,再进行方法调用。代码如下:

export class RouterModule {// ...// 初始化路由栈,需要关联Navigation组件static routerMap: Map<string, NavPathStack> = new Map<string, NavPathStack>();// 通过名称注册路由栈public static createRouter(routerName: string, router: NavPathStack): void {RouterModule.routerMap.set(routerName, router);}// 通过名称获取路由栈public static getRouter(routerName: string): NavPathStack {return RouterModule.routerMap.get(routerName) as NavPathStack;}// 通过传入RouterModule跳转到指定页面组件,RouterModule中需要增加routerName字段用于获取路由栈public static async push(router: RouterModel): Promise<void> {const harName = router.builderName.split('_')[0];await import(harName).then((ns: ESObject): Promise<void> => ns.harInit(router.builderName));RouterModule.getRouter(router.routerName).pushPath({ name: router.builderName, param: router.param });}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/64480.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【腾讯云】AI驱动TDSQL-C Serveress 数据库技术实战营-如何是从0到1体验电商可视化分析小助手得统计功能,一句话就能输出目标统计图

欢迎来到《小5讲堂》 这是《腾讯云》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 背景效果图流程图创建数据库基本信息数据库配置设置密码控制台开启…

XSLT 编辑 XML

XSLT 编辑 XML 介绍 XSLT&#xff08;可扩展样式表语言转换&#xff09;是一种用于转换XML文档的语言。它允许开发人员将XML数据转换为其他格式&#xff0c;如HTML、PDF或纯文本。XSLT通过使用XPath查询来定位XML文档中的元素&#xff0c;并对这些元素应用转换规则。在本教程…

thinkphp8自带分页bootstrap

tp8引用的是bootstrap3.4.1这个版本&#xff1b; 前端结构&#xff1a; <ul class"pagination"><li><a href"/index.php?page4"></a></li><li><a href"/index.php?page1">1</a></li>…

win服务器的架设、windows server 2012 R2 系统的下载与安装使用

文章目录 windows server 2012 R2 系统的下载与安装使用1 windows server 2012 的下载2 打开 VMware 虚拟机软件&#xff08;1&#xff09;新建虚拟机&#xff08;2&#xff09;设置虚拟机&#xff08;3&#xff09;打开虚拟机 windows server 2012&#xff08;4&#xff09;进…

如何在谷歌浏览器中开启安全浏览

在数字化时代&#xff0c;网络安全变得愈发重要。作为全球最受欢迎的网络浏览器之一&#xff0c;谷歌浏览器提供了多种功能来保护用户的在线安全。本文将详细介绍如何在谷歌浏览器中开启安全浏览&#xff0c;并额外提供一些有用的页面滚动设置、地址栏快捷搜索和跟踪防护的相关…

djiango DRF的使用

djiango DRF的使用 一 、初始 DRF序列化环境安装环境配置数据模型定义定义DRF序列化模型对象 二 、DRF请求和响应请求对象&#xff08;Request objects&#xff09;响应对象&#xff08;Response objects&#xff09;状态码&#xff08;Status codes&#xff09;包装&#xff0…

如何使用 Python 连接 PostgreSQL 数据库?

在Python开发中&#xff0c;连接PostgreSQL数据库是一个常见的需求。 我们可以使用多种库来实现这一功能&#xff0c;其中最常用的是psycopg2。 下面我将详细介绍如何使用psycopg2来连接PostgreSQL数据库&#xff0c;并提供一些实际开发中的建议和注意事项。 1. 使用 psycop…

计算机网络-HTTP协议

HTTP HTTP是一种不保存状态&#xff0c;即无状态的协议。HTTP协议自身不对请求和响应之间的通信进行保存。为了保存状态因此后面也有一些技术产生比如Cookies技术。 HTTP是通过URI定位网上的资源&#xff0c;理论上将URI可以访问互联网上的任意资源。 如果不是访问特定的资源…

CTFHub 命令注入-综合练习(学习记录)

综合过滤练习 命令分隔符的绕过姿势 ; %0a %0d & 那我们使用%0a试试&#xff0c;发现ls命令被成功执行 /?ip127.0.0.1%0als 发现一个名为flag_is_here的文件夹和index.php的文件&#xff0c;那么我们还是使用cd命令进入到文件夹下 http://challenge-438c1c1fb670566b.sa…

前端 下载文件时如何处理后端返回的 文件流

在前端&#xff0c;处理文件下载通常涉及到接受一个 文件流&#xff08;Blob 或者 ArrayBuffer&#xff09;&#xff0c;然后将它转换成可以下载的链接。以下是实现前端文件下载并接受文件流的一些常见方法。 1. 使用 Blob 和 URL.createObjectURL 创建下载链接 假设后端返回…

HttpSevletRequest Body信息不能被多次读取的问题

在 Java Web 开发中&#xff0c;HTTP 请求体是客户端向服务器发送数据的主要载体&#xff0c;例如表单提交、JSON 数据等。当服务器收到请求后&#xff0c;通常通过 HttpServletRequest 类获取请求体的内容。然而&#xff0c;HTTP 请求体通常只能被读取一次。这是因为请求体使用…

ARM CCA机密计算安全模型之受保护内存

安全之安全(security)博客目录导读 目录 1、一般威胁模型 2、可能的缓解措施 3、CCA 使用外部内存 4、外部内存初始化 5、资产 6、基线内存保护配置文件 7、内存清理 8、额外的内存保护 许多 Realm 和 CCA 资产存储在外部内存中。本博客讨论针对基于内存攻击的缓解措施…

ubuntu升级python版本

Ubuntu升级Python版本 解压缩文件&#xff1a; 下载完成后&#xff0c;解压缩文件&#xff1a; tar -xf Python-3.12.0.tgz编译并安装&#xff1a; 进入解压后的目录&#xff0c;然后配置和安装Python&#xff1a; codecd Python-3.12.0 ./configure --enable-optimizations ma…

从Windows到Linux:跨平台数据库备份与还原

数据库的备份与还原 目录 引言备份 2.1 备份所有数据库2.2 备份单个数据库2.3 备份多个指定数据库 传输备份文件还原 4.1 还原所有数据库4.2 还原单个数据库4.3 还原多个指定数据库 注意事项拓展 1. 引言 在不同的操作系统间进行数据库迁移时&#xff0c;命令行工具是我们的…

准备写一个内网穿透的工具

准备写一个内网穿透的工具&#xff0c;目前只实现了HTTP内网穿透的GET方式&#xff0c;看能不能坚持写下去 git地址&#xff1a; xuejiazhi/PortRelay

google guava 库 最佳实践 学习指南 学习实用示例

学习Guava库 核心提纲: 入门示例 guava 最佳实践 学习指南 1. 概览与入门 Guava库的介绍Guava的安装与依赖配置Guava的主要模块和功能概览 入门示例 2. 基本工具类 Preconditions&#xff1a;用于断言和参数检查Verify&#xff1a;用于验证对象状态 https://blog.csdn.net/…

捣鼓小玩意-分批处理工具类

分批处理工具类 博主自己的博客点击访问&#xff08;内容大部分更新在自己的博客&#xff0c;有时间才会整理到CSDN&#xff09; 有时候会遇到一些大批量数据成千上万的列表&#xff0c;如果单独一个循环处理&#xff0c;可能会很慢&#xff0c;或者是遇到如需要根据id in ()…

提示词战术技巧-前导课

在人工智能的快速发展中,大模型(如OpenAI的GPT-4和GPT-4)为各行各业带来了革命性的变化。正确地选择适合的业务需求的大模型,并掌握提示词工程的编写与调优是成功实现AI应用的关键。本文旨在提供一个全面的教学指南,帮助学习者理解和掌握大模型的业务选型与提示词工程的技…

零配置打包工具 Parcel 的详细使用指南

前言 在前端开发中&#xff0c;选择一个高效且易用的打包工具至关重要。Parcel 作为一款零配置的 Web 应用打包工具&#xff0c;凭借其卓越的性能和简单的使用体验&#xff0c;赢得了众多开发者的青睐。它不仅能够自动处理依赖关系和代码打包&#xff0c;还支持热模块替换和多…

【AI知识】逻辑回归介绍+ 做二分类任务的实例(代码可视化)

1. 分类的基本概念 在机器学习的有监督学习中&#xff0c;分类一种常见任务&#xff0c;它的目标是将输入数据分类到预定的类别中。具体来说&#xff1a; 分类任务的常见应用&#xff1a; 垃圾邮件分类&#xff1a;判断一封电子邮件是否是垃圾邮件 。 医学诊断&#xff1a;…