Skip to content

Router — 页面路由

基于 Native WebView 栈的页面路由,每次导航都会创建/销毁独立的 WebView 实例。

交互示例

导航 API 演示
<script setup lang="ts">
import { ref } from 'vue'
import woo from 'mini-sdk'

const url = ref('pages/detail/index')
const params = ref('id=42&type=article')
const log = ref<string[]>([])

function addLog(msg: string) {
  log.value.unshift(`[${new Date().toLocaleTimeString()}] ${msg}`)
  if (log.value.length > 6) log.value.pop()
}

async function doNavigateTo() {
  const query = Object.fromEntries(new URLSearchParams(params.value))
  await woo.navigateTo({ url: url.value, params: query })
  addLog(`navigateTo("${url.value}", ${JSON.stringify(query)})`)
}

async function doNavigateBack() {
  await woo.navigateBack()
  addLog('navigateBack()')
}

async function doReLaunch() {
  await woo.reLaunch(url.value)
  addLog(`reLaunch("${url.value}")`)
}

async function doGetRoute() {
  const route = woo.getRoute()
  addLog(`getRoute() → path="${route.path}" query=${JSON.stringify(route.query)}`)
}

async function doGetPages() {
  const res = await woo.getCurrentPages()
  addLog(`getCurrentPages() → ${JSON.stringify(res.pages)}`)
}
</script>

<template>
  <div class="demo-router">
    <div class="form-grid">
      <div class="field flex-2">
        <label>目标页面路径</label>
        <input v-model="url" placeholder="pages/detail/index" />
      </div>
      <div class="field flex-2">
        <label>参数 (query string 格式)</label>
        <input v-model="params" placeholder="id=42&type=article" />
      </div>
    </div>

    <div class="btn-row">
      <button class="btn btn-primary" @click="doNavigateTo">navigateTo</button>
      <button class="btn btn-outline" @click="doNavigateBack">navigateBack</button>
      <button class="btn btn-outline" @click="doReLaunch">reLaunch</button>
      <button class="btn btn-ghost" @click="doGetRoute">getRoute</button>
      <button class="btn btn-ghost" @click="doGetPages">getCurrentPages</button>
    </div>

    <div v-if="log.length" class="log-box">
      <div v-for="(entry, i) in log" :key="i" class="log-entry">{{ entry }}</div>
    </div>
  </div>
</template>

<style scoped>
.demo-router { display: flex; flex-direction: column; gap: 14px; width: 100%; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.flex-2 { grid-column: span 2; }
.field { display: flex; flex-direction: column; gap: 5px; }
label { font-size: 11.5px; font-weight: 600; color: var(--vp-c-text-3); text-transform: uppercase; letter-spacing: 0.05em; }
input { padding: 7px 10px; border-radius: 6px; border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg); font-size: 13px; color: var(--vp-c-text-1); font-family: var(--vp-font-family-mono); outline: none; transition: border-color 0.15s; }
input:focus { border-color: #6366f1; }
.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }
.btn { padding: 7px 14px; border-radius: 6px; border: none; font-size: 13px; font-weight: 500; cursor: pointer; font-family: inherit; transition: all 0.15s; }
.btn-primary { background: #6366f1; color: white; }
.btn-primary:hover { background: #4f46e5; }
.btn-outline { background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider); color: var(--vp-c-text-1); }
.btn-outline:hover { background: var(--vp-c-bg-soft); }
.btn-ghost { background: transparent; color: var(--vp-c-text-2); border: 1px dashed var(--vp-c-divider); }
.btn-ghost:hover { background: var(--vp-c-bg-soft); color: var(--vp-c-text-1); }
.log-box { border: 1px solid var(--vp-c-divider); border-radius: 8px; overflow: hidden; max-height: 180px; overflow-y: auto; }
.log-entry { padding: 7px 12px; font-size: 12px; color: var(--vp-c-text-2); font-family: var(--vp-font-family-mono); border-bottom: 1px solid var(--vp-c-divider); white-space: nowrap; }
.log-entry:last-child { border-bottom: none; }
</style>

API 参考

woo.navigateTo(options)

导航到新页面(压栈),支持字符串简写
ts
// ✨ 字符串简写
await woo.navigateTo('pages/detail/index')

// 带参数
await woo.navigateTo({
  url: 'pages/detail/index',
  params: { id: 42, type: 'article' },
})

// 带返回值回调
woo.navigateTo({
  url: 'pages/select/index',
  onBack(data) {
    console.log('用户选择了:', data)
  },
})

Options

参数类型必填说明
urlstring页面路径(不含前导 /
paramsRecord<string, string | number | boolean>URL 查询参数
onBack(data?: any) => void目标页 navigateBack 时回调

多 WebView 注意

onBack 注册在当前 WebView 的 JS 上下文中。子页面返回时,Native 将 backResult 事件投递到本 WebView,回调自动触发。当前 WebView 在后台保活,不会丢失。


woo.navigateBack(options?)

返回上一页(弹栈),可携带数据传给上一页的 onBack
ts
// 简单返回
await woo.navigateBack()

// 跨多级返回
await woo.navigateBack({ delta: 2 })

// 携带数据返回
await woo.navigateBack({
  data: { selected: 'item_1', timestamp: Date.now() },
})

Options

参数类型默认值说明
deltanumber1返回层数
dataany传给上一页 onBack 的任意数据

调用后不再执行

navigateBack 后当前 WebView 即被销毁,后续代码不会执行,无需手动 return


woo.reLaunch(options)

关闭所有页面,重新打开指定页面。支持字符串简写
ts
await woo.reLaunch('pages/home/index')

await woo.reLaunch({ url: 'pages/home/index', params: { refresh: true } })

woo.navigateToTab(options)

切换到 Tab 页面,同时关闭所有非 Tab 页面。
ts
await woo.navigateToTab('pages/home/index')

woo.reLaunchTab(options)

重启指定 Tab(清空历史后重新加载)。常用于登出场景。
ts
await woo.reLaunchTab('pages/home/index')

woo.getCurrentPages()

获取当前页面栈(异步)。
ts
const pages = await woo.getCurrentPages()
console.log(pages) // ['pages/home/index', 'pages/detail/index']

woo.getRoute()

同步获取当前 WebView 的页面路径和 query(从 URL 解析,无 bridge)。
ts
const { path, query } = woo.getRoute()
console.log(path)  // 'pages/detail/index'
console.log(query) // { id: '42', type: 'article' }(均为字符串)
运行环境URL 格式示例
模拟器(hash 路由)hashhttp://localhost/#/pages/detail?id=1
Native(pathname 路由)pathname + searchhttps://host/pages/detail?id=1

woo.navigateToMiniApp(options)

跳转到另一个小程序。
ts
await woo.navigateToMiniApp({
  appId: 'com.example.partner',
  path: 'pages/detail/index',
  extraData: { itemId: 99 },
})

基于 OpenSumi IDE 构建