网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


前言

最近在做跨平台开发的时候,发现了一个很有意思的现象:同样是路由导航,Flutter、React Native、Web 和 iOS 的实现方式完全不同,而且各有各的痛点。Flutter 的路由不够直观,RN 的多层导航嵌套让人头疼,Web 的路由和状态耦合严重,iOS 的导航虽然稳定但不够灵活。

今天我们就来聊聊这几个平台的路由系统到底有什么本质不同,以及在实际开发中应该如何选择合适的导航方案。

路由即状态的三种实现方式

路由本质上就是应用状态的一种表现形式。不同的平台对"路由即状态"这个概念有不同的实现方式,这也导致了它们在使用体验上的差异。

Flutter:路由栈即状态

Flutter 的路由系统基于路由栈的概念。每次 push 一个新页面,实际上是在栈顶添加一个路由对象;每次 pop,就是从栈顶移除一个路由。这个栈本身就是应用状态的一部分。

Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => DetailPage()),
);

这种设计的好处是路由状态很清晰,栈的结构就是导航的历史记录。但问题也很明显:路由栈是隐式的,你很难直接访问或修改整个栈的状态。想要实现复杂的导航逻辑,比如跳转到栈中间的某个页面,或者清空栈重新开始,就比较麻烦。

React Native:路由即组件状态

React Native 的路由系统(比如 React Navigation)把路由状态存储在组件树中。每个导航器(Navigator)都有自己的状态,这个状态决定了当前显示哪些屏幕。

const Stack = createStackNavigator();

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Detail" component={DetailScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

这种设计的好处是路由状态可以很容易地访问和修改,你可以通过 navigation.navigate() 跳转到任何已注册的屏幕。但问题也很明显:多层导航嵌套的时候,状态管理会变得很复杂。每个导航器都有自己的状态,它们之间如何协调是个大问题。

Web:路由即 URL

Web 的路由系统(比如 Vue Router、React Router)把路由状态存储在 URL 中。URL 的变化会触发路由的变化,路由的变化会触发组件的渲染。

// Vue Router
const routes = [
  { path: '/', component: Home },
  { path: '/detail/:id', component: Detail },
];

// URL: /detail/123 -> 渲染 Detail 组件,id = 123

这种设计的好处是路由状态完全可见,用户可以通过 URL 直接访问任何页面,也可以使用浏览器的前进后退功能。但问题也很明显:路由和状态耦合严重。很多开发者会把业务状态也放在 URL 中,导致 URL 变得很长很复杂。而且 Web 路由需要考虑 SEO、服务端渲染等复杂场景。

Flutter 路由栈的隐性成本

Flutter 的路由栈设计虽然看起来简单,但实际上有很多隐性成本,这些成本在项目规模小的时候不明显,但项目大了之后就会暴露出来。

内存占用问题

Flutter 的路由栈会保持所有页面的状态,这意味着即使页面不在栈顶,它的 Widget 树和状态也会保留在内存中。如果导航层级很深,内存占用会显著增加。

// 假设有这样的导航路径:
// Home -> List -> Detail -> SubDetail -> SubSubDetail
// 即使当前显示的是 SubSubDetail,前面的 4 个页面都还在内存中

这个问题在移动设备上尤其明显,因为内存资源有限。如果页面中有大量图片或者复杂的状态,内存压力会更大。

状态同步问题

由于路由栈是隐式的,不同页面之间的状态同步变得困难。比如页面 A 跳转到页面 B,页面 B 修改了某些数据,页面 A 如何知道这些变化?通常的做法是使用回调函数或者全局状态管理,但这又引入了新的复杂度。

// 页面 A
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DetailPage(
      onDataChanged: (data) {
        // 通过回调更新页面 A 的状态
        setState(() {
          this.data = data;
        });
      },
    ),
  ),
);

这种方式在小项目中还能接受,但在大项目中,回调函数会变得很多很乱,维护成本很高。

导航逻辑分散

Flutter 的导航逻辑通常分散在各个页面中,每个页面都知道自己可以跳转到哪些页面。这种设计在小项目中没问题,但在大项目中会导致导航逻辑难以统一管理。

// 页面 A 中
Navigator.push(context, MaterialPageRoute(builder: (context) => PageB()));

// 页面 B 中
Navigator.push(context, MaterialPageRoute(builder: (context) => PageC()));

// 页面 C 中
Navigator.push(context, MaterialPageRoute(builder: (context) => PageD()));

如果后续需要修改导航逻辑,比如添加权限检查、添加埋点、修改跳转动画等,就需要修改很多地方的代码。

页面是否常驻的跨端对比

不同平台对页面生命周期的管理方式不同,这也影响了路由系统的设计。

Flutter:页面常驻在栈中

Flutter 的页面一旦被 push 到栈中,就会一直保留在内存中,直到被 pop 出来。这意味着页面的状态会一直保持,即使用户看不到这个页面。

// 用户从 Home 跳转到 Detail
Navigator.push(context, MaterialPageRoute(builder: (context) => DetailPage()));
// 此时 Home 页面还在内存中,状态也保留着

// 用户从 Detail 返回到 Home
Navigator.pop(context);
// Detail 页面被销毁,Home 页面重新显示,状态还是之前的

这种设计的好处是用户体验流畅,返回上一页时状态还在。但问题是内存占用大,而且页面之间的状态隔离不够清晰。

React Native:页面可以懒加载

React Native 的页面可以按需加载,不活跃的页面可能会被卸载以释放内存。但具体行为取决于导航器的配置和系统的内存压力。

// React Navigation 可以配置页面的生命周期
<Stack.Screen 
  name="Detail" 
  component={DetailScreen}
  options={{
    // 页面离开时是否保持状态
    detachPreviousScreen: false,
  }}
/>

这种设计的好处是内存占用更可控,但问题是页面状态可能会丢失,需要额外的状态管理机制。

Web:页面完全由路由控制

Web 的页面完全由路由控制,路由变化时组件会被卸载和重新挂载。这意味着页面状态不会自动保留,需要手动保存到 URL、localStorage 或者全局状态中。

// Vue Router
// 路由从 /home 变化到 /detail
// Home 组件被卸载,Detail 组件被挂载
// 如果 Home 组件有状态,需要手动保存

这种设计的好处是状态管理清晰,但问题是需要额外的状态持久化逻辑。

iOS:页面生命周期明确

iOS 的页面生命周期很明确,viewDidLoadviewWillAppearviewDidAppear 等生命周期方法可以精确控制页面的状态。

class DetailViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 页面加载时调用
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // 页面即将显示时调用
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // 页面已经消失时调用
    }
}

这种设计的好处是生命周期清晰,但问题是需要手动管理很多细节。

可维护的导航设计方案

基于以上分析,我们可以总结出一套可维护的导航设计方案,这套方案可以适用于不同的平台。

统一的路由配置

首先,我们需要一个统一的路由配置文件,把所有路由信息集中管理:

// Flutter 示例
class AppRoutes {
  static const String home = '/';
  static const String detail = '/detail';
  static const String profile = '/profile';
  
  static Map<String, WidgetBuilder> getRoutes() {
    return {
      home: (context) => HomePage(),
      detail: (context) => DetailPage(),
      profile: (context) => ProfilePage(),
    };
  }
}

这样修改路由逻辑时,只需要修改一个地方。

路由中间件机制

为了实现权限检查、埋点、日志等功能,我们需要一个路由中间件机制:

class RouteMiddleware {
  static Future<bool> checkAuth(BuildContext context, String route) async {
    // 检查权限
    if (route == '/profile' && !await AuthService.isLoggedIn()) {
      Navigator.pushNamed(context, '/login');
      return false;
    }
    return true;
  }
  
  static void logNavigation(String from, String to) {
    // 记录导航日志
    Analytics.logEvent('navigation', {
      'from': from,
      'to': to,
    });
  }
}

这样可以在不修改业务代码的情况下,添加各种导航相关的功能。

状态管理分离

路由状态和业务状态应该分离。路由状态只负责导航,业务状态由专门的状态管理方案负责:

// 路由状态:只负责导航
class RouteState {
  final String currentRoute;
  final List<String> routeStack;
}

// 业务状态:由状态管理方案负责
class UserState {
  final User? user;
  final bool isLoading;
}

这样路由逻辑和业务逻辑就不会互相干扰。

页面生命周期统一管理

不同平台的生命周期管理方式不同,但我们可以抽象出一套统一的接口:

abstract class PageLifecycle {
  void onPageCreate();
  void onPageShow();
  void onPageHide();
  void onPageDestroy();
}

这样在不同平台上实现这个接口,就可以统一管理页面生命周期。

总结

Flutter、React Native、Web 和 iOS 的路由系统各有特点,也各有痛点。Flutter 的路由栈设计简单但不够灵活,RN 的路由状态管理复杂,Web 的路由和状态耦合严重,iOS 的路由稳定但不够灵活。

在实际开发中,我们应该根据项目的特点选择合适的导航方案,并且通过统一的路由配置、路由中间件、状态管理分离等方式来提高代码的可维护性。理解不同平台路由系统的本质差异,可以帮助我们更好地设计和实现跨平台应用。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐