您现在的位置是:首页 >技术教程 >从React Native,Flutter到小程序(八)路由网站首页技术教程

从React Native,Flutter到小程序(八)路由

SpringHeather 2024-06-14 17:17:50
简介从React Native,Flutter到小程序(八)路由

React Native

StackNavigator 命名路由


import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

const LoginScreen = ({ navigation }) => {
  // 登录钩子
  // 判断用户是否已登录,如果是则导航到主屏幕 MainScreen,并传递用户登录信息,否则继续停留在这个登录屏幕 LoginScreen
  const isLoggedIn = true; // 假设当前用户已登录
  if (isLoggedIn) {
    const userInfo = {
      name: "Alice",
      email: "alice@example.com",
      loggedInAt: new Date()
    }
    navigation.navigate('MainScreen', userInfo);
  }

  return (
    // 显示登录表单和逻辑
    <View><Text>Login Screen</Text></View>
  );
};

const MainScreen = ({ route }) => {
  // 读取路由参数中的用户登录信息
  const { name, email, loggedInAt } = route.params;

  return (
    <View>
      <Text>Main Screen</Text>
      <Text>Name: {name}</Text>
      <Text>Email: {email}</Text>
      <Text>Logged in at: {loggedInAt.toString()}</Text>
    </View>
  );
};

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen
          name="LoginScreen"
          component={LoginScreen}
          options={{ title: '登录' }}
        />
        <Stack.Screen
          name="MainScreen"
          component={MainScreen}
          options={{ title: '主界面' }}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

export default App;

在这个示例代码中,我们在 LoginScreen 组件中添加了一个路由参数对象 userInfo。如果用户已经登录,则将 userInfo 对象作为参数传递给 MainScreen 屏幕组件。具体而言,在 navigation.navigate 函数中,我们首先指定目标屏幕名称为 ‘MainScreen’,然后在第二个参数(可选参数)中传递路由参数 userInfo。

在 MainScreen 组件中,我们使用 route.params 访问路由参数对象,并取出其中的用户登录信息。最后将这些信息以文本形式渲染到屏幕上

TabBar

import React from 'react';
import { View } from 'react-native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

// 创建 BottomTabNavigator 组件
const Tab = createBottomTabNavigator();

// 用于导航的几个 Tab 页面组件
function HomeScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      {/* 这里把导航内容随便写了一些 */}
      <Text>Home Screen</Text>
    </View>
  );
}

function ProfileScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Profile Screen</Text>
    </View>
  );
}

function SettingsScreen() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Settings Screen</Text>
    </View>
  );
}

// 导出 TabBar
export default function App() {
  return (
    // 配置底部 TabBar 的样式
    <Tab.Navigator tabBarOptions={{activeTintColor: '#e91e63'}}>
      {/* 配置每个 Tab 图标和标题 */}
      <Tab.Screen name="Home" component={HomeScreen} options={{tabBarLabel: 'Home', tabBarIcon: ({ color, size }) => (<Icon type="material-community" name="home-outline" color={color} size={size} />)}} />
      <Tab.Screen name="Profile" component={ProfileScreen} options={{tabBarLabel: 'Profile', tabBarIcon: ({ color, size }) => (<Icon type="material-community" name="account-outline" color={color} size={size} />)}} />
      <Tab.Screen name="Settings" component={SettingsScreen} options={{tabBarLabel: 'Settings', tabBarIcon: ({ color, size }) => (<Icon type="material-community" name="settings-outline" color={color} size={size} />)}} />
    </Tab.Navigator>
  );
}

请注意,您需要在代码中导入所需的库和组件(例如 react-navigation 和 react-native-vector-icons)。在这里,我们使用了 createBottomTabNavigator 组件来创建底部 TabBar,并且为每个 Tab 页面设置了图标和标题。如果您需要进行更多自定义,可以参考官方文档对 tabBarOptions 属性的其他配置选项。

Flutter

匿名路由

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Anonymous Routing Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go to New Page'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => MyNewPage(),
                fullscreenDialog: true,
              ),
            );
          },
        ),
      ),
    );
  }
}

class MyNewPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('New Page'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go back'),
          onPressed: () {
            Navigator.pop(context); // 关闭匿名路由
          },
        ),
      ),
    );
  }

  bool get maintainState => true; // 保持匿名路由状态

  bool get opaque => false;

  bool get canPop => true; // 允许关闭匿名路由
}

在这个例子中,我们实现了两个页面:MyHomePage和MyNewPage。MyHomePage是应用程序的起始页,其中包含一个按钮,当点击该按钮时,它会打开MyNewPage作为匿名路由页面。MyNewPage 包含一个按钮,当点击该按钮时,它会关闭当前打开的匿名路由页面。

请注意,在MyNewPage类中,我们实现了三个方法:maintainState、opaque和canPop。这些方法定义了我们要使用哪种状态保持机制、是否渲染 widget 透明背景以及页面能否被关闭等属性。

命名路由

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Named Routing Demo',
      initialRoute: '/',
      routes: {
        '/': (context) => MyHomePage(),
        '/newpage': (context) => MyNewPage(),
      },
    );
  }
}

class MyHomePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go to New Page'),
          onPressed: () {
            Navigator.pushNamed(context, '/newpage');
          },
        ),
      ),
    );
  }
}

class MyNewPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('New Page'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Go back'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

在这个例子中,我们使用MaterialApp的initialRoute属性来定义应用程序的起始路由,并使用routes属性来定义命名路由表。

在routes属性中,我们将路由名’/‘映射到MyHomePage widget,并将路由名’/newpage’映射到MyNewPage widget。当用户从主页点击按钮时,我们调用了Flutter提供的Navigator.pushNamed()方法来跳转到命名为’/newpage’的路由。当用户在MyNewPage页面中点击返回按钮时,我们可以使用Navigator.pop(context)方法关闭当前页面并返回上一个路由。

请注意,不同于匿名路由例子,这里通过设置路由名映射到相应的Widget类,可以更加灵活和易于管理复杂的应用程序结构。

import 'package:flutter/material.dart';

class LoginScreen extends StatelessWidget {
  const LoginScreen({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    final isLoggedIn = true; // 假设当前用户已登录
    if (isLoggedIn) {
      final userInfo = {
        'name': 'Alice',
        'email': 'alice@example.com',
        'loggedInAt': DateTime.now(),
      };
      Navigator.pushNamed(context, '/main', arguments: userInfo);
    }

    return Scaffold(
      appBar: AppBar(title: const Text('登录')),
      body: const Center(child: Text('Login Screen')),
    );
  }
}

class MainScreen extends StatelessWidget {
  const MainScreen({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    final arguments =
        ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>;
    final name = arguments['name'];
    final email = arguments['email'];
    final loggedInAt = arguments['loggedInAt'];

    return Scaffold(
      appBar: AppBar(title: const Text('主界面')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Main Screen'),
            Text('Name: $name'),
            Text('Email: $email'),
            Text('Logged in at: $loggedInAt'),
          ],
        ),
      ),
    );
  }
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/login',
      onGenerateRoute: (settings) {
        final name = settings.name;
        final arguments = settings.arguments;

        switch (name) {
          case '/login':
            return MaterialPageRoute(builder: (_) => LoginScreen());
          case '/main':
            return MaterialPageRoute(builder: (_) => MainScreen(), settings: RouteSettings(arguments: arguments));
        }
      },
    );
  }
}

在这个Flutter版本中,我们使用了MaterialApp作为根部件,并在onGenerateRoute回调中通过处理settings来在不同的路由之间进行导航。在LoginScreen窗口中,只需将页面名称设置为’main’并传递用户信息参数,即可在MainScreen窗口中展示。

Tab Bar

Flutter 中使用 TabBar 组件的示例,包含了不同类型的 Tab 对应不同的页面,并且可以滑动切换:

import 'package:flutter/material.dart';

class MyTabbedPage extends StatefulWidget {
  
  _MyTabbedPageState createState() => _MyTabbedPageState();
}

class _MyTabbedPageState extends State<MyTabbedPage> with SingleTickerProviderStateMixin {

  late final TabController _tabController;

  
  void initState() {
    super.initState();
    _tabController = TabController(length: 4, vsync: this);
  }

  
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('My Tabbed Page'),
        bottom: TabBar(
          controller: _tabController,
          isScrollable: true, // 允许 TabBar 滚动
          tabs: [
            Tab(
              icon: Icon(Icons.directions_car),
              child: Text('Cars'),
            ),
            Tab(
              icon: Icon(Icons.flight),
              child: Text('Flights'),
            ),
            Tab(
              icon: Icon(Icons.hotel),
              child: Text('Hotels'),
            ),
            Tab(
              icon: Icon(Icons.event),
              child: Text('Events'),
            ),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          Center(child: Text('This is the cars page')),
          Center(child: Text('This is the flights page')),
          Center(child: Text('This is the hotels page')),
          ListView.builder( // 构建一个 ListView 作为最后一个 Tab 的内容
            itemCount: 20, // 随便写点数据
            itemBuilder: (BuildContext context, int index) {
              return ListTile(
                leading: Icon(Icons.event),
                title: Text('Event $index'),
                subtitle: Text('This is the event detail.'),
              );
            },
          ),
        ],
      ),
    );
  }
}

这个例子中有四个 Tab,分别对应了不同的页面(前三个是简单的 Center Widget,最后一个是包含了 ListView.builder 的 Widget);同时我们允许 TabBar 可以滚动,以便用户可以在更小的屏幕上轻松地查看和选择不同的 Tab。

微信小程序

目录结构

  • app.js // 全局变量、生命周期等
  • app.json // 应用级别配置文件
  • pages/ // 存放页面相关文件
    • index/ // 首页页面目录
      • index.js // 页面逻辑代码
      • index.wxml // 页面视图结构代码
      • index.wxss // 页面样式代码
    • detail/ // 详情页页面目录
      • detail.js // 页面逻辑代码
      • detail.wxml // 页面视图结构代码
      • detail.wxss // 页面样式代码
  • utils/ // 存放工具类函数文件
    • util.js // 工具类函数代码
      其中,app.js为全局入口文件;app.json为应用级别的配置文件,例如定义应用的窗口背景色、页面路径等;pages目录存放所有的页面相关文件夹,每个文件夹包含页面的js、wxml、wxss代码(可选);utils目录存放所有工具类的函数文件。

命名路由

下面是一个简单的示例,包括了app.js、index.js、detail.js以及app.json等文件的内容,希望能够帮到您理解路由的实现方式:

app.js:

App({
  globalData: {
    pages: ["pages/index/index", "pages/detail/detail"]
    // 存储所有页面地址的数组
  }
})

app.json:

{
  "pages": [
    "pages/index/index",
    "pages/detail/detail"
  ],
  "window": {
    "navigationBarTitleText": "Demo"
  }
}

index.js:

Page({
  jumpToDetail() {
    wx.navigateTo({
      url: '/pages/detail/detail'
    })
  }
})

detail.js:

javascript
Page({
  backToIndex() {
    wx.navigateBack()
  }
})

这里使用了wx.navigateBack方法来返回首页,其它方法也可以用wx.redirectTo或wx.switchTab等。

同时需要注意,在使用wx.navigateTo等跳转API时,应该检查页面栈是否已满(超过10层),否则可能会导致跳转失败。另外,目标页面的路径都应写在app.js中全局变量的pages数组中,避免出错。

三个 API 的区别

(1)wx.navigateTo
保留当前页面,跳转到其他页面。
被跳转的页面新建入栈,当前页面仍然存在于栈中。
用户可以通过左上角返回按钮或手势返回到前一个页面,没有多余操作可达到关闭被跳转页面的效果。

(2)wx.redirectTo
关闭当前页面,跳转到目标页。
被跳转页面替换当前页面,当前页面从栈中移出,再无法返回。类似于浏览器的"无法返回之前页面"效果
在打开某些页面时(例如支付结果页),我们可能希望用户不能再次回到之前的页面,此时可以使用 redirectTo。

(3)wx.switchTab
跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。
只能跳转到 tarBar 中已经配置的页面,且只能关闭非 tabBar 页面,不能关闭 tarBar 页面。常用于跨模块页面跳转。

app.json 中可以配置 tabBar,示例代码如下:

{
  "pages": [
    "pages/index/index",
    "pages/about/about",
    "pages/mine/mine"
  ],
  "tabBar": {
    "color": "#333",
    "selectedColor": "#4CAF50",
    "backgroundColor": "#fff",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "/icons/index.png",
        "selectedIconPath": "/icons/index_selected.png"
      },
      {
        "pagePath": "pages/about/about",
        "text": "关于我们",
        "iconPath": "/icons/about.png",
        "selectedIconPath": "/icons/about_selected.png"
      },
      {
        "pagePath": "pages/mine/mine",
        "text": "个人中心",
        "iconPath": "/icons/mine.png",
        "selectedIconPath": "/icons/mine_selected.png"
      }
    ]
  }
}

在 app.json 文件中,我们可以通过 “tabBar” 字段来配置 tabBar。其中,“color”表示未选中的 Tab 标签文字颜色,“selectedColor” 表示选中的文字颜色,“backgroundColor”表示 tabBar 背景色,“list”表示 tabBar 的列表项数组。

在各自的页面目录下,也可以通过覆盖 onTabItemTap() 方法来监听 Tab 切换事件,示例代码如下:

// pages/index/index.js
Page({
  onTabItemTap(item) {
    console.log(item.index)
    console.log(item.pagePath)
    console.log(item.text)
  }
})

// pages/about/about.js
Page({
  onTabItemTap(item) {
    console.log(item.index)
    console.log(item.pagePath)
    console.log(item.text)
  }
})

// pages/mine/mine.js
Page({
  onTabItemTap(item) {
    console.log(item.index)
    console.log(item.pagePath)
    console.log(item.text)
  }
})

在各自的页面中,我们覆盖了 onTabItemTap() 方法,在该方法中可以获取到当前选中的 tabBar 列表项信息,包括“index”、“pagePath”和“text”。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。