Skip to content

路由与导航

路由配置

页面配置

在 Taro 中,页面路由需要在 app.config.js(React)或 app.json(Vue)中配置:

javascript
// app.config.js (React)
export default {
  pages: [
    'pages/index/index',
    'pages/detail/index',
    'pages/login/index'
  ],
  window: {
    backgroundTextStyle: 'light',
    navigationBarBackgroundColor: '#fff',
    navigationBarTitleText: 'Taro 应用',
    navigationBarTextStyle: 'black'
  },
  tabBar: {
    list: [
      {
        pagePath: 'pages/index/index',
        text: '首页',
        iconPath: 'assets/images/home.png',
        selectedIconPath: 'assets/images/home-active.png'
      },
      {
        pagePath: 'pages/my/index',
        text: '我的',
        iconPath: 'assets/images/my.png',
        selectedIconPath: 'assets/images/my-active.png'
      }
    ]
  }
}

路由参数

在跳转页面时,可以通过 URL 参数传递数据:

javascript
// 传递参数
Taro.navigateTo({
  url: '/pages/detail/index?id=1&name=测试'
})

// 接收参数
// 在 detail 页面的 onLoad 生命周期中
componentDidMount() {
  const { id, name } = this.$router.params
  console.log(id, name)
}

// 或使用 React Hooks
import { useRouter } from '@tarojs/taro'

function Detail() {
  const router = useRouter()
  const { id, name } = router.params
  
  return (
    <View>
      <Text>ID: {id}</Text>
      <Text>Name: {name}</Text>
    </View>
  )
}

导航方法

基本导航

javascript
// 跳转到新页面,保留当前页面
Taro.navigateTo({
  url: '/pages/detail/index'
})

// 重定向到新页面,关闭当前页面
Taro.redirectTo({
  url: '/pages/login/index'
})

// 返回到上一页
Taro.navigateBack({
  delta: 1 // 返回的页面数
})

// 跳转到 Tab 页面,关闭所有非 Tab 页面
Taro.switchTab({
  url: '/pages/home/index'
})

// 关闭所有页面,跳转到新页面
Taro.reLaunch({
  url: '/pages/index/index'
})

导航动画

在 H5 端,可以通过配置实现导航动画:

javascript
// 跳转到新页面,带动画
Taro.navigateTo({
  url: '/pages/detail/index',
  animationType: 'slide-in-right', // 动画类型
  animationDuration: 300 // 动画时长
})

路由拦截

全局路由拦截

可以通过 Taro 的 addInterceptor 方法实现路由拦截:

javascript
// app.js
import Taro from '@tarojs/taro'

// 添加路由拦截器
Taro.addInterceptor({
  // 发起请求前的处理
  invoke(options) {
    // 检查是否需要登录
    if (options.url.includes('/pages/need-login/')) {
      const token = Taro.getStorageSync('token')
      if (!token) {
        // 跳转到登录页面
        Taro.navigateTo({
          url: '/pages/login/index'
        })
        // 中断当前导航
        return false
      }
    }
    return options
  },
  // 请求成功后的处理
  success(res) {
    console.log('导航成功', res)
  },
  // 请求失败后的处理
  fail(err) {
    console.log('导航失败', err)
  }
})

页面级路由拦截

在页面的生命周期中,可以实现页面级的路由拦截:

javascript
// 在页面组件中
componentDidMount() {
  // 监听页面卸载前的事件
  this.$onReachBottom(() => {
    console.log('页面触底')
  })
  
  // 监听页面滚动
  this.$onPageScroll((res) => {
    console.log('页面滚动', res.scrollTop)
  })
  
  // 监听页面尺寸变化
  this.$onResize((res) => {
    console.log('页面尺寸变化', res.size)
  })
}

// 页面卸载前的处理
componentWillUnmount() {
  // 清理定时器等
}

路由状态管理

使用 Redux 管理路由状态

javascript
// store/actionTypes.js
export const SET_ROUTE = 'SET_ROUTE'

// store/actions.js
import { SET_ROUTE } from './actionTypes'

export const setRoute = (route) => ({
  type: SET_ROUTE,
  payload: route
})

// store/reducers.js
import { SET_ROUTE } from './actionTypes'

const initialState = {
  currentRoute: ''
}

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case SET_ROUTE:
      return {
        ...state,
        currentRoute: action.payload
      }
    default:
      return state
  }
}

// 在页面中使用
import { useDispatch, useSelector } from 'react-redux'
import { setRoute } from '../../store/actions'

function Home() {
  const dispatch = useDispatch()
  const currentRoute = useSelector(state => state.route.currentRoute)
  
  useEffect(() => {
    dispatch(setRoute('/pages/home/index'))
  }, [])
  
  return (
    <View>
      <Text>当前路由: {currentRoute}</Text>
    </View>
  )
}

使用 MobX 管理路由状态

javascript
// store/routeStore.js
import { observable, action } from 'mobx'

class RouteStore {
  @observable currentRoute = ''
  
  @action setRoute(route) {
    this.currentRoute = route
  }
}

export default new RouteStore()

// 在页面中使用
import { observer } from 'mobx-react'
import routeStore from '../../store/routeStore'

const Home = observer(() => {
  useEffect(() => {
    routeStore.setRoute('/pages/home/index')
  }, [])
  
  return (
    <View>
      <Text>当前路由: {routeStore.currentRoute}</Text>
    </View>
  )
})

export default Home

路由传递复杂数据

使用 EventChannel

对于复杂数据,可以使用 EventChannel 进行传递:

javascript
// 页面 A
Taro.navigateTo({
  url: '/pages/detail/index',
  success: function(res) {
    // 创建事件通道
    const eventChannel = res.eventChannel
    // 发送数据
    eventChannel.emit('acceptDataFromOpenerPage', {
      data: { id: 1, name: '测试' },
      message: 'Hello from Page A'
    })
  }
})

// 页面 B
// 在 onLoad 生命周期中
componentDidMount() {
  // 获取事件通道
  const eventChannel = this.$scope.getOpenerEventChannel()
  // 监听事件
  eventChannel.on('acceptDataFromOpenerPage', function(data) {
    console.log(data)
  })
}

// 或使用 React Hooks
import { useRouter } from '@tarojs/taro'

function Detail() {
  const router = useRouter()
  
  useEffect(() => {
    // 获取事件通道
    const eventChannel = router.getOpenerEventChannel()
    // 监听事件
    eventChannel.on('acceptDataFromOpenerPage', function(data) {
      console.log(data)
    })
  }, [])
  
  return (
    <View>Detail Page</View>
  )
}

使用全局状态管理

对于需要在多个页面共享的数据,可以使用全局状态管理:

javascript
// store/index.js
import { createStore } from 'redux'

const initialState = {
  sharedData: {}
}

function reducer(state = initialState, action) {
  switch (action.type) {
    case 'SET_SHARED_DATA':
      return {
        ...state,
        sharedData: action.payload
      }
    default:
      return state
  }
}

export default createStore(reducer)

// 页面 A
import store from '../../store'

function PageA() {
  const handleNavigate = () => {
    // 存储数据到全局状态
    store.dispatch({
      type: 'SET_SHARED_DATA',
      payload: { id: 1, name: '测试' }
    })
    // 跳转到页面 B
    Taro.navigateTo({ url: '/pages/pageB/index' })
  }
  
  return (
    <Button onClick={handleNavigate}>跳转到页面 B</Button>
  )
}

// 页面 B
import store from '../../store'

function PageB() {
  const [data, setData] = useState({})
  
  useEffect(() => {
    // 从全局状态获取数据
    const sharedData = store.getState().sharedData
    setData(sharedData)
  }, [])
  
  return (
    <View>
      <Text>ID: {data.id}</Text>
      <Text>Name: {data.name}</Text>
    </View>
  )
}

路由性能优化

预加载页面

在 H5 端,可以使用预加载来提高页面加载速度:

javascript
// 预加载页面
Taro.preload({
  url: '/pages/detail/index'
})

懒加载页面

对于大型应用,可以使用懒加载来减少初始包大小:

javascript
// app.config.js
export default {
  pages: [
    'pages/index/index',
    'pages/login/index'
  ],
  subPackages: [
    {
      root: 'pages/detail',
      pages: [
        'index'
      ]
    }
  ]
}

路由缓存

在小程序端,可以使用页面缓存来提高页面切换速度:

javascript
// app.config.js
export default {
  window: {
    backgroundTextStyle: 'light',
    navigationBarBackgroundColor: '#fff',
    navigationBarTitleText: 'Taro 应用',
    navigationBarTextStyle: 'black'
  },
  // 启用页面缓存
  tabBar: {
    list: [
      {
        pagePath: 'pages/index/index',
        text: '首页',
        iconPath: 'assets/images/home.png',
        selectedIconPath: 'assets/images/home-active.png'
      }
    ]
  },
  // 页面配置
  pages: [
    'pages/index/index'
  ],
  // 页面缓存配置
  'lazyCodeLoading': 'requiredComponents'
}

路由最佳实践

  1. 合理规划路由:根据应用功能合理规划路由结构
  2. 统一路由管理:使用集中式的路由配置,便于管理
  3. 路由参数校验:对路由参数进行校验,确保数据合法性
  4. 路由拦截:实现路由拦截,处理登录、权限等逻辑
  5. 性能优化:使用预加载、懒加载等方式优化路由性能
  6. 错误处理:处理路由跳转失败的情况
  7. 用户体验:添加适当的导航动画,提升用户体验

基于 VitePress 的本地知识库