您现在的位置是:首页 >技术教程 >从React Native,Flutter到小程序(八)路由网站首页技术教程
从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 // 页面样式代码
- index/ // 首页页面目录
- utils/ // 存放工具类函数文件
- util.js // 工具类函数代码
其中,app.js为全局入口文件;app.json为应用级别的配置文件,例如定义应用的窗口背景色、页面路径等;pages目录存放所有的页面相关文件夹,每个文件夹包含页面的js、wxml、wxss代码(可选);utils目录存放所有工具类的函数文件。
- util.js // 工具类函数代码
命名路由
下面是一个简单的示例,包括了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”。