Jetpack Compose中的Navigation从入门到精通完全指南

Jetpack Compose中的Navigation从入门到精通完全指南

什么是Android导航

导航帮助您理解应用程序在不同组件间的移动方式。

Android JetPack Navigation可以帮助您以简单的方式实现高级导航。

导航组件由三个主要部分组成:

导航图(Navigation Graph): 这是一个资源,它将所有与导航相关的数据集中在一起。这包括应用程序中的所有位置(称为目标位置)以及用户在应用程序中可能采取的路径。它就像一本大书,里面包含了您在应用程序中可以访问的所有地方以及如何在它们之间移动的方法。可以将其视为地图和指南的结合。
NavHost: 这是一个独特的可组合项,您可以将其包含在布局中。它显示来自您的导航图的各个目标位置。NavHost将NavController与指定可组合项目标位置的导航图链接起来。当您在可组合项之间导航时,NavHost的内容将自动重新组合。导航图中的每个可组合项目标位置都与一个路由关联。
NavController: NavController是导航组件的中央API。它具有状态并跟踪组成应用程序屏幕的可组合项的后退栈以及每个屏幕的状态。

Jetpack Compose中的导航

导航组件为Jetpack Compose应用程序提供了支持。您可以在利用导航组件的基础设施和功能的同时,在可组合项之间进行导航。

要开始在Jetpack Compose中进行导航,您需要在项目的build.gradle文件中包含所需的依赖项:

implementation "androidx.navigation:navigation-compose:2.7.1"

Jetpack Compose中导航的基本概念。

NavController
NavController是导航组件的中央API。它具有状态并跟踪组成应用程序屏幕的可组合项的后退栈以及每个屏幕的状态。

您可以通过在可组合项中使用rememberNavController()方法来创建一个NavController:

val navController = rememberNavController()

您应该在可组合项层次结构中的一个位置创建NavController,并确保所有需要引用它的可组合项都能够访问它。这遵循状态提升的原则,并允许您使用NavController和通过currentBackStackEntryAsState()提供的状态作为更新屏幕外的可组合项的真实数据源。有关此功能的示例,请参阅与底部导航栏集成。

注意:如果您在片段中使用导航组件,您无需在Compose中定义新的导航图或使用NavHost可组合项。有关更多信息,请参阅互操作性部分。

NavHost
每个NavController必须与一个NavHost可组合项关联。NavHost将NavController与指定应该能够在其之间导航的可组合项目标位置的导航图链接起来。当您在可组合项之间导航时,NavHost的内容将自动重新组合。导航图中的每个可组合项目标位置都与一个路由相关联。

关键术语:路由是一个字符串,用于定义到达特定可组合项的路径。可以将其视为指向特定目的地的隐式深层链接。每个目的地应具有唯一的路由。

创建NavHost需要之前通过rememberNavController()创建的NavController以及您的导航图的起始目的地的路由。NavHost的创建使用了导航Kotlin DSL中的lambda语法来构建导航图。您可以使用composable()方法来添加导航结构。该方法要求您提供一个路由和应与目的地关联的可组合项:

NavHost(navController = navController, startDestination = "profile") {composable("profile") { Profile(/*...*/) }composable("friendslist") { FriendsList(/*...*/) }/*...*/
}

注意:导航组件要求您遵循导航原则并使用固定的起始目的地。您不应该对startDestination路由使用可组合项值。

示例:如何设置导航图、NavHostNavigationItem

步骤1:在一个文件中定义导航的屏幕名称和路由,例如AppNavigation.kt

enum class Screen {HOME,    LOGIN,
}
sealed class NavigationItem(val route: String) {object Home : NavigationItem(Screen.HOME.name)object Login : NavigationItem(Screen.LOGIN.name)
}

步骤2:定义NavHost,比如AppNavHost.kt

@Composable
fun AppNavHost(modifier: Modifier = Modifier,navController: NavHostController,startDestination: String = NavigationItem.Splash.route,... // other parameters
) {NavHost(modifier = modifier,navController = navController,startDestination = startDestination) {composable(NavigationItem.Splash.route) {SplashScreen(navController)}composable(NavigationItem.Login.route) {LoginScreen(navController)}
}

步骤3:在你的MainActivity.kt中调用AppNavHost

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {AutoPartsAppTheme {Surface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {AppNavHost(navController = rememberNavController())}}}}
}

导航参数:
导航Compose还支持在可组合项目标位置之间传递参数。为了实现这一点,您需要向路由添加参数占位符,类似于在使用基础导航库时向深层链接添加参数的方式:

使用情况:

  1. 无参数
  2. 使用简单参数,如预定义的数据类型(例如Int、String等)
  3. 使用复杂参数,如用户定义的数据类型
  4. 可选参数
  5. 返回结果的导航

无参数:

NavHost(navController = navController, startDestination = "profile") {composable("profile") { Profile(/*...*/) }composable("friendslist") { FriendsList(/*...*/) }/*...*/
}

使用简单参数:
默认情况下,所有参数都会被解析为字符串。composable()的arguments参数接受NamedNavArguments列表。您可以使用navArgument方法快速创建NamedNavArgument,并指定其确切类型:

NavHost(startDestination = "profile/{userId}") {...composable("profile/{userId}") {...}
}

默认情况下,所有参数都会被解析为字符串。composable()的arguments参数接受NamedNavArguments列表。您可以使用navArgument方法快速创建NamedNavArgument,并指定其确切类型:

NavHost(startDestination = "profile/{userId}") {...composable("profile/{userId}",arguments = listOf(navArgument("userId"){type = NavType.StringType })) {...}
}

您应该从composable()函数的lambda中可用的NavBackStackEntry中提取参数。

composable("profile/{userId}") { backStackEntry ->val userId = backStackEntry.arguments?.getString("userId")// 在这里获取用户数据Profile(navController, // 传递获取的用户数据,如UserInfo)
}

要将参数传递给目标位置,需要在进行导航调用时将其附加到路由中:

navController.navigate("profile/user1234")

有关支持的类型列表,请参阅在目的地之间传递数据。

使用复杂参数或用户定义的参数:
强烈建议在导航时不要传递复杂的数据对象,而是在执行导航操作时将最小必要的信息作为参数传递,例如唯一标识符或其他形式的ID:

// 仅在导航到新目标时传递用户ID作为参数
navController.navigate("profile/user1234")

复杂对象应存储为单一真相源(例如数据层)中的数据。导航到目的地后,您可以使用传递的ID从单一真相源中加载所需信息。要检索ViewModel中的参数,负责访问数据层的ViewModel,您可以使用ViewModel的SavedStateHandle

class UserViewModel(savedStateHandle: SavedStateHandle,private val userInfoRepository: UserInfoRepository
) : ViewModel() {private val userId: String = checkNotNull(savedStateHandle["userId"])// Fetch the relevant user information from the data layer,// ie. userInfoRepository, based on the passed userId argumentprivate val userInfo: Flow<UserInfo> = userInfoRepository.getUserInfo(userId)--------------- OR -----------------// fetch data from network or database    private val _dataFlow =MutableStateFlow<UserInfo>(userInfoRepository.getUserInfo(userId))val dataFlow get() = _dataFlow.asStateFlow()
}

Composable函数

//Navhost 
composable("profile/{userId}") { backStackEntry ->val userId = backStackEntry.arguments?.getString("userId")// here you have to fetch user data val userInfo by taskViewModel.dataFlow.collectAsState()   Profile(navController, userInfo)
}// Profile screen
@Composable
fun Profile(navController: NavController, userInfo:UserInfo){// do you work here
}

注意:将ViewModel放在可组合屏幕之外,因为预览将无法工作,并且最佳实践是避免可组合和ViewModel之间的耦合。

这种方法有助于防止在配置更改期间数据丢失以及更新或改变对象时出现任何不一致性。

有关为什么应避免将复杂数据作为参数传递的更详细解释,以及支持的参数类型列表,请参阅在目的地之间传递数据。

添加可选参数

Navigation Compose还支持可选导航参数。可选参数与必需参数有两点不同:

必须使用查询参数语法("?argName={argName}")进行包含
必须设置defaultValue,或者设置nullable = true(隐式地将默认值设置为null)
这意味着所有可选参数都必须显式地添加到composable()函数中作为列表:

composable("profile?userId={userId}/{isMember}",arguments = listOf(navArgument("userId") {type = NavType.StringTypedefaultValue = "user1234"// OR type = NavType.StringTypenullable = true},navArgument("isNewTask") {type = NavType.BoolType})
) { backStackEntry ->val userId = backStackEntry.arguments?.getString("userId")val isMember = backStackEntry.arguments?.getBoolean("isMember")?:falseProfile(navController, userId, isMember)
}

现在,即使没有传递参数给目标,defaultValue = "user1234"也会被使用。

通过路由处理参数的结构意味着您的可组合部件完全独立于导航,并使它们更容易进行测试。

带有结果的返回导航

带有结果的返回导航是最常见的任务。例如,当您打开筛选对话框并选择筛选条件,然后带着所选的筛选条件返回导航到应用这些筛选条件到屏幕上。

有两个屏幕。1. FirstScreen(第一个屏幕)和2. SecondScreen(第二个屏幕)。我们需要从SecondScreen(第二个屏幕)获取数据到FirstScreen(第一个屏幕)。

NavHost.kt:设置导航图。

val navController = rememberNavController()NavHost(navController = navController,startDestination = "firstscreen") {composable("firstscreen") {FirstScreen(navController)}composable("secondscreen") {SecondScreen(navController)}
}
@Composable
fun FirstScreen(navController: NavController) {// Retrieve data from next screenval msg = navController.currentBackStackEntry?.savedStateHandle?.get<String>("msg")Column(Modifier.fillMaxSize(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {Button(onClick = { navController.navigate("secondscreen") }) {Text("Go to next screen")}Spacer(modifier = Modifier.height(8.dp))msg?.let {Text(it)}}
}

FirstScreen.kt: 利用NavController当前返回堆栈条目的savedStateHandle在从SecondScreen返回后检索数据。

@Composable
fun FirstScreen(navController: NavController) {// Retrieve data from next screenval msg = navController.currentBackStackEntry?.savedStateHandle?.get<String>("msg")Column(Modifier.fillMaxSize(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {Button(onClick = { navController.navigate("secondscreen") }) {Text("Go to next screen")}Spacer(modifier = Modifier.height(8.dp))msg?.let {Text(it)}}
}

SecondScreen.kt: 将数据放入NavController的前一个返回堆栈条目的savedStateHandle中。

@Composable
fun SecondScreen(navController: NavController) {var text by remember {mutableStateOf("")}Column(Modifier.fillMaxSize(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {TextField(value = text, onValueChange = { text = it },placeholder = {Text("Enter text", color = Color.Gray)})Spacer(Modifier.height(8.dp))Button(onClick = {// Put data into savedStateHandle to retrive data on the previous screennavController.previousBackStackEntry?.savedStateHandle?.set("msg", text)navController.popBackStack()}) {Text(text = "Submit")}}
}

https://github.com/KaushalVasava/JetPackCompose_Basic/tree/navigate-back-with-result

Deep links

Navigation Compose支持将隐式深链接定义为composable()函数的一部分。它的deepLinks参数接受一个NavDeepLinks列表,可以使用navDeepLink方法快速创建:

val uri = "https://www.example.com"
composable("profile?id={id}",deepLinks = listOf(navDeepLink { uriPattern = "$uri/{id}" })
) { backStackEntry ->Profile(navController, backStackEntry.arguments?.getString("id"))
}

这些深链接可以将特定的URL、操作或MIME类型与可组合关联起来。默认情况下,这些深链接不会向外部应用程序公开。要使这些深链接对外部可用,必须在应用程序的manifest.xml文件中添加适当的元素。为了启用上述深链接,您应该在清单文件的元素内添加以下内容:

<activity ><intent-filter>...<data android:scheme="https" android:host="www.example.com" /></intent-filter>
</activity>

当深链接被其他应用触发时,导航会自动进入到该可组合。

这些相同的深链接也可以用于从可组合中构建具有适当深链接的PendingIntent:

val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(Intent.ACTION_VIEW,"https://www.example.com/$id".toUri(),context,MyActivity::class.java
)
val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {addNextIntentWithParentStack(deepLinkIntent)getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

然后,您可以像其他任何PendingIntent一样使用这个deepLinkPendingIntent来打开您的应用程序的深链接目标位置。

Nested Navigation


目标可以分组为嵌套图形,以模块化您应用程序界面中的特定流程。一个例子就是一个独立的登录流程。

嵌套图形将其目标分组,就像主图形一样,并且它需要一个指定的起始目标作为其关联路由,当访问该嵌套图形的路由时,您将进入该起始目标。

要将嵌套图形添加到NavHost中,可以使用导航扩展函数:

NavHost(navController, startDestination = "home") {...// 通过其路由('login')导航到图形会自动导航到图形的起始目标 - 'username'// 因此封装了图形的内部路由逻辑navigation(startDestination = "username", route = "login") {composable("username") { ... }composable("password") { ... }composable("registration") { ... }}...
}

强烈建议随着图形的增长,将导航图形拆分为多个方法。这还允许多个模块贡献自己的导航图形。

fun NavGraphBuilder.loginGraph(navController: NavController) {navigation(startDestination = "username", route = "login") {composable("username") { ... }composable("password") { ... }composable("registration") { ... }}
}

通过将该方法作为NavGraphBuilder的扩展方法,您可以与预构建的导航、composable和dialog扩展方法一起使用:

NavHost(navController, startDestination = "home") {...loginGraph(navController)...
}

示例:

val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {composable("about") {}navigation(startDestination = "login",route = "auth") {composable("login") {val viewModel = it.sharedViewModel<SampleViewModel>(navController)Button(onClick = {navController.navigate("calendar") {popUpTo("auth") {inclusive = true}}}) {}}composable("register") {val viewModel = it.sharedViewModel<SampleViewModel>(navController)} composable("forgot_password") {val viewModel = it.sharedViewModel<SampleViewModel>(navController)}}navigation(startDestination = "calendar_overview",route = "calendar") {composable("calendar_overview") { }composable("calendar_entry") { }}}

NavBackStack扩展函数

@Composable
inline fun <reified T : ViewModel> NavBackStackEntry.sharedViewModel(navController: NavController): T {val navGraphRoute = destination.parent?.route ?: return viewModel()val parentEntry = remember(this) {navController.getBackStackEntry(navGraphRoute)}return viewModel(parentEntry)
}

与bottom nav bar的集成

通过在可组合层次结构的较高级别定义NavController,您可以将导航与其他组件(如底部导航组件)连接起来。这样做可以通过选择底部栏中的图标进行导航。

要使用BottomNavigationBottomNavigationItem组件,请将androidx.compose.material依赖项添加到Android应用程序中。

dependencies {implementation "androidx.compose.material:material:1.5.1"
}
android {buildFeatures {compose true}composeOptions {kotlinCompilerExtensionVersion = "1.5.3"}kotlinOptions {jvmTarget = "1.8"}
}

为了将底部导航栏中的项链接到导航图中的路由,建议定义一个sealed class(例如这里的Screen),其中包含目标的路由和String资源ID。

sealed class Screen(val route: String, @StringRes val resourceId: Int) {object Profile : Screen("profile", R.string.profile)object FriendsList : Screen("friendslist", R.string.friends_list)
}

然后将这些项放入可以供BottomNavigationItem使用的列表中:

val items = listOf(Screen.Profile,Screen.FriendsList,
)

BottomNavigation的可组合函数中,使用currentBackStackEntryAsState()函数获取当前的NavBackStackEntry。该条目可让您访问当前的NavDestination。然后,可以通过比较项的路由与当前目标及其父目标的路由(以处理使用嵌套导航时的情况)来确定每个BottomNavigationItem的选中状态,通过NavDestination层次结构。

项的路由还用于将onClick lambda连接到导航调用,以便点击该项时导航到该项。通过使用saveStaterestoreState标志,正确保存和恢复该项的状态和返回堆栈,以便在底部导航项之间切换时进行操作。

val navController = rememberNavController()
Scaffold(bottomBar = {BottomNavigation {val navBackStackEntry by navController.currentBackStackEntryAsState()val currentDestination = navBackStackEntry?.destinationitems.forEach { screen ->BottomNavigationItem(icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },label = { Text(stringResource(screen.resourceId)) },selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,onClick = {navController.navigate(screen.route) {// Pop up to the start destination of the graph to// avoid building up a large stack of destinations// on the back stack as users select itemspopUpTo(navController.graph.findStartDestination().id) {saveState = true}// Avoid multiple copies of the same destination when// reselecting the same itemlaunchSingleTop = true// Restore state when reselecting a previously selected itemrestoreState = true}})}}}
) { innerPadding ->NavHost(navController, startDestination = Screen.Profile.route, Modifier.padding(innerPadding)) {composable(Screen.Profile.route) { Profile(navController) }composable(Screen.FriendsList.route) { FriendsList(navController) }}
}

在这里,您可以利用NavController.currentBackStackEntryAsState()方法来提升navController状态,将其从NavHost函数中提取出来,并与BottomNavigation组件共享。这意味着BottomNavigation会自动具有最新的状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/141740.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

前端关于对象中套用对象传参的小问题

在js的对象是引用类型的&#xff0c;他如果里面还套用对象的话那么通过axios传参给后端就会出现一个问题&#xff0c;就是【object&#xff0c;object】这种包装形式 那么如何来解决这个问题呢&#xff1f; 其实这就是要对数据传输中json格式要有一定的了解才可以解决这个问题…

【李沐深度学习笔记】线性代数

课程地址和说明 线性代数p1 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 线性代数 标量 标量&#xff08;scalar&#xff09;&#xff0c;亦称“无向量”。有些物理量&#xff0c;只具有数值大小&#xff0c…

低功耗无线扫描唤醒技术,重塑物联网蓝牙新体验

随着人类社会活动的信息化和通信技术的发展&#xff0c;传统设施越来越倾向于网络化、无线化。物联网被人们视为继计算机、互联网之后信息技术产业发展的第三次革命。无线短距离通信方式是物联网的主要通信方式之一&#xff0c;随着物联网终端通信设备应用越来越广&#xff0c;…

AIGC专栏7——EasyPhoto 人像训练与生成原理详解

AIGC专栏7——EasyPhoto 人像训练与生成原理详解 学习前言源码下载地址为什么是LoraEasyPhoto的训练流程1、数据的预处理a、人像排序i、人脸特征向量提取过程ii、人脸偏移角度计算iii、人像排序 b、人像分割与修复i、人像分割ii、图像修复与超分处理 2、Lora模型训练a、训练的基…

Python爬虫自动切换爬虫ip的完美方案

在进行网络爬虫时&#xff0c;经常会遇到需要切换爬虫ip的情况&#xff0c;以绕过限制或保护自己的爬虫请求。今天&#xff0c;我将为你介绍Python爬虫中自动切换爬虫ip的终极方案&#xff0c;让你的爬虫更加高效稳定。 步骤一&#xff1a;准备爬虫ip池 首先&#xff0c;你需要…

二值贝叶斯滤波计算4d毫米波聚类目标动静属性

机器人学中有些问题是二值问题&#xff0c;对于这种二值问题的概率评估问题可以用二值贝叶斯滤波器binary Bayes filter来解决的。比如机器人前方有一个门&#xff0c;机器人想判断这个门是开是关。这个二值状态是固定的&#xff0c;并不会随着测量数据变量的改变而改变。就像门…

mysql用事务实现更新数据

前言&#xff1a;在手动批量更新正式环境数据库时&#xff0c;建议使用事物进行更新&#xff0c;避免更错数据&#xff0c;造成不必要的麻烦。 现表中有三条数据&#xff0c;使用事物批量将name字段为mgx&#xff0c;phone字段为17837107346&#xff0c;所有数据中的address字段…

解决Vue设置图片的动态src不生效的问题

一、问题描述 在vue项目中&#xff0c;想要动态设置img的src时&#xff0c;此时发现图片会加载失败。在Vue代码中是这样写的&#xff1a; 在Vue的data中是这样写的&#xff1a; 我的图片在根目录下的static里面&#xff1a; 但是在页面上这个图片却无法加载出来。 二、解决方案…

抖音SEO矩阵系统源码开发搭建

1. 确定需求和功能&#xff1a;明确系统的主要目标和需要实现的功能&#xff0c;包括关键词研究、短视频制作、外链建设、数据分析、账号设置优化等方面。 2. 设计系统架构&#xff1a;根据需求和功能确定系统的架构&#xff0c;包括前端、后端、数据库等部分的设计&#xff0…

惠普P1108激光打印机报错光束探测错误检修

在使用打印机的过程中&#xff0c;有时候会遇到光束探测错误的问题&#xff0c;导致打印机无法正常工作。这个问题可能是由多种原因引起的&#xff0c;包括硬件故障、驱动程序错误、操作系统问题等。在这里&#xff0c;我将为您提供一些解决光束探测错误的方法。 故障描述&…

Spring Boot:控制器调用模板引擎渲染数据的基本过程

目录 基础知识注解&#xff1a; Controller方法&#xff1a;RequestMapping 基本过程添加 FreeMarker 依赖创建控制器方法创建 FTL 文件 基础知识 注解&#xff1a; Controller 控制器注解&#xff0c;表示这个类是一个控制器类&#xff0c;里面定义了一些处理客户端请求的方…

Springcloud实战之自研分布式id生成器

一&#xff0c;背景 日常开发中&#xff0c;我们需要对系统中的各种数据使用 ID 唯一表示&#xff0c;比如用户 ID 对应且仅对应一个人&#xff0c;商品 ID 对应且仅对应一件商品&#xff0c;订单 ID 对应且仅对应 一个订单。我们现实生活中也有各种 ID &#xff0c;比如身…

TCP 和 UDP哪个更好

传输控制协议 &#xff08;TCP&#xff09; 和用户数据报协议 &#xff08;UDP&#xff09; 是互联网的基础支柱&#xff0c;支持从网络源到目的地的不同类型的数据传输。TCP更可靠&#xff0c;而UDP优先考虑速度和效率。本文解释了两种协议的工作原理&#xff0c;并详细讨论了…

ChunJun: 自定义插件

序言 Chunjun的版本兼容可能会有问题,在我们了解了自定义插件后,在修改源码以应对不同的场景就会得心应手了,针对Chunjun1.12.Release版本说明cuiyaonan2000163.com 自定义插件整体流程 从数据流的角度来看ChunJun&#xff0c;可以理解为不同数据源的数据流通过对应的ChunJu…

单元测试 —— JUnit 5 参数化测试

JUnit 5参数化测试 目录 设置我们的第一个参数化测试参数来源 ValueSourceNullSource & EmptySourceMethodSourceCsvSourceCsvFileSourceEnumSourceArgumentsSource参数转换参数聚合奖励总结 如果您正在阅读这篇文章&#xff0c;说明您已经熟悉了JUnit。让我为您概括一下…

使用原生html<table>构造复杂table表

<table border data-sort"sortDisabled" align"center" class"table"><tbody><tr class"textarea1"><td rowspan"1" colspan"2" class"background-gray"><label>日期<…

Fiddler抓取Https请求配置

官网&#xff1a;https://www.telerik.com/fiddler 配置抓取https包 1.Tools->Options->Https&#xff0c;勾选下面。 2.Actions ->Trust Root Certificate.安装证书到本地 3.在手机端设置代理&#xff1a;本机ip如&#xff1a;192.168.1.168 端口号:8888。 4.手机…

Vue中的自定义指令详解

文章目录 自定义指令自定义指令-指令的值&#xff08;给自定义指令传参数&#xff09;自定义指令- v-loading指令封装 自定义指令 自定义指令&#xff1a;自己定义的指令&#xff0c;可以封装一些dom 操作&#xff0c;扩展额外功能&#xff08;自动聚焦&#xff0c;自动加载&a…

@DateTimeFormat 和 @JsonFormat 的详细研究

关于这两个时间转化注解&#xff0c;先说结论 一、介绍 1、DateTimeFormat DateTimeFormat 并不会根据得到其属性 pattern 把前端传入的数据转换成自己想要的格式&#xff0c;而是将前端的String类型数据封装到Date类型&#xff1b;其次它的 pattern 属性是用来规范前端传入…

el-select 下拉框全选、多选的几种方式组件

组件一、基础多选 适用性较广的基础多选&#xff0c;用 Tag 展示已选项 <template><el-select v-model"value1" multiple placeholder"请选择"><el-optionv-for"item in options":key"item.value":label"item.la…