7 Star 154 Fork 25

GVPHZWei/ZRouter

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

介绍

ZRouter是一款轻量级且非侵入性的鸿蒙动态路由框架,可解决HAR/HSP业务模块间的耦合与通信问题。主要特性:

  • 简化Navigation使用,无需关注路由表的配置,对Navigation及NavDestination组件保持零侵入;
  • 支持API链式调用,让API更简洁直观;
  • 为了进一步简化使用,支持NavDestination页面模板化,是一个可选项;
  • 注解参数支持使用静态常量,可跨模块定义;
  • 支持自定义与全局拦截器,可设优先级及中断逻辑,可实现页面重定向、登录验证等业务场景。
  • 支持服务路由,可实现Har/Hsp模块间的通信;
  • 支持全局及单个页面的生命周期函数管理,可使任意类都能享有与组件相同的生命周期特性,可实现页面埋点统计等业务场景;
  • 支持跨多级页面参数携带返回监听;
  • 支持自定义URL路径跳转,可在拦截器内自行解析URL实现业务逻辑;
  • 内置多种转场动画效果(平移、旋转、渐变、缩放、高斯模糊),并支持自定义动画;
  • 支持启动模式、混淆、嵌套Navigation、Hap;
  • 支持第三方Navigation的使用本库API;
  • 支持与您现有项目中的Navigation无缝融合,实现零成本向本库迁移;
  • 未来计划:支持共享元素动画、持续优化。

使用十分简单,没有繁琐的配置,两行代码就可以完成页面的跳转,如下:

a1.png

ZRouter已上架录入到华为鸿蒙生态伙伴组件专区

router-register-plugin编译插件

下载安装

在项目根目录的hvigor目录下的hvigor-config.json5文件中配置安装

  "dependencies": {
    "router-register-plugin":"1.3.0"
  },

Static Badge npm

最后记得Sync Now或重新build让插件安装生效。

或者使用hvigorw命令行工具执行任一命令,命令行工具会自动执行安装构建依赖。

hvigorw --sync

初始配置

在每个模块中的hvigorfile.ts文件导入router-register-plugin插件模块,如下:

// 1、导入
import { routerRegisterPlugin, PluginConfig } from 'router-register-plugin'

// 2、初始化配置
const config: PluginConfig = {
    scanDirs: ['src/main/ets/pages', 'src/main/ets/views'], // 扫描的目录,如果不设置,默认是扫描src/main/ets目录
    logEnabled: true, // 查看日志
    viewNodeInfo: false, // 查看节点信息
    isAutoDeleteHistoryFiles: true // 删除无用编译产物
    lifecycleObserverAttributeName: 'xxx' // 可选,设置全局的生命周期实现类在组件上的属性名,默认值是lifecycleObserver

}
export default {
    // 3、添加插件
    plugins:[routerRegisterPlugin(config)] 
}

常用的配置字段:

  • scanDirs:扫描的目录,建议设置可更精准、更快扫描生成文件,如果不设置,默认是扫描src/main/ets目录
  • logEnabled:日志记录开关。
  • viewNodeInfo:查看节点信息的开关,只有logEnabled和viewNodeInfo同时开启才会生效
  • isAutoDeleteHistoryFiles:是否删除无用编译产物;
  • lifecycleObserverAttributeName:设置全局的生命周期实现类在组件上的属性名,默认值是lifecycleObserver,若要设置单个页面的名称,可在@ZRoute注解中的loAttributeName属性上设置。

PluginConfig配置对象还有其他属性,但不建议使用,使用默认值即可。

上面所有路径都是相对模块的src目录而言的,是相对路径。最后记得Sync Now或重新build让配置生效。

其中_generated目录和route_map.json文件在编译阶段自动生成的,建议在git的.gitignore忽略掉这两个文件。

_generated
route_map.json

ZRouter的基本使用

下载安装

在每个har/hsp模块中,通过ohpm工具下载安装库:

ohpm install @hzw/zrouter

页面跳转

新建三个模块分别是harA、harB、hspC,三者之间没有依赖关系,entry模块依赖了这三个模块,通过ZRouter可以在四个模块间相互跳转,从而达到模块解耦效果。模块关系图如下图:

6a594e11394c60d93983297a1e5322db.png

1、在EntryAbility的onCreate()方法中初始化ZRouter,建议使用initialize()方法进行初始化,init()方法已弃用

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 如果项目中存在hsp模块则传入true
      ZRouter.initialize((config) => {
          config.isLoggingEnabled = BuildProfile.DEBUG
          config.isHSPModuleDependent = true
    })
}

建议在AbilityStage的onCreate()方法中完成初始化


export class AppAbilityStage extends AbilityStage{
  onCreate(): void {
    // 应用HAP首次加载时触发,可以在此执行该Module的初始化操作(例如资源预加载、线程创建等)。
    // 在module.json5配置文件中,通过配置 srcEntry 参数来指定模块对应的代码路径,以作为HAP加载的入口。
    // 初始化路由
    ZRouter.initialize((config) => {
      config.isLoggingEnabled = BuildProfile.DEBUG
      config.isHSPModuleDependent = true 
      // 服务路由初始化配置,如果没有使用服务路由,可不设置
      config.loadDynamicModule = ['@hzw/hara', 'harb', 'hspc']
      config.onDynamicLoadComplete = () => {
        console.log("已完成所有模块的加载")
      }
    })

  }
}

2、在Index页面使用Navigation作为根视图,通过ZRouter的getNavStack()方法获取NavPathStack实例,将其传入到Navigation的构造函数中。

如果在Index入口文件中启动Splash页面,建议放在Navigation的onAppear方法中进行启动,或者组件的onPageShow方法,具体可参考demo

// Index 中使用 aboutToAppear 生命周期函数会因为 Navigation 还没初始化完成导致无法有效跳转,可使用替换成 onPageShow
@Entry
@Component
struct Index {

  build() {
    // 获取NavPathStack实例对象
    Navigation(ZRouter.getNavStack()){
      Column({space:12}){
        Button('toHarAMainPage').onClick((event: ClickEvent) => {
        // 跳转页面
          ZRouter.push("harAMainPage")
        })
      }
    }
    .title('Main')
    .height('100%')
    .width('100%')
  }
}

通过ZRouter的pushXX()方法进行页面跳转,参数是@Route装饰器上的name属性值。或者用ZRouter的getNavStack()方法来执行页面跳转。

3、在NavDestination子页的使用自定义@Route或@ZRoute注解描述当前页面,其中name属性是必填的,页面跳转需要用到name值,建议使用驼峰式命名,还有另外三个可选属性分别是:

  • description:页面描述,没有功能作用;
  • needLogin:如果页面需要登录,可以将值设置为true,然后在拦截器中做页面重定向到登录页;
  • extra:额外的值可以通过该属性设置

@Route/@Service/@ZAttribute/@ZLifecycle等自定义注解上的name属性支持使用静态常量,在后面文档有详细介绍

代码如下:

@Route({ name: 'hspCPage1', needLogin:true ,extra: 'hsp'})
@Component
export struct Page1 {
  @State message: string = 'Hello World';

  build() {
    NavDestination(){
      Column({space:12}){
        Button('toHarAPage1').onClick((event: ClickEvent) => {
          ZRouter.push("harAPage1")
        })
      }

    }
    .title('hspCPage1')
    .width('100%')
    .height('100%')

  }
}

建议通过ZRouter.getInstance()方式来操作路由的跳转与关闭,使用会更灵活简洁,之前的ZRouter的静态方法依然保留着,在1.2.0版本起将标记为过期状态了。

 ZRouter.getInstance()
  .setParam("root data")
  .setLunchMode(LaunchMode.STANDARD) // 启动模式
  .enableCrossPageParamReturn() // 跨页面参数返回
  .setAnimate(true)
  .setPopListener((r) => {
    LogUtil.log("index result: ", r.data ," from: ", r.from);
  })
  .navigation("harAPage3")

拦截器

ZRouter支持多个拦截器和全局拦截器,在拦截器中可以做页面跳转的拦截,比如登录拦截,404拦截、埋点、自定义URL路径跳转等。

全局拦截器

全局拦截器提供两种使用方式:

  • 直接函数回调时的方式;
  • 类实现接口的方式(建议使用,功能更全面),支持字面量对象和new创建的对象。

函数回调的方式,代码示例:

@Entry
@Component
struct Index {
  aboutToAppear(): void {
    ZRouter.setGlobalInterceptor((info) => {
      if (info.notRegistered) {
        return
      }
      let isLogin = AppStorage.get<Boolean>("isLogin")
      if (info.needLogin && !isLogin) {
        let param = ZRouter.getParamByName(info.data?.name ?? "")
        ZRouter.redirectForResult2("LoginPage", param, (data) => {
            if (data.result) {
              // 登录成功
              promptAction.showToast({ message: `登录成功` })
              return true // 返回true 则继续跳转登录前的页面
            }
            return false
          })
      }
    })

  }
    
}
  

类实现接口的方式,代码示例:


export class GlobalNavigateInterceptor implements  IGlobalNavigateInterceptor{
  onRootWillShow?: ((fromContext: NavDestinationContext) => void) | undefined = (fromContext) => {
    console.log("IInterceptor Global onRootWillShow: ", fromContext.pathInfo.name)
  }
  onPageWillShow?: ((fromContext: NavDestinationContext, toContext: NavDestinationContext) => void) | undefined = (from ,to)=>{
    console.log("IInterceptor Global onPageWillShow: ", from, to.pathInfo.name, to.pathInfo.param)
  }

  onNavigate?: ((context: InterceptorInfo) => void) | undefined = (info)=>{
    if (info.notRegistered) return
    console.log("IInterceptor Global onNavigate: ", info.name)

    let isLogin = AppStorage.get<boolean>("isLogin")
    if (info.isNeedLogin && !isLogin) {
      let param = info.param
      ZRouter.redirectForResult2<boolean>("LoginPage", param, (data) => {
        if (data.data) {
          // 登录成功
          promptAction.showToast({ message: `登录成功` })
          return true // 返回true 则继续跳转登录前的页面
        }
        return false
      })
    }

  }
}

// 添加拦截器
ZRouter.setGlobalInterceptor(new GlobalNavigateInterceptor())       


info.notRegistered()方法判断当前页面是否注册,如果没有注册,将使用ZRouter.redirect() 方法来重定向到404页面;通过ZRouter.redirectForResult() 方法来重定向到登录页面,这个方法接受一个回调函数,该回调函数会在用户登录成功或失败后被调用,在回调函数内部,使用 data.result判断是否登录 ,如果登录成功了给回调函数 return true 来指示继续执行登录前的页面跳转。如果登录失败,或者用户取消登录,回调函数将返回 false,表示不跳转。

登录页面代码示例:

@Route({ name: 'LoginPage'})
@Component
export struct LoginPage{

  build() {
    NavDestination(){
       Column({space:15}){
         Button('登录成功').onClick((event: ClickEvent) => {
            // 模拟登录
           AppStorage.setOrCreate<boolean>('isLogin', true)
           ZRouter.finishWithResult<boolean>(true)
         })
       }
       .width('100%')
       .height('100%')
    }
      .width('100%')
      .height('100%')
      .title('LoginPage')
  }
}

在登录成功后通过ZRouter.finishWithResult()方法携带数据关闭页面,会将状态传递给redirectForResult2()方法的回调函数。

上面是全局拦截器,每个跳转都会触发,如果需要添加多个拦截器,则可以使用setInterceptor()方法。

自定义拦截器

自定义拦截器,首先实现接口IInterceptor,然后使用setInterceptor()方法注册拦截器,,代码示例如下:

export interface IInterceptor {
  process: ProcessCallback;
  // 优先级,数字越大优先级越高
  priority: number;
}
export type ProcessCallback = (context: InterceptorInfo) => InterceptorInfoOrNull;

在IInterceptor的process()方法中进行页面跳转的拦截,process()方法返回null会中断后面的拦截器逻辑,返回context则继续执行后面的拦截器逻辑。

代码示例:


aboutToAppear(): void {
  ZRouter.setInterceptor(new UrlInterceptor())
}

export class UrlInterceptor implements IInterceptor {
  // 设置拦截器优先级,数值越大则优先执行
  priority: number = 10000;
  process: (context: InterceptorInfo) => InterceptorInfoOrNull = (context) => {
    return context
  }
}

关于其他API的使用请参考demo。

NavDestination页面模板化

在介绍基本使用的流程中,我们知道每个页面都需要通过NavDestination来包裹,这样会造成代码的冗余,因此可通过ZRouter的模板化能力将NavDestination层去除。

具体使用见详细文档

自定义URL路径跳转

在项目中一般会设计一套统一的URL路径跳转规范,通过URL路径跳转到不同原生页面。比如下面的URL路径:

hzw://hello?id=69&name=harAPage3

获取URL路径上的name参数进行跳转原生页面,可以设置一个拦截器来拦截URL路径跳转。代码示例:

跳转:

Button('https://www.baidu.com?id=66&name=hspCIndex').onClick((event: ClickEvent) => {
       ZRouter.getInstance()
         .navigation("https://www.baidu.com?id=66&name=hspCIndex")
    })

Button('hzw://hello?id=69&name=harAPage3').onClick((event: ClickEvent) => {
  ZRouter.getInstance()
    .navigation("hzw://hello?id=69&name=harAPage3")
})

拦截器:

export class UrlInterceptor implements IInterceptor {
  // 设置拦截器优先级,数值越大则优先执行
  priority: number = 10000;
  process: (context: InterceptorInfo) => InterceptorInfoOrNull = (context) => {
    console.log("IInterceptor process: ", this.priority , context.name)
    // 自定义URL路径是没有注册的
    if (context.notRegistered) {
      // 如果是URL路径跳转
      if (this.isLink(context.name)) {
        // 拦截到URL路径跳转,进行处理
        const map = this.parseQueryString(context.name)
        const name = map.get('name')
        if (name) {
          // 跳转原生页面
          ZRouter.getInstance()
            .setParam(map.get("id"))
            .navigation(name)
        }
      } else {
        ZRouter.getInstance().redirect("PageNotFound2")
      }
      return null // 返回null则拦截掉
    }
    return context
  };

   isLink(str: string): boolean {
    const linkRegex = /^(hzw:\/\/|http:\/\/|https:\/\/|www\.).+/;
    return linkRegex.test(str);
  }


  parseQueryString(queryString: string) {
    let params = new HashMap<string, string>();
    let queryStringWithoutQuestionMark = queryString.split('?')[1];
    if (queryStringWithoutQuestionMark) {
      let keyValues = queryStringWithoutQuestionMark.split('&');
      keyValues.forEach((keyValue) => {
        let pair = keyValue.split('=');
        let key = decodeURIComponent(pair[0]);
        let value = decodeURIComponent(pair[1]);
        params.set(key, value)
      })
    }
    return params;
  }

}

注解上使用静态常量,可跨模块定义

router-register-plugin插件1.0.7版本起,@Route与@Service注解的name属性可使用静态常量,方便统一管理路由名称;静态常量支持当前模块或跨模块定义,常量的定义模版如下:

export class RouterConstants {
  public static readonly URL_TEST_PAGE: string = "url_test";
  public static readonly HARA_MAIN_PAGE :string = "harAMainPage"
}

如果路由常量在一个公共模块定义,建议在模块的Index.ets文件导出,另外RouterConstants的文件必须是.ets后缀,不支持ts后缀文件。具体可参考案例

服务路由-模块间通信

服务路由主要用于实现模块之间的通信,模块间是相互独立且不直接依赖于彼此。

1.0.9版本开始支持,具体使用可见详情文档 或者参考demo

生命周期函数管理

ZRouter的组件生命周期管理能力,主要有两个特点:

  • 不影响你原有的生命周期业务逻辑,对NavDestination页面保持着零侵入性,整合了组件通用生命周期函数和NavDestination生命周期函数
  • 可以让任何一个类具备有与组件的生命周期能力;

具体使用见详细文档

路由转场动画

从1.1.1版本起内置了转场动画(平移、旋转、渐变、缩放、高斯模糊),也支持自定义转场动画;具体使用见详细文档

第三方Navigation实例使用本库的API

如果第三方Navigation实例使用本库的API,需要将第三方Navigation的NavPathStack实例注册到ZRouter中,代码示例:

  aboutToAppear(): void {
    // 在合适的时机注册导航栈
    // let s = ZRouter.getNavStackByName(NAV_STACK_NAME)
    ZRouter.registerNavStack(NAV_STACK_NAME, this.stack)
  }

  aboutToDisappear(): void {
    ZRouter.unregisterNavStack(NAV_STACK_NAME)
  }

上面是模拟代码,具体注册的时机需要根据实际情况来定。NAV_STACK_NAME是一个自定义常量,用于标识导航栈的名称。

页面跳转:

  Column({ space: 15 }) {
      Text(this.msg)
      Button("harAMainPage").onClick((event: ClickEvent) => {
        ZRouter.getInstance(NAV_STACK_NAME)
          .setAnimate(true)
          .setPopListener((v) => {
            this.msg = v.data?.toString() + ' ' + v.from?.toString()
          })
          .navigation("harAMainPage")
      })
    }

把标识导航栈的名称NAV_STACK_NAME,入参到ZRouter.getInstance()方法中,就可使用ZRouter的API。

在ArkUI-X项目上的使用

router-register插件在ArkUI-X项目的配置有所不同,需要使用者自己手动修改下hvigorfile.ts文件,详细见ArkuiX-ZRouter,或者issues IB35F5

混淆

生产环境需要在每个模块的obfuscation-rules.txt文件添加混淆配置:

-keep-file-name
Index
_generated
ZR*

工作原理

路由注册流程代码是由插件在编译阶段自动化生成,其原理是不难的,通过Hvigor插件扫描指定目录的ets文件,递归解析ets文件的语法树节点,查找解析注解上的参数,然后将这些信息通过模板引擎生成对应的代码逻辑。

与Java注解处理器原理是类似的

ZRouter库是对NavPathStack对进行高度封装的,包括了页面跳转、自定义拦截器、服务路由、生命周期回调、转场动画、NavDestination模板化等功能,提供了更简洁易用的API,其中部分思想参考了Android ARouter路由框架。

编译插件的基本流程图:

84fbcf502ec66b87981622eaf57499e4.png

接口列表

查看详细文档

FQA

查看详细文档

源码

实战案例

这里推荐一个基于ZRouter搭建项目的实战案例,仅做参考具体可根据你项目来调整。

参与贡献

  • Fork 本仓库
  • 新建分支
  • 提交代码
  • 新建 Pull Request

作者其他库

联系我们

欢迎大家提交issue、PR(可以统一收集问题,方便更多人查阅,会第一时间回复处理) ,或进群交流(+v: 751496032)。

_cgi-bin_mmwebwx-bin_webwxgetmsgimg__MsgID7985482268088807228skeycrypt_4f9ae0b8_0271518ab0cb7cd42bc056451ad75554mmweb_appidwx_webfilehelper.md.jpg

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

ZRouter是一款轻量级且非侵入性的鸿蒙动态路由框架,可解决HAR/HSP业务模块间的耦合与通信问题,提供了自定义拦截器、服务路由、转场动画、生命周期函数管理、NavDestination模板化等功能。 展开 收起
TypeScript
Apache-2.0
取消

发行版 (14)

全部
14天前

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
TypeScript
1
https://gitee.com/common-apps/ZRouter.git
git@gitee.com:common-apps/ZRouter.git
common-apps
ZRouter
ZRouter
master

搜索帮助