ArkUI NavDestination:导航页要有自己的职责

举报
蓝瘦的蜕变 发表于 2026/09/19 21:28:34 2026/09/19
【摘要】 今天我们把 NavDestination 单独拎出来看。 Navigation 负责管栈,但真正展示内容的是 NavDestination。如果目标页只是一堆随手写进去的组件,标题、参数、返回都散在外面,后面维护会很难受。我们这次做一个最小文章列表,点进去后由目标页自己接住参数、设置标题,并负责返回。 列表页只负责选择

ArkUI NavDestination:导航页要有自己的职责

今天我们把 NavDestination 单独拎出来看。

Navigation 负责管栈,但真正展示内容的是 NavDestination。如果目标页只是一堆随手写进去的组件,标题、参数、返回都散在外面,后面维护会很难受。我们这次做一个最小文章列表,点进去后由目标页自己接住参数、设置标题,并负责返回。

列表页只负责选择

列表页里准备两条内容,点击后压入同一个目标页:

this.pathStack.pushPathByName(item.name, new NavDestinationParam(item.title, item.summary, item.author))

这里传的是轻量参数。别把函数、上下文对象、大块业务对象塞进去。目标页拿不到参数时,也要能展示缺省内容。

目标页负责承接

目标页用 NavDestination 包住内容:

NavDestination() {
  // 详情内容
}
.title(this.titleText())

这一步让目标页有自己的导航标题,而不是让外层页面到处替它管理。

onReady 里拿回 pathStack

目标页需要返回时,用 onReady 拿到当前目的地所属的导航栈:

.onReady((context: NavDestinationContext) => {
  this.pathStack = context.pathStack
})

这样按钮里直接 this.pathStack.pop() 就能回到上一个目的地。这个 Demo 不讲生命周期监听、拦截器、自定义转场,也不做系统路由表;我们先把目标页自己的职责写顺。

运行后看什么

进入案例页后,点任意文章卡片。你会看到目标页标题跟着参数变化,正文也来自参数;如果以后参数缺失,页面也有默认文案兜底。

你平时写详情页时,标题和返回逻辑更喜欢放在列表页外层,还是放回详情页自己管理?这个选择会直接影响后面页面拆分的舒服程度。

完整代码

class NavDestinationArticle {
  name: string
  title: string
  summary: string
  author: string
  color: string
  bgColor: string

  constructor(name: string, title: string, summary: string, author: string, color: string, bgColor: string) {
    this.name = name
    this.title = title
    this.summary = summary
    this.author = author
    this.color = color
    this.bgColor = bgColor
  }
}

class NavDestinationParam {
  title: string
  summary: string
  author: string

  constructor(title: string, summary: string, author: string) {
    this.title = title
    this.summary = summary
    this.author = author
  }
}

@Entry
@Component
struct ArkUINavDestinationDemo {
  private pathStack: NavPathStack = new NavPathStack()
  private articles: NavDestinationArticle[] = [
    new NavDestinationArticle('ArticleDetail', '页面标题别写死', '目标页拿到参数后,标题和内容都要能给出缺省值。', 'ArkUI Notes', '#7C3AED', '#F5F3FF'),
    new NavDestinationArticle('ArticleDetail', '返回别到处找 router', 'NavDestination 里通过 onReady 拿到 context.pathStack,再决定怎么返回。', 'Navigation Lab', '#C2410C', '#FFF7ED')
  ]

  @Builder
  destinationMap(name: string, param: Object) {
    if (name === 'ArticleDetail') {
      ArticleDestinationPage({ param: param as NavDestinationParam })
    }
  }

  @Builder
  articleCard(item: NavDestinationArticle) {
    Column({ space: 10 }) {
      Row() {
        Text(item.author)
          .fontSize(12)
          .fontColor(item.color)
          .padding({ left: 10, right: 10, top: 4, bottom: 4 })
          .backgroundColor('#FFFFFF')
          .borderRadius(12)

        Blank()
      }
      .width('100%')

      Text(item.title)
        .fontSize(20)
        .fontWeight(700)
        .fontColor('#0F172A')

      Text(item.summary)
        .fontSize(14)
        .fontColor('#475569')
        .lineHeight(21)

      Button() {
        Text('打开详情')
          .fontSize(14)
          .fontColor('#FFFFFF')
      }
      .width(120)
      .height(38)
      .borderRadius(19)
      .backgroundColor(item.color)
      .onClick(() => {
        this.pathStack.pushPathByName(item.name, new NavDestinationParam(item.title, item.summary, item.author))
      })
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
    .padding(16)
    .backgroundColor(item.bgColor)
    .borderRadius(8)
  }

  build() {
    Navigation(this.pathStack) {
      Scroll() {
        Column({ space: 16 }) {
          Text('ArkUI NavDestination')
            .width('100%')
            .fontSize(28)
            .fontWeight(800)
            .fontColor('#0F172A')

          Text('NavDestination 是 Navigation 栈里的目标页。它应该负责自己的标题、内容、返回和参数缺省。')
            .width('100%')
            .fontSize(14)
            .fontColor('#475569')
            .lineHeight(22)

          Column({ space: 8 }) {
            Text('这一页只做一件事')
              .fontSize(16)
              .fontWeight(700)
              .fontColor('#0F172A')
            Text('列表页只负责选择文章;详情页自己用 NavDestination 承接标题、内容和返回逻辑。')
              .fontSize(13)
              .fontColor('#64748B')
              .lineHeight(20)
          }
          .width('100%')
          .padding(14)
          .backgroundColor('#F8FAFC')
          .borderRadius(8)

          ForEach(this.articles, (item: NavDestinationArticle) => {
            this.articleCard(item)
          }, (item: NavDestinationArticle) => item.title)
        }
        .width('100%')
        .padding(20)
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#FFFFFF')
    }
    .title('NavDestination')
    .titleMode(NavigationTitleMode.Mini)
    .mode(NavigationMode.Auto)
    .navDestination(this.destinationMap)
  }
}

@Component
struct ArticleDestinationPage {
  param: NavDestinationParam = new NavDestinationParam('未命名页面', '这条内容没有拿到参数,所以先展示缺省文案。', 'Unknown')
  private pathStack: NavPathStack = new NavPathStack()

  private titleText(): string {
    return this.param.title.length > 0 ? this.param.title : '未命名页面'
  }

  private summaryText(): string {
    return this.param.summary.length > 0 ? this.param.summary : '这条内容没有拿到参数,所以先展示缺省文案。'
  }

  private authorText(): string {
    return this.param.author.length > 0 ? this.param.author : 'Unknown'
  }

  build() {
    NavDestination() {
      Column({ space: 16 }) {
        Text(this.authorText())
          .fontSize(13)
          .fontColor('#7C3AED')
          .padding({ left: 10, right: 10, top: 4, bottom: 4 })
          .backgroundColor('#F5F3FF')
          .borderRadius(12)

        Text(this.titleText())
          .fontSize(28)
          .fontWeight(800)
          .fontColor('#0F172A')

        Text(this.summaryText())
          .fontSize(15)
          .fontColor('#475569')
          .lineHeight(23)

        Column({ space: 8 }) {
          Text('这个目标页做了什么')
            .fontSize(16)
            .fontWeight(700)
            .fontColor('#0F172A')
          Text('它用 title 设置导航栏标题,用 onReady 拿到 NavDestinationContext,再通过 context.pathStack 完成返回。')
            .fontSize(13)
            .fontColor('#64748B')
            .lineHeight(20)
        }
        .width('100%')
        .padding(14)
        .backgroundColor('#F8FAFC')
        .borderRadius(8)

        Button() {
          Text('返回列表')
            .fontSize(15)
            .fontColor('#FFFFFF')
        }
        .width('100%')
        .height(44)
        .borderRadius(22)
        .backgroundColor('#7C3AED')
        .onClick(() => {
          this.pathStack.pop()
        })
      }
      .width('100%')
      .height('100%')
      .padding(20)
      .alignItems(HorizontalAlign.Start)
      .backgroundColor('#FFFFFF')
    }
    .title(this.titleText())
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack
    })
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。