鸿蒙技术分享:Navigation页面管理-鸿蒙@fw/router框架源码解析(二)
本文是系列文章,其他文章见:
鸿蒙@fw/router框架源码解析(一)-Router页面管理
鸿蒙@fw/router框架源码解析(三)-Navigation页面容器封装
鸿蒙@fw/router框架源码解析(四)-路由Hvigor插件实现原理
鸿蒙@fw/router框架源码解析(五)-无代码依赖如何实现拦截器逻辑
鸿蒙@fw/router框架源码解析(六)-模块化开发如何实现代码解耦
鸿蒙@fw/router框架源码解析
介绍
@fw/router是在HarmonyOS鸿蒙系统中开发应用所使用的开源模块化路由框架。
该路由框架基于模块化开发思想设计,支持页面路由和服务路由,支持自定义装饰器自动注册,与系统路由相比使用更便捷,功能更丰富。
具体功能介绍见@fw/router:鸿蒙模块化路由框架,助力开发者实现高效模块化开发!
基于模块化的开发需求,本框架支持以下功能:
- 支持页面路由和服务路由;
- 页面路由支持多种模式(router模式,Navigation模式,混合模式);
- router模式支持打开非命名路由页面;
- 页面打开支持多种方式(push/replace),参数传递;关闭页面,返回指定页面,获取返回值,跨页面获取返回值;
- 支持服务路由,可使用路由url调用公共方法,达到跨技术栈调用以及代码解耦的目的;
- 支持页面路由/服务路由通过装饰器自动注册;
- 支持动态导入(在打开路由时才import对应har包),支持自定义动态导入逻辑;
- 支持添加拦截器(打开路由,关闭路由,获取返回值);
- Navigation模式下支持自定义Dialog对话框;
详见gitee传送门
代码解析
Navigation页面
页面注册@NavigationRoute
@NavigationRoute({ routeName: "testPage", hasParams: true })
@Component
export struct TestDestination {@Prop params?: Record<string, ESObject>build() {Column() {NavDestination() {TestPageContent({ pageName: 'TestDestination', params: this.params })}}}
}
Navigation页面注册使用了自定义的类装饰器@NavigationRoute。我们来看一下其实现:
export function NavigationRoute(options: RouteRegisterOptions) {return (target: ESObject) => {}
}
我们发现,该装饰器的实现代码是个空方法,空方法的话如何实现页面注册呢?
这是因为在ArkTS中,struct无法使用自定义的装饰器,虽然IDE编译不会报错,但是这个装饰器代码根本不会执行。
那么,Navigation页面到底是怎么完成注册的?
答案是:FWRouterHvigorPlugin。
在这个hvigor插件中,插件代码扫描模块中的.eta文件,解析ts语法,当发现装饰器@NavigationRoute时,就会将它所装饰的类名提取出来,然后生成对应的builder和自动注册代码。具体如下:
@Builder
function testDestinationBuilder(params: ESObject) {TestDestination({ params: params });
}@RouterClassProvider({ routeName: 'testPage', builder: wrapBuilder(testDestinationBuilder) })
export class TestDestinationProvider {
}
我们看到插件生成了两部分代码,testDestinationBuilder
是对Navigation页面TestDestination
的包装,这是ArkTS的要求。
具体原因可以查看鸿蒙应用开发从入门到入魔:Navigation路由管理为什么这么麻烦?
这里有一个细节,就是TestDestination({ params: params })
的参数params。因为不是所有的页面都是有入参的,那理论上params
是有时候需要传值,有时候不需要传值。
虽然我们可以简化逻辑,强制所有页面都传递params
,但这样就导致了即便是不需要参数的页面也需要增加定义@Prop params?: Record<string, ESObject>
。
这种处理方法无疑有点粗暴,所以我们选择给NavigationRoute
的入参增加hasParams
参数,当参数值为true时,传值params参数,当值为false时,不传值,比如TestDestination()
。
插件生成的代码中还有一个TestDestinationProvider
,它的作用是什么?
其实,testDestinationBuilder
只是必须的代码模板,不是我们自己想要的。@RouterClassProvider({ routeName: 'testPage', builder: wrapBuilder(testDestinationBuilder) })
这是核心逻辑。
我们来看@RouterClassProvider的实现代码:
export function RouterClassProvider(options: RouterClassProviderOptions) {return (target: ESObject) => {RouterManagerForNavigation.getInstance().registerBuilder(options.routeName, options.builder)}
}
我们看到这个装饰器真正调用了RouterManagerForNavigation
中的注册方法,将路由名和页面builder的匹配关系注册到了管理器中。
那么,为什么要这样实现呢?
我们想要的其实就只有一个@RouterClassProvider
装饰器,但装饰器不能单独使用,必须装饰在一个类上,所以我们定义了TestDestinationProvider
类。
而TestDestination
不能直接拿来注册,必须包装进@builder,所以我们定义了testDestinationBuilder
。
除此之外,@RouterClassProvider
装饰器的触发时机是其所在的文件被import的时候。
因此,在har包中,我们需要将生成的代码文件自动添加到模块的index.ets中去。
export * from './src/main/ets/generated/RouterBuilder';
在entry中,我们需要在EntryAbility.ets中导入。
import('../generated/RouterBuilder');
以上是hvigor插件为了完成Navigation页面所做的事情,至于方案为什么是这样,建议详细查看具体原因可以查看鸿蒙应用开发从入门到入魔:Navigation路由管理为什么这么麻烦?。
打开页面
对于@fw/router而言,打开router页面和Navigation页面都是一样,因此使用完全相同的api,所以前面的openWithRequest
、_realOpen
、open
等方法逻辑完全一致,此处不再赘述。
RouterManagerForNavigation.open
open(request: RouterRequestWrapper): Promise<RouterResponse> {return new Promise((resolve, reject) => {if (!this.currentNavPathStack) {resolve(RouterResponseError.RequestNotFoundResponsor)return}if (!this.canOpen(request.routeName)) {resolve(RouterResponseError.RequestNotFoundResponsor)return}switch (request.rawRequest.openMode) {case PageRouteOpenMode.replace:this.currentNavPathStack!.replacePath({name: request.routeName, param: request.params})request.resolve = resolve;this.inject(request)break;default:this.currentNavPathStack!.pushDestination({name: request.routeName, param: request.params}).then(() => {request.resolve = resolve;this.inject(request)}).catch((e: ESObject) => {console.log(`${e}`)if (e.code == 100005) {resolve(RouterResponseError.RequestNotFoundResponsor)} else {resolve(RouterResponseError.UnknownError)}})break;}})}
RouterManagerForNavigation.open方法,主要是处理了replace和push两种不同的打开模式。
页面返回值
系统的返回监听
我们可以看到,在push页面时,我们调用了pushDestination
方法,而它的入参其实是支持获取页面返回值的。
declare class NavPathInfo {constructor(name: string, param: unknown, onPop?: import('../api/@ohos.base').Callback<PopInfo>);name: string;param?: unknown;/*** The callback when next page returns.** @type { ?import('../api/@ohos.base').Callback<PopInfo> }* @syscap SystemCapability.ArkUI.ArkUI.Full* @crossplatform* @atomicservice* @since 12*/onPop?: import('../api/@ohos.base').Callback<PopInfo>;
}
onPop
会在页面关闭时被处罚,而且支持返回值。
但是,我们并没有使用该参数,因为它在跨页面返回值存在逻辑问题。
当页面A打开页面B,页面B打开页面C,然后页面C直接返回页面A并传递返回值时,我们期望的效果是页面A拿到页面C的返回值。
比如,课程详情页打开支付中间页,然后打开付款页面;付款成功或失败后返回课程详情页;课程详情页需要通过付款是否成功来判断页面是否刷新页面。
但是,onPop
目前的逻辑是页面C直接返回页面A并带返回值时,页面B的onPop会被触发,页面A的onPop并不会被触发。
所以,虽然onPop
用起来非常方便,但为了功能的完整性,我们还是放弃了使用该参数。
返回值实现逻辑
最终的实现逻辑和router类似,即通过监听页面生命周期来手动触发回调。
close(options?: RouterBackOptionsWrapper | undefined): boolean {// ...this.resultStrategy = RouterResultStrategy.onPagePop// `NavPathStack.pop/popToName`方法`result`参数为undefined时无法触发其push方法的onPop回调;if (options && options.routeName && options.routeName.length > 0) {let routeInfo = this.getRequest(options.routeName)if (routeInfo?.destinationInfo) {this.resultStrategy = RouterResultStrategy.onPageShowthis.backToRouteName = options.routeNamethis.backToIndex = routeInfo?.destinationInfo.index}let result = this.currentNavPathStack!.popToName(options.routeName, backParams, true)if (result == -1) {// 失败后清空,防止影响其他场景的返回值取值(比如侧滑返回,点系统返回按钮等)this.backParams = undefined}return result != -1} else {let result = this.currentNavPathStack!.pop(backParams, true)if (result == undefined) {// 失败后清空,防止影响其他场景的返回值取值(比如侧滑返回,点系统返回按钮等)this.backParams = undefined}return result != undefined}}
首先看一下close
方法,因为返回上一页和返回指定页面在返回值处理逻辑上存在很大差异,所以我们单独定义了resultStrategy
返回值策略属性。
这是为了加强代码的可读性,否则无论是使用者还是开发者都容易在各种条件判断中迷失。
/*** 页面路由返回值处理策略*/
export enum RouterResultStrategy {/*** 在被打开的页面pop出栈时,触发打开该页面对应的回调方法。*/onPagePop,/*** 返回指定页面routeName时,当routeName onShow时,触发最后获取到的回调方法(即routeName打开页面时传入的回调方法)。*/onPageShow,
}
然后我们看一下最核心的生命周期监听逻辑:
observerPageLifecycle(uiAbility: UIAbility) {observer.on("navDestinationUpdate", (navDestinationInfo: observer.NavDestinationInfo) => {const name = navDestinationInfo.name.toString()const id = navDestinationInfo.navDestinationId// 通过监听页面生命周期方法,将系统堆栈和routes保持一致,用来处理返回值回调switch (navDestinationInfo.state) {case observer.NavDestinationState.ON_APPEAR:if (!this.hasRequest(name, id)) {let request = this.hasUndefinedRequest(name)if (request) {request.destinationInfo = navDestinationInfo} else {this.inject(new RouterRequestWrapper({ url: "other/" + name }), navDestinationInfo)}}breakcase observer.NavDestinationState.ON_SHOWN: {if (this.resultStrategy == RouterResultStrategy.onPageShow && this.backToRouteName === name) {this.lastResolve?.({code: RouterResponseError.Success.code,msg: RouterResponseError.Success.msg,data: this.backParams})// 使用后清空,防止影响其他场景的返回值取值(比如侧滑返回,点系统返回按钮等)this.backParams = undefined}break}case observer.NavDestinationState.ON_WILL_DISAPPEAR: {if (this.resultStrategy == RouterResultStrategy.onPagePop) {this.getRequest(name, id)?.request?.resolve?.({code: RouterResponseError.Success.code,msg: RouterResponseError.Success.msg,data: this.backParams})// 使用后清空,防止影响其他场景的返回值取值(比如侧滑返回,点系统返回按钮等)this.backParams = undefined} else {if (this.backToIndex != undefined && navDestinationInfo.index == this.backToIndex + 1) {this.lastResolve = this.getRequest(name, id)?.request?.resolve}}this.removeRequest(name, id)break}case observer.NavDestinationState.ON_BACKPRESS: {// api12.beta2 该状态不会被触发this.getRequest(name, id)?.request?.resolve?.({code: RouterResponseError.Success.code,msg: RouterResponseError.Success.msg})this.removeRequest(name, id)break}}})}
- 监听
ON_APPEAR
状态,将页面与open方法的request参数(inject方法)绑定; - 监听
ON_SHOWN
状态,处理RouterResultStrategy.onPageShow
策略,当指定页面触发该状态,则找到该页面发起的请求(这其实是在ON_WILL_DISAPPEAR
状态中完成),并触发其revolve回调,回传参数; - 监听
ON_WILL_DISAPPEAR
状态,处理RouterResultStrategy.onPagePop
策略,在本页面消失时,获取到打开本页面的请求,并触发其resolve回调,回传参数; - 监听
ON_BACK_PRESS
状态,api12.beta2该状态不会被触发,其实是无效逻辑;因此,当页面侧滑返回或者点击导航栏返回按钮时,实际走的还是ON_WILL_DISAPPEAR
状态的逻辑。
总结
我们可以看到,Navigation的页面封装其实router更为复杂,主要是其相比router页面,系统并没有给与原生的自动注册逻辑,从而导致了巨大的复杂性。
除此之外,动态导入也增加了很多复杂度。
如果官方可以自己解决掉自动注册和动态导入两个问题,我相信对于绝大多数人而言,路由框架就没有封装的必要了。
相关文章:
鸿蒙技术分享:Navigation页面管理-鸿蒙@fw/router框架源码解析(二)
本文是系列文章,其他文章见: 鸿蒙fw/router框架源码解析(一)-Router页面管理 鸿蒙fw/router框架源码解析(三)-Navigation页面容器封装 鸿蒙fw/router框架源码解析(四)-路由Hvigor插件…...
数据结构:树
树的基本定义: 树是一种数据结构,它是由n(n>1)个有限节点组成一个具有层次关系的集合。把它叫做 “树” 是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点: …...
矩阵sum,prod函数
s u m sum sum表示求和, p r o d prod prod表示求乘积 s u m sum sum函数 对于矩阵,可以对某一行或某一列求和,也可以对矩阵整体求和 s u m ( a , 1 ) sum(a,1) sum(a,1)计算每一列的和 s u m ( a , 2 ) sum(a,2) sum(a,2)计算每一行的和 计算矩阵整体…...
Cursor安装与使用,5分钟完成需求
Cursor简单介绍 Cursor是一款基于AI的代码编辑器,旨在帮助开发者更高效地编写和管理代码。它提供了智能代码补全、AI对话和跨文件编辑等创新功能。 一、安装下载 1、下载cursor:https://www.cursor.com/ 2、注册账号,直接拿自己的邮箱登录…...
嵌入式系统应用-LVGL的应用-平衡球游戏 part1
平衡球游戏 part1 1 平衡球游戏的界面设计2 界面设计2.1 背景设计2.2 球的设计2.3 移动球的坐标2.4 用鼠标移动这个球2.5 增加边框规则2.6 效果图 3 为小球增加增加动画效果3.1 增加移动效果代码3.2 具体效果图片 平衡球游戏 part2 第二部分文章在这里 1 平衡球游戏的界面设计…...
Vue基本语法
Options API 选项式/配置式api 需要在script中的export default一个对象对象中可以包含data、method、components等keydata是数据,数据必须是一个方法(如果是对象,会导致多组件的时候,数据互相影响,因为对象赋值后&…...
UIE与ERNIE-Layout:智能视频问答任务初探
内容来自百度飞桨ai社区UIE与ERNIE-Layout:智能视频问答任务初探: 如有侵权,请联系删除 1 环境准备 In [2] # 安装依赖库 !pip install paddlenlp --upgrade !pip install paddleocr --upgrade !pip install paddlespeech --upgrade In …...
Mac启动服务慢问题解决,InetAddress.getLocalHost().getHostAddress()慢问题。
项目启动5分钟,很明显有问题。像网上其他的提高jvm参数就不说了,应该不是这个问题,也就快一点。 首先找到自己的电脑名称(用命令行也行,只要能找到自己电脑名称就行,这里直接在共享里看)。 复制…...
Django 视图层
from django.shortcuts import render, HttpResponse, redirectfrom django.http import JsonResponse1. render: 渲染模板 def index(request):print(reverse(index))return render(request, "index.html")return render(request, index.html, context{name: lisi})…...
HickWall 详解
优质博文:IT-BLOG-CN 一、监控分类 【1】Tracing调用链: 【2】Logging日志: 【3】Metrics指标:在应用发布之后,会长时间存在的度量维度。某个接口的请求量、响应时间。 Metrics数据模型 二、Metirc 接入 【1】pom…...
开源的跨平台SQL 编辑器Beekeeper Studio
一款开源的跨平台 SQL 编辑器,提供 SQL 语法高亮、自动补全、数据表内容筛选与过滤、连接 Web 数据库、存储历史查询记录等功能。该编辑器支持 SQLite、MySQL、MariaDB、Postgres 等主流数据库,并兼容 Windows、macOS、Linux 等桌面操作系统。 项目地址…...
Linux应用层学习——Day4(进程处理)
system #include<stdio.h> #include<stdlib.h>int main(int argc, char const *argv[]) {//使用标准库函数创建子进程//int system (const char *__command);//const char *__command:使用linux命令直接创建一个子进程//return:成功返回0 失败返回失败编号int sys…...
起别名typedef
#include<stdio.h> //typedef int myType1; //typedef char myType2; typedef struct { int a; int b; }po; int main() { /*myType1 a5; myType2 bo; printf("%d\n",a); printf("%c\n",b);*/ po p;//不需要加struct关键…...
【Linux内核】ashmem pin/unpin
前言 在 Linux 内核的 ASHMEM(Android Shared Memory)实现中,pin 和 unpin 操作主要用于管理共享内存的生命周期和可用性。这些操作有助于确保在内存使用期间,特定的共享内存区域不会被回收或释放。 Pin 操作 定义 Pin 操作用…...
【docker】docker网络六种网络模式
Docker 网络模式总结 网络模式描述使用场景bridge默认的网络模式,容器之间通过虚拟网桥通信,容器与宿主机隔离。单机部署、本地开发、小型项目host容器与宿主机共享网络堆栈,容器直接使用宿主机的 IP 地址。高性能网络应用、日志处理、大量与…...
永磁同步电机谐波抑制算法(11)——基于矢量比例积分调节器(vector PI controller,VPI controller)的谐波抑制策略
1.前言 相比于传统的谐振调节器,矢量比例积分调节器(vector PI controller,VPI controller)多一个可调零点,能够实现电机模型的零极点对消。因此VPI调节器也被广泛应用于交流控制/谐波抑制中。 2.参考文献 [1] A. G…...
排序算法中稳定性的意义和作用
多关键字排序:当需要对数据进行多个关键字排序时,稳定性变得非常重要。例如,先按次要关键字排序,再按主要关键字排序。如果排序算法是稳定的,那么在按主要关键字排序后,次要关键字的顺序将被保留。保持关联…...
网站怎么防御https攻击
HTTPS攻击,它不仅威胁到网站的数据安全,还可能影响用户隐私和业务稳定运行。 HTTPS攻击主要分为以下几种类型: 1.SSL劫持:攻击者通过中间人攻击手段,篡改HTTPS流量,从而实现对数据的窃取或伪造。 2.中间人攻…...
gitignore 不起作用
.gitignore不起作用 文件已提交至远程仓库,已经被Git跟踪。清除缓存.gitignore位置可能不是与 .git隐藏文件夹同级目录。将文件移至同级目录缓存未清除 清除缓存 清楚git缓存步骤 进入项目路径 清除本地当前的Git缓存 git rm -r --cached . 应用.gitignore等本地…...
Hive学习基本概念
基本概念 hive是什么? Facebook 开源,用于解决海量结构化日志的数据统计。 基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能 本质是将HQL转化为MapReduce程序。 Hive处理的数据存储在H…...
在 Ubuntu 使用 fonts-noto-cjk 设置 Matplotlib 支持中文的完整教程
在 Ubuntu 使用 fonts-noto-cjk 设置 Matplotlib 支持中文的完整教程 1. 为什么需要配置中文字体?2. 安装 fonts-noto-cjk安装命令:检查字体安装是否成功 3. 配置 Matplotlib 支持中文3.1 手动加载字体3.2 设置全局字体(可选)修改…...
《C++ Primer Plus》学习笔记|第10章 对象和类 (24-12-2更新)
文章目录 10.3 类的构造函数和析构函数10.3.2 使用构造函数显式地调用构造函数隐式地调用构造函数使用对象指针 10.3.3默认构造函数10.3.4 析构函数析构函数示例 10.4 this指针三个const的作用 10.5 对象数组10.6 类作用域10.9 复习题1.什么是类?2.类如何实现抽象、…...
SpringMVC接收数据
一、访问路径设置: RequestMapping注解的作用就是将请求的URL地址和处理请求的方式(handler方法)关联起来,建立映射关系;SpringMVC接收到指定的请求,就会来找到在映射关系中对应的方法来处理这个请求 1.精准路径匹配: 在RequestMapping注解指定URL地址…...
Python数组拆分(array_split())
天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...
Git 使用总结
下载 git bash:http://git-scm.com/download/win 第一次使用 git 时,配置用户信息: git config --global user.email "your.emailexample.com" 从github仓库中下载项目到本地,修改后重新上传: git clone 项…...
NaviveUI框架的使用 ——安装与引入(图标安装与引入)
文章目录 概述安装直接引入引入图标样式库 概述 🍉Naive UI 是一个轻量、现代化且易于使用的 Vue 3 UI 组件库,它提供了一组简洁、易用且功能强大的组件,旨在为开发者提供更高效的开发体验,特别是对于构建现代化的 web 应用程序。…...
YOLOv11 NCNN安卓部署
YOLOv11 NCNN安卓部署 之前自己在验证更换relu激活函数重新训练部署模型的时候,在使用ncnn代码推理验证效果很好,但是部署到安卓上cpu模式会出现大量的错误检测框,现已更换会官方默认的权重 前言 YOLOv11 NCNN安卓部署 目前的帧率可以稳定…...
多线程安全单例模式的传统解决方案与现代方法
在多线程环境中实现安全的单例模式时,传统的双重检查锁(Double-Checked Locking)方案和新型的std::once_flag与std::call_once机制是两种常见的实现方法。它们在实现机制、安全性和性能上有所不同。 1. 传统的双重检查锁方案 双重检查锁&am…...
golang debug调试
1. 本地调试 1:Add Configurations 添加配置文件(Run kind :Directory) 2:进入run运行窗口 3:debug断点调试模式 1. Resume Program (继续运行) 图标: ▶️ 或 ► 快捷键: F9(Windows/Linux&a…...
安装 RabbitMQ 服务
安装 RabbitMQ 服务 一. RabbitMQ 需要依赖 Erlang/OTP 环境 (1) 先去 RabbitMQ 官网,查看 RabbitMQ 需要的 Erlang 支持:https://www.rabbitmq.com/ 进入官网,在 Docs -> Install and Upgrade -> Erlang Version Requirements (2) …...
pandas 大数据获取某列所有唯一值
目录 方法1: 方法2: 方法3 处理大数据: 方法1: data.groupby().groups.keys() import pandas as pd# 假设我们有以下的数据 data = {RTDR_name: [A, B, A, C, B, A],value: [1, 2, 3, 4, 5, 6] }# 创建 DataFrame temp_data = pd.DataFrame(data)# 获取 RTDR_name 列的…...
【AI系统】LLVM 架构设计和原理
LLVM 架构设计和原理 在上一篇文章中,我们详细探讨了 GCC 的编译过程和原理。然而,由于 GCC 存在代码耦合度高、难以进行独立操作以及庞大的代码量等缺点。正是由于对这些问题的意识,人们开始期待新一代编译器的出现。在本节,我们…...
Node.js 中的文件系统(fs)模块详解与代码示例
Node.js 中的文件系统(fs)模块详解与代码示例 Node.js 的 fs 模块提供了与文件系统交互的能力,包括文件的读写、目录的管理等。以下是 fs 模块中一些常用方法的详细解释和代码示例: 1. 异步读取文件内容 作用:异步读…...
TinyXML2的一些用法
TinyXML2 原始字符串字面量 TinyXML21. XML文档操作1.1 LoadFile(const char* filename)1.2SaveFile(const char* filename)1.3RootElement()1.4Parse(const char* xml) 2.元素操作2.1 FirstChildElement(const char* name nullptr)2.2 NextSiblingElement(const char* name …...
【Vue3】从零开始创建一个VUE项目
【Vue3】从零开始创建一个VUE项目 手动创建VUE项目附录 package.json文件报错处理: Failed to get response from https://registry.npmjs.org/vue-cli-version-marker 相关链接: 【VUE3】【Naive UI】<NCard> 标签 【VUE3】【Naive UI】&…...
springboot370高校宣讲会管理系统(论文+源码)_kaic
毕 业 设 计(论 文) 高校宣讲会管理系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,…...
navicat连接mysql 8.0以上版本2059错误
安装了最新版本8.0.4的mysql之后,使用navicat链接提示以下错误。原因是因为mysql8.0 之前的版本中加密规则是 mysql_native_password,而 mysql8.0 之后的版本加密规则是caching_sha2_password 处理方案 解决方案1:下载安装最新版本navicat…...
SQL优化与性能——C++与SQL性能优化
在开发高效的数据库应用程序时,性能优化至关重要,尤其是当系统规模逐渐扩大或并发请求增加时。数据库操作往往是应用程序性能的瓶颈所在,因此,在 C 应用程序中合理优化数据库操作,管理数据库连接池、使用批量插入与更新…...
AI高中数学教学视频生成技术:利用通义千问、MathGPT、视频多模态大模型,语音大模型,将4个模型融合 ,生成高中数学教学视频,并给出实施方案。
大家好,我是微学AI,今天给大家介绍一下AI高中数学教学视频生成技术:利用通义千问、MathGPT、视频多模态大模型,语音大模型,将4个模型融合 ,生成高中数学教学视频,并给出实施方案。本文利用专家模…...
vscode远程连接ssh
一. 使用vscode里的ssh查件连不上远程的解决方法 删除Windows上的known_host文件,该文件会在连接之后自动生成,用于验证远程服务器的身份。 konwn_host和id_rsa,id_rsa.pub的关系 (1)konwn_host用于客户端验证远程服务…...
学习ASP.NET Core的身份认证(基于Session的身份认证2)
基于Session的身份认证通过后,后续访问控制器的函数时该如何控制访问权限?虽然可以按上篇文章方式在需要做控制的函数开头检查Session的用户标识,可以写个全局通用检查类供所需函数调用,但还是有更简便的方法,本文学习…...
深度学习基本单元结构与输入输出维度解析
深度学习基本单元结构与输入输出维度解析 在深度学习领域,模型的设计和结构是理解其性能和应用的关键。本文将介绍深度学习中的基本单元结构,包括卷积神经网络(CNN)、反卷积(转置卷积)、循环神经网络&…...
playwright 学习复仇记-1 开端
前言 说到 web 自动化,大家最熟悉的就是 selenium 了,selenium 之后又出现了三个强势的框架Puppeteer、CyPress、TestCafe, 但这3个都需要掌握 JavaScript 语言,所以只是少部分人在用。 2020年微软开源一个 UI 自动化测试工具 Pl…...
从零开始使用GOT-OCR2.0——多模态OCR项目:微调数据集构建 + 训练(解决训练报错,成功实验微调训练)
在上一篇文章记录了GOT-OCR项目的环境配置和基于官方模型参数的基础使用。环境安装的博文快速链接: 从零开始使用GOT-OCR2.0——多模态通用型OCR(非常具有潜力的开源OCR项目):项目环境安装配置 测试使用-CSDN博客 本章在环境配置…...
Rust学习笔记_10——守卫
Rust学习笔记_07——枚举和范围 Rust学习笔记_08——String Rust学习笔记_09——模式匹配 守卫 文章目录 守卫1. 介绍2. 基本用法3. 示例4. 复杂用法5. if let5.1 基本用法5.2 示例5.3 守卫与if let的区别与联系 1. 介绍 在Rust中,守卫(guardÿ…...
UE5 打包报错 Unknown structure 的解决方法
在虚幻引擎5.5 打包报错如下: UATHelper: 打包 (Windows): LogInit: Display: LogProperty: Error: FStructProperty::Serialize Loading: Property ‘StructProperty /Game/Components/HitReactionComponent/Blueprints/BI_ReactionInterface.BI_ReactionInterface…...
如何打开链接中的网址
文章目录 1 概念介绍2 使用方法3 示例代码我们在上一章回中介绍了包管理相关的内容,本章回中将介绍如何使用url_launcher包.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在这里介绍url_launcher包主要用来打开Url中的内容,Url可以是电话号码,网址,邮箱等内容。如…...
React 前端框架4
六、React 中的事件处理 (一)绑定事件的方式 在 React 中,事件绑定和传统的 HTML 中的事件绑定有一些不同,它采用了驼峰命名法来命名事件名称,并且事件绑定的属性值是一个函数。例如,在 HTML 中绑定点击事…...
Neo4j启动时指定JDK版本
项目使用jdk1.8,同时需要安装neo4j5.15版本,使用jdk17. 1.mac或者liunx,找到neo4j目录bin的下neo4j文件 设置JAVA_HOME: 2.windows,找到bin下面的neo4j.bat文件 set "JAVA_HOME{JDK文件目录}" 重启后生效。...
【k8s深入理解之 Scheme 补充-2】理解 register.go 暴露的 AddToScheme 函数
AddToScheme 函数 AddToScheme 就是为了对外暴露,方便别人调用,将当前Group组的信息注册到其 Scheme 中,以便了解该 Group 组的数据结构,用于后续处理 项目版本用途使用场景k8s.io/apiV1注册资源某一外部版本数据结构࿰…...