HarmonyOS应用六之应用程序进阶一

news2024/11/23 19:31:33

目录:

    • 1、UIAbility的冷启动和UIAbility热启动
    • 2、静态资源和动态资源的访问
    • 3、页面跳转
      • 3.1、页面返回跳转
    • 4、HAR的ArkUI组件、接口、资源,供其他应用或当前应用的其他模块引用
      • 4.1、导出HAR的ArkUI组件
      • 4.2、引用HAR的ArkUI组件
    • 5、循环渲染
    • 6、状态管理最佳实践
    • 7、AppStorage的使用精细化拆分复杂状态

1、UIAbility的冷启动和UIAbility热启动

  • UIAbility冷启动:指的是UIAbility实例处于完全关闭状态下被启动,这需要完整地加载和初始化UIAbility实例的代码、资源等。

目标UIAbility冷启动时,在目标UIAbility的onCreate()生命周期回调中,接收调用方传过来的参数。然后在目标UIAbility的onWindowStageCreate()生命周期回调中,解析调用方传递过来的want参数,获取到需要加载的页面信息url,传入windowStage.loadContent()方法。

import { AbilityConstant, Want, UIAbility } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window, UIContext } from '@kit.ArkUI';

const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[EntryAbility]';

export default class EntryAbility extends UIAbility {
  funcAbilityWant: Want | undefined = undefined;
  uiContext: UIContext | undefined = undefined;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 接收调用方UIAbility传过来的参数
    this.funcAbilityWant = want;
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageCreate');
    // Main window is created, set main page for this ability
    let url = 'pages/Index';
    if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') {
      url = 'pages/Page_ColdStartUp';
    }
    windowStage.loadContent(url, (err, data) => {
      // ...
    });
  }
}
  • UIAbility热启动:指的是UIAbility实例已经启动并在前台运行过,由于某些原因切换到后台,再次启动该UIAbility实例,这种情况下可以快速恢复UIAbility实例的状态。

在应用开发中,会遇到目标UIAbility实例之前已经启动过的场景,这时再次启动目标UIAbility时,不会重新走初始化逻辑,只会直接触发onNewWant()生命周期方法。为了实现跳转到指定页面,需要在onNewWant()中解析参数进行处理。

例如短信应用和联系人应用配合使用的场景。

1、用户先打开短信应用,短信应用的UIAbility实例启动,显示短信应用的主页。
2、用户将设备回到桌面界面,短信应用进入后台运行状态。
3、用户打开联系人应用,找到联系人张三。
4、用户点击联系人张三的短信按钮,会重新启动短信应用的UIAbility实例。
5、由于短信应用的UIAbility实例已经启动过了,此时会触发该UIAbility的onNewWant()回调,而不会再走onCreate()和onWindowStageCreate()等初始化逻辑。

import { AbilityConstant, Want, UIAbility } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import type { Router, UIContext } from '@kit.ArkUI';
import type { BusinessError } from '@kit.BasicServicesKit';

const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[EntryAbility]';

export default class EntryAbility extends UIAbility {
  funcAbilityWant: Want | undefined = undefined;
  uiContext: UIContext | undefined = undefined;
  // ...
  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    if (want?.parameters?.router && want.parameters.router === 'funcA') {
      let funcAUrl = 'pages/Page_HotStartUp';
      if (this.uiContext) {
        let router: Router = this.uiContext.getRouter();
        router.pushUrl({
          url: funcAUrl
        }).catch((err: BusinessError) => {
          hilog.error(DOMAIN_NUMBER, TAG, `Failed to push url. Code is ${err.code}, message is ${err.message}`);
        });
      }
    }
  }
}

热启动是前提得有冷启动,就是应用启动后处于后台运行,当从其他位置点击进来需要进入后台运行的应用程序时就需要使用到热启动。

2、静态资源和动态资源的访问

在组件中,经常需要使用字符串、图片等资源。HSP中的组件需要使用资源时,一般将其所用资源放在HSP包内,而非放在HSP的使用方处,以符合高内聚低耦合的原则。
在工程中,常通过 r / r/ r/rawfile的形式引用应用资源。可以用 r / r/ r/rawfile访问本模块resources目录下的资源,如访问resources目录下定义的图片src/main/resources/base/media/example.png时,可以用$r(“app.media.example”)

// library/src/main/ets/pages/Index.ets
// 正确用例
Image($r('app.media.example'))
  .id('example')
  .borderRadius('48px')
// 错误用例
Image("../../resources/base/media/example.png")
  .id('example')
  .borderRadius('48px')

将需要对外提供的资源封装为一个资源管理类:

// library/src/main/ets/ResManager.ets
export class ResManager{
  static getPic(): Resource{
    return $r('app.media.pic');
  }
  static getDesc(): Resource{
    return $r('app.string.shared_desc');
  }
}

对外暴露的接口,需要在入口文件index.ets中声明:

// library/index.ets
export { ResManager } from './src/main/ets/ResManager';

3、页面跳转

在这里插入图片描述
其中router.pushUrl方法的入参中url的内容为:

'@bundle:com.samples.hspsample/library/ets/pages/Menu'

url内容的模板为:

'@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'

3.1、页面返回跳转

在这里插入图片描述

页面返回router.back方法的入参中url说明:

如果从HSP页面返回HAP页面,url的内容为:

'pages/Index'

url内容的模板为:

'页面所在的文件名(不加.ets后缀)'

如果从HSP1的页面跳到HSP2的页面后,需要返回到HSP1的页面,url的内容为:

'@bundle:com.samples.hspsample/library/ets/pages/Menu'

url内容的模板为:

'@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'

4、HAR的ArkUI组件、接口、资源,供其他应用或当前应用的其他模块引用

4.1、导出HAR的ArkUI组件

Index.ets文件是HAR导出声明文件的入口,HAR需要导出的接口,统一在Index.ets文件中导出。Index.ets文件是DevEco Studio默认自动生成的,用户也可以自定义,在模块的oh-package.json5文件中的main字段配置入口声明文件,配置如下所示:

{
  "main": "Index.ets"
}

导出ArkUI组件
ArkUI组件的导出方式与ts的导出方式一致,通过export导出ArkUI组件,示例如下:

// library/src/main/ets/components/mainpage/MainPage.ets
@Component
export struct MainPage {
  @State message: string = 'HAR MainPage';

  build() {
    Column() {
      Row() {
        Text(this.message)
          .fontSize(32)
          .fontWeight(FontWeight.Bold)
      }
      .margin({ top: '32px' })
      .height(56)
      .width('624px')

      Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, alignContent: FlexAlign.Center }) {
        Column() {
          Image($r('app.media.pic_empty')).width('33%')
          Text($r('app.string.empty'))
            .fontSize(14)
            .fontColor($r('app.color.text_color'))
        }
      }.width('100%')
      .height('90%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.page_background'))
  }
}

HAR对外暴露的接口,在Index.ets导出文件中声明如下所示:

// library/Index.ets
export { MainPage } from './src/main/ets/components/mainpage/MainPage';

4.2、引用HAR的ArkUI组件

HAR的依赖配置成功后,可以引用HAR的ArkUI组件。ArkUI组件的导入方式与ts的导入方式一致,通过import引入HAR导出的ArkUI组件,示例如下所示:

// entry/src/main/ets/pages/IndexSec.ets
import { MainPage } from 'library';

@Entry
@Component
struct IndexSec {
  build() {
    Row() {
      // 引用HAR的ArkUI组件
      MainPage()
    }
    .height('100%')
  }
}

5、循环渲染

@Entry
@Component
struct Parent {
  @State simpleList: Array<string> = ['one', 'two', 'three'];

  build() {
    Row() {
      Column() {
        ForEach(this.simpleList, (item: string) => {
          ChildItem({ item: item })
        }, (item: string) => item)
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ChildItem {
  @Prop item: string;

  build() {
    Text(this.item)
      .fontSize(50)
  }
}

运行效果如图所示:
在这里插入图片描述
此次是循环遍历数据,通过@Prop监听父组件中的值,进行字体大小为50的逻辑处理。

6、状态管理最佳实践

@Observed
class Translate {
  translateX: number = 20;
}

@Entry
@Component
struct UnnecessaryState1 {
  @State translateObj: Translate = new Translate(); // 同时存在读写操作,并关联了Button组件,推荐使用状态变量
  buttonMsg = 'I am button'; // 仅读取变量buttonMsg的值,没有任何写的操作,直接使用一般变量即可

  build() {
    Column() {
      Button(this.buttonMsg)
        .onClick(() => {
          animateTo({
            duration: 50
          }, () => {
            this.translateObj.translateX = (this.translateObj.translateX + 50) % 150; // 点击时给变量translateObj重新赋值
          })
        })
    }
    .translate({
      x: this.translateObj.translateX // 读取translateObj中的值
    })
  }
}

没有关联任何UI组件的状态变量和没有修改过的状态变量不应该定义为状态变量,直接使用一般变量即可,否则会影响性能。

在这里插入图片描述

7、AppStorage的使用精细化拆分复杂状态

对AppStorage的使用,以“HMOS世界App”中共享用户信息和用户收藏信息为例,描述如何拆分状态存储。用户信息和用户收藏信息涉及的模块和界面展示如下:
在这里插入图片描述

  • “我的”模块顶部有展示用户信息的组件“UserInfoView”,底部有展示用户收藏列表,列表卡片上需要高亮展示用户是否点赞了当前文章。
  • “探索”模块首页展示技术文章列表,列表卡片上同样需要展示用户是否点赞了当前文章。
  • 当两个模块中任一模块的卡片有点赞交互时,需要同步用户是否对文章点赞的状态给另一个模块。

当前项目中已经使用AppStorage存储用户信息UserData,UserData的数据结构和“UserInfoView”组件使用UserData状态展示用户信息的代码如下:

//用户信息UserData的数据结构
export interface UserData {
  id: string;
  username: string;
  description: string;
    // ...
}

//在业务类中获取服务端用户信息
getUserData(): void {
  this.userAccountRepository.getUserData().then((data: UserData) => {
    //1.将用户信息数据存储到AppStorage中
    AppStorage.setOrCreate('userData', data);
  })
}

//“我的”模块顶部展示用户信息的视图组件
@Component
struct UserInfoView {
  //2.在UI中使用@StorageLink装饰器接收AppStorage中存储的用户信息
  @StorageLink('userData') userData: UserData | null = null;

  build() {
    Column() {
      Row({ space: Constants.MIDDLE_SPACE }) {
        // ...
        Column() {
          //3.展示用户信息userData中的用户名
          Text(this.userData? this.userData.username : $r('app.string.default_login'))
            // ...
        }
      }
      // ...
    }
    // ...
  }
}

现在“探索“模块和“我的“模块需要共享用户的收藏列表信息,只需要共享收藏的文章id数组即可。不同模块间的状态共享考虑将其也存储在AppStorage中,有如下两种存储方案:

  1. 收藏信息也是用户信息的一部分,将收藏信息作为用户信息userData的一个属性,存储在当前AppStorage里key值为“userData”的变量上。

  2. 收藏信息单独存入AppStorage中,不与用户信息userData绑定。

第一种方案的代码实现如下:

export interface UserData {
  id: string;
  username: string;
  description: string;
  // 1. 在用户信息UserData上增加用户收藏的资源列表id信息类型定义
  collectedIds: string[];
  // ...
}

//在业务类中获取服务端用户信息
getUserData(): void {
  this.userAccountRepository.getUserData().then((data: UserData) => {
    //2.将用户信息数据存储到AppStorage中
    AppStorage.setOrCreate('userData', data);
  })
}

// 探索模块的文章卡片组件
@Component
export struct ArticleCardView {
   //3.在探索文章列表卡片上通过@StorageLink装饰器获取用户信息对象userData
  @StorageLink('userData') userData: UserData | null = null;
  @Prop articleItem: LearningResource = new LearningResource();

  //4.根据收藏信息数组计算当前文章是否被收藏
  isCollected(): boolean {
    return this.userData && this.userData.collectedIds.some((id: string) => id === this.articleItem.id);
  }

  //7.处理界面点赞交互逻辑:使用@StorageLink装饰器接收的userData状态子属性collectedIds被修改后,新值会同步到AppStorage中
  handleCollected(): void {
    const index = this.userData?.collectedIds.findIndex((id: string) => id === this.articleItem.id);
    if (index === -1) {
      this.userData?.collectedIds.push(resourceId);
    } else {
      this.userData?.collectedIds.splice(index, 1);
    }
    // ...
  }
  
  build(){
    ActionButtonView({
      //5.根据当前文章是否被用户收藏,判断收藏图标是否高亮
      imgResource: this.isCollected() ? $r('app.media.btn_favorites_on') : $r('app.media.btn_favorites_normal'),
      count: this.articleItem.collectionCount,
      textWidth: $r('app.float.star_icon_width')
    })
    .onClick(() => {
      //6.用户点击收藏图标时,调用处理收藏状态修改的函数
      this.handleCollected();
    })
  }
}

这种实现方案下,当用户在“UserInfoView ”组件上重新修改用户描述信息userData.description属性值时,属性值变化将同步回AppStorage中。ArkUI监听到AppStorage中key值为“userData”的值变化,随后通知所有使用了AppStorage中key值为“userData”的组件重新渲染。

在上述界面中,“我的“模块中展示用户信息的组件“UserInfoView ”会重新渲染。由于“探索”模块的文章卡片组件ArticleCardView 通过@StorageLink装饰器绑定了AppStorage中key值为“userData”的变量,所有的文章卡片组件也都会重新渲染。而这些组件与用户描述信息无关,不应该被描述信息的修改变化影响,从而导致渲染刷新。

改为使用上述第二种方案实现,代码如下:

//在业务类中获取用户信息
getUserData(): void {
  this.userAccountRepository.getUserData().then((data: UserData) => {
    //1.将用户收藏信息单独数据存储到AppStorage中
    AppStorage.setOrCreate('collectedIds', data.collectedIds);
    AppStorage.setOrCreate('userData', data);
  })
}

// 探索模块的文章卡片组件
@Component
export struct ArticleCardView {
  //2.通过@StorageLink装饰器获取AppStorage中存储的收藏信息
  @StorageLink('collectedIds') collectedIds: string[] = [];
  @Prop articleItem: LearningResource = new LearningResource();

  //3.根据收藏信息数组计算当前文章是否被收藏  
  isCollected(): boolean {
    return this.collectedIds.some((id: string) => id === this.articleItem.id);
  }

  //6.处理界面点赞交互逻辑:使用@StorageLink装饰器接收的状态collectedIds被修改后,新值会同步到AppStorage中
  handleCollected(): void {
    const index = this.collectedIds.findIndex((id: string) => id === this.articleItem.id);
    if (index === -1) {
      this.collectedIds.push(resourceId);
    } else {
      this.collectedIds.splice(index, 1);
    }
    // ...
  }
  
  build(){
    ActionButtonView({
      //4.根据当前文章是否被用户收藏,判断收藏图标是否高亮
      imgResource: this.isCollected() ? $r('app.media.btn_favorites_on') : $r('app.media.btn_favorites_normal'),
      count: this.articleItem.collectionCount,
      textWidth: $r('app.float.star_icon_width')
    })
    .onClick(() => {
      //5.用户点击收藏图标时,调用处理收藏状态修改的函数
      this.handleCollected();
    })
  }
}

在此方案中,由于文章卡片组件没有绑定AppStorage中key值为“userData”的变量,当用户编辑修改了用户描述userData.description的值时, 文章卡片组件不会重新渲染。

并且,当用户点击文章卡片上的收藏按钮修改文章收藏状态时,变化同步回AppStorage中的key值为“collectedIds”的变量。ArkUI监听到AppStorage中key值为“collectedIds”的值变化,只会通知所有绑定了AppStorage中key值为“collectedIds”变量的组件重新渲染,不会造成“我的“模块用户信息组件“UserInfoView ”重新渲染。

因此,从性能的角度考虑,在使用LocalStorage或AppStorage装饰器存储状态变量时需要合理设计状态的数据结构,避免无意义的渲染刷新。

说明:
过分追求状态结构拆分可能在某些场景导致组件设计过度,不利于维护。此时,可以将对象或类上经常一起改变的几个属性聚合成一个新的对象或类模型,并使用@Observed装饰器修饰,再作为属性挂载到之前的对象或类上。通过此方法,当属性变化时ArkUI只会通知变化给新的对象或类,不会通知最上层的对象。这样既可以有效的减少无用渲染次数,又能使代码更好维护。

如类ClassA上存在属性b、c、d。其中c和d经常一起发生变化,即当c的状态修改时同时也要修改d的状态。

class ClassA{
  b: string;
  c: number;
  d: boolean;
}

此时,将c和d组合在一起做为新的类ClassE的属性并使用@Observed装饰器修饰。对于ClassA去掉c、d属性,新增属性e且其类型为ClassE,设计如下:

class ClassA{
  b: string;
  e: ClassE;
}
@Observed
class ClassE{
  c: number;
  d: boolean;
}

使用此方案,在AppStorage中存入数据结构为ClassA的变量。当ClassA实例的属性e中的属性c的值变化时,状态变化会通知使用ClassE实例的组件重新渲染,不会通知所有使用AppStorage中ClassA实例的组件更新,即只使用了ClassA实例b属性的组件不会重新渲染。

总结:
上述状态变量案例也可以使用此方法去实现防止资源的浪费,使用@Observed修饰一个新类里面包含collectedIds,当collectedIds发生改变只会刷新新类,而不会刷新UserData,从而避免了资源的浪费。

伪代码如下:

export interface UserData {
  id: string;
  username: string;
  description: string;
  e: ClassE;


@Observed
class ClassE{
 // 1. 在用户信息UserData上增加用户收藏的资源列表id信息类型定义
  collectedIds: string[];
  // ...
}
}

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

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

相关文章

【生成模型】学习笔记

生成模型 生成模型概述&#xff08;通俗解释&#xff09; 生成的核心是生成抽象化的内容&#xff0c;利用已有的内容生成没有的/现实未发生的内容。这个过程类似于人类发挥想象力的过程。 生成模型的应用场景非常广泛&#xff0c;可以应用于艺术表达&#xff0c;如画的生成、…

前端规范工程-2:JS代码规范(Prettier + ESLint)

Prettier 和 ESLint 是两个在现代 JavaScript 开发中广泛使用的工具&#xff0c;它们结合起来可以提供以下作用和优势&#xff1a; 代码格式化和风格统一&#xff1a; Prettier 是一个代码格式化工具&#xff0c;能够自动化地处理代码的缩进、空格、换行等格式问题&#xff0c;…

【PHP陪玩系统源码】游戏陪玩系统app,陪玩小程序优势

陪玩系统开发运营级别陪玩成品搭建 支持二开源码交付&#xff0c;游戏开黑陪玩系统: 多客陪玩系统&#xff0c;游戏开黑陪玩&#xff0c;线下搭子&#xff0c;开黑陪玩系统 前端uniapp后端php&#xff0c;数据库MySQL 1、长时间的陪玩APP源码开发经验&#xff0c;始终坚持从客户…

JAVA中的集合有哪些???

引言; Java 中的集合类主要分为两大类:Collection 接口和 Map 接口。前者是存储对象的集合类&#xff0c;后者存储的是键值对(key-value)。 &#xff08;这是在IntelliJ IDEA中使用Diagram功能来操作绘制的UML类图&#xff09; Set&#xff08;实现类&#xff09; HashSet: 基…

安全中心 (SOC) 与 网络运营中心 (NOC)

NOC 和 SOC 之间的区别 网络运营中心 (NOC) 负责维护公司计算机系统的技术基础设施&#xff0c;而安全运营中心 (SOC) 则负责保护组织免受网络威胁。 NOC 专注于防止自然灾害、停电和互联网中断等自然原因造成的网络干扰&#xff0c;而 SOC 则从事监控、管理和保护。 NOC 提…

《Windows PE》3.2.3 NT头-扩展头

■扩展头&#xff08;可选标头仅限映像文件&#xff09; OptionalHeader字段描述了可执行文件的更多细节和布局信息&#xff0c;如图像基址、入口点、数据目录、节表等。它的具体结构取决于文件的机器架构&#xff0c;可以是IMAGE_OPTIONAL_HEADER32&#xff08;32位&#xff…

简单线性回归分析-基于R语言

本题中&#xff0c;在不含截距的简单线性回归中&#xff0c;用零假设对统计量进行假设检验。首先&#xff0c;我们使用下面方法生成预测变量x和响应变量y。 set.seed(1) x <- rnorm(100) y <- 2*xrnorm(100) &#xff08;a&#xff09;不含截距的线性回归模型构建。 &…

如何选择合适的跨境网络专线?

选择合适的跨境网络专线对于保障企业的国际业务顺畅运行至关重要。以下是一些选择跨境网络专线时可以参考的关键点&#xff1a; 服务商的信誉和经验&#xff1a;首先考察服务商的市场声誉和行业经验。一个好的服务商应该拥有良好的客户评价和成功案例&#xff0c;这表明他们有能…

心觉:如何抓住宇宙送来的运气和机会?

Hi&#xff0c;我是心觉&#xff0c;与你一起玩转潜意识、脑波音乐和吸引力法则&#xff0c;轻松掌控自己的人生&#xff01; 挑战每日一省写作186/1000天 赚钱需要系统学习吗 你会发现生活中没什么学历&#xff0c;知道的也没你多&#xff0c;行动力也不一定有你强&#x…

【Orange Pi 5嵌入式应用编程】-用户空间GPIO控制

用户空间GPIO控制 文章目录 用户空间GPIO控制1、嵌入式Linux的GPIO子系统介绍1.1 sysfs文件访问GPIO1.2 通过字符设备访问GPIO1.3 库与工具2、RK3588的GPIO介绍3、用户空间操作GPIO编程3.1 硬件准备3.2 通过libgpio操作GPIO3.2.1 GPIO输出3.2.3 GPIO输入3.2.3 边沿事件检测(中断…

Tomcat监控与调优:比Tomcat Manager更加强大的Psi-Probe

这是一款 Tomcat 管理和监控工具&#xff0c;前身是 Lambda Probe。由于 Lambda Probe 2006不再更新&#xff0c;所以 PSI Probe 算是对其的一个 Fork 版本并一直更新至今。 Probe psi-probe是在相同的开源许可证(GPLV2)下分发的社区驱动的 Lambda Probe &#xff0c;psi-pro…

笔记整理—linux进程部分(4)进程状态与守护进程

进程的几种重要状态&#xff0c;就绪态&#xff1b;运行态&#xff1b;僵尸态&#xff1b;等待态&#xff08;浅度睡眠、深度睡眠&#xff09;&#xff1b;停止态。 就单核CPU而言&#xff0c;在同一时间只能运行一个进程&#xff0c;但实际上要运行的进程不止一个&#xff0c;…

手机实时提取SIM卡打电话的信令声音-(题外、插播一条广告)

手机实时提取SIM卡打电话的信令声音-(题外、插播一条广告) 前言 在去年的差不多这个时候&#xff0c;我们做了一遍外置配件的选型&#xff0c;筛选过滤了一批USB蓝牙配件和type-c转usb的模块。详情可参考《外置配件的电商价格和下载链接的选型.docx》一文&#xff1a;蓝牙电话…

Chainlit集成LlamaIndex并使用通义千问实现和数据库交互的网页对话应用(text2sql)

前言 我在之前的几篇文章中写了如何使用Chainlit集成Langchain并使用通义千问实现和数据库交互的网页对话应用&#xff0c;但是发现Langchain的几种和数据库交互的组件都不够让我满意&#xff0c;虽然已经满足了大部分场景的需求&#xff0c;但是问题还是很多&#xff0c;比如…

Frp服务部署

我这里使用的0.44版本部署如果有其他版本需求范我跟github自行下载 https://github.com/fatedier/frp/releases 相关中文文档可以查看 https://github.com/fatedier/frp/blob/dev/README_zh.md 为了方便我这里使用了同一台服务器部署的 如果需要内网穿透 Frps部署在公网 Frp…

亚洲市场|人工智能对固态硬盘SSD需求影响

随着人工智能(AI)技术的快速发展&#xff0c;对于高效能存储的需求也在日益增长。在亚洲市场中&#xff0c;固态硬盘(SSD)作为关键的数据存储设备&#xff0c;其重要性不言而喻。 扩展阅读&#xff1a; 内存&#xff1a;生成式AI带来全新挑战与机遇 这可能是最清晰的AI存储数…

数学语言符号汇总

&#xff08;任意&#xff09; &#xff08;存在&#xff09; 子集和真子集 一些集合关系符号的Latex写法&#xff1a; 的Latex写法为\not\subset 的Latex写法是\subsetneqq&#xff0c;否定式写法是\subseteqq 1&#xff09;子集 注意&#xff1a;子集只有两种&#xff1…

php email功能实现:详细步骤与配置技巧?

php email发送功能详细教程&#xff1f;如何使用php email服务&#xff1f; 无论是用户注册、密码重置&#xff0c;还是订单确认&#xff0c;电子邮件都是与用户沟通的重要手段。AokSend将详细介绍如何实现php email功能&#xff0c;并提供一些配置技巧&#xff0c;帮助你更好…

C/C++逆向:数据类型识别

在逆向工程中&#xff0c;数据类型识别是理解程序逻辑的重要步骤&#xff0c;因为它直接影响对程序逻辑和功能的理解&#xff0c;识别出数据类型有助于确定变量的含义和函数的行为。在分析恶意软件或者寻找安全漏洞时&#xff0c;识别数据类型能够帮助发现代码中的潜在问题。例…

CTFshow 命令执行 web29~web36(正则匹配绕过)

目录 web29 方法一&#xff1a;include伪协议包含文件读取 方法二&#xff1a;写入文件 方法三&#xff1a;通识符 web30 方法一&#xff1a;filter伪协议文件包含读取 方法二&#xff1a;命令执行函数绕过 方法三&#xff1a;写入文件 web31 方法一&#xff1a;filter伪…