HarmonyOS 应用开发之分布式数据对象跨设备数据同步

场景介绍

传统方式下,设备之间的数据同步,需要开发者完成消息处理逻辑,包括:建立通信链接、消息收发处理、错误重试、数据冲突解决等操作,工作量非常大。而且设备越多,调试复杂度也将同步增加。

其实设备之间的状态、消息发送进度、发送的数据等都是“变量”。如果这些变量支持“全局”访问,那么开发者跨设备访问这些变量就能像操作本地变量一样,从而能够自动高效、便捷地实现数据多端同步。

分布式数据对象即实现了对“变量”的“全局”访问。向应用开发者提供内存对象的创建、查询、删除、修改、订阅等基本数据对象的管理能力,同时具备分布式能力。为开发者在分布式应用场景下提供简单易用的JS接口,轻松实现多设备间同应用的数据协同,同时设备间可以监听对象的状态和数据变更。满足超级终端场景下,相同应用多设备间的数据对象协同需求。与传统方式相比,分布式数据对象大大减少了开发者的工作量。

基本概念

  • 分布式内存数据库
    分布式内存数据库将数据缓存在内存中,以便应用获得更快的数据存取速度,不会将数据进行持久化。若数据库关闭,则数据不会保留。

  • 分布式数据对象
    分布式数据对象是一个JS对象型的封装。每一个分布式数据对象实例会创建一个内存数据库中的数据表,每个应用程序创建的内存数据库相互隔离,对分布式数据对象的“读取”或“赋值”会自动映射到对应数据库的get/put操作。

    分布式数据对象的生命周期包括以下状态:

    • 未初始化:未实例化,或已被销毁。
    • 本地数据对象:已创建对应的数据表,但是还无法进行数据同步。
    • 分布式数据对象:已创建对应的数据表,设备在线且组网内设置同样sessionId的对象数>=2,可以跨设备同步数据。若设备掉线或将sessionId置为空,分布式数据对象退化为本地数据对象。

运作机制

图1 分布式数据对象运作机制

分布式数据对象生长在分布式内存数据库之上,在分布式内存数据库上进行了JS对象型的封装,能像操作本地变量一样操作分布式数据对象,数据的跨设备同步由系统自动完成。

JS对象型存储与封装机制

  • 为每个分布式数据对象实例创建一个内存数据库,通过SessionId标识,每个应用程序创建的内存数据库相互隔离。

  • 在分布式数据对象实例化的时候,(递归)遍历对象所有属性,使用“Object.defineProperty”定义所有属性的set和get方法,set和get中分别对应数据库一条记录的put和get操作,Key对应属性名,Value对应属性值。

  • 在开发者对分布式数据对象进行“读取”或者“赋值”的时候,都会自动调用到set和get方法,映射到对应数据库的操作。

表1 分布式数据对象和分布式数据库的对应关系

分布式对象实例对象实例属性名称属性值
分布式内存数据库一个数据库(sessionID标识)一条数据库记录的key一条数据库记录的value

跨设备同步和数据变更通知机制

分布式数据对象,最重要的功能就是对象之间的数据同步。可信组网内的设备可以在本地创建分布式数据对象,并设置sessionID。不同设备上的分布式数据对象,通过设置相同的sessionID,建立对象之间的同步关系。

如下图所示,设备A和设备B上的“分布式数据对象1”,其sessionID均为session1,这两个对象建立了session1的同步关系。

图2 对象的同步关系

一个同步关系中,一个设备只能有一个对象加入。比如上图中,设备A的“分布式数据对象1”已经加入了session1的同步关系,所以设备A的“分布式数据对象2”就加入失败了。

建立同步关系后,每个Session有一份共享对象数据。加入了同一个Session的对象,支持以下操作:

(1)读取/修改Session中的数据。

(2)监听数据变更,感知其他设备对共享对象数据的修改。

(3)监听状态变更,感知其他设备的加入和退出。

同步的最小单位

关于分布式数据对象的数据同步,值得注意的是,同步的最小单位是“属性”。比如,下图中对象1包含三个属性:name、age和parents。当其中一个属性变更时,则数据同步时只需同步此变更的属性。

图3 数据同步视图

对象持久化缓存机制

分布式对象主要运行在应用程序的进程空间。当调用分布式对象持久化接口时,通过分布式数据库对对象进行持久化和同步,进程退出后数据也不会丢失。

该场景是分布式对象的扩展场景,主要用于以下情况:

  • 在设备上创建持久化对象后APP退出,重新打开APP,创建持久化对象,加入同一个Session,数据可以恢复到APP退出前的数据。

  • 在设备A上创建持久化对象并同步后持久化到设备B后,A设备的APP退出,设备B打开APP,创建持久化对象,加入同一个Session,数据可以恢复到A设备退出前的数据。

资产同步机制

在分布式对象中,可以使用资产类型来描述本地实体资产文件,分布式对象跨设备同步时,该文件会和数据一起同步到其他设备上。当前只支持资产类型,不支持资产类型数组。如需同步多个资产,可将每个资产作为分布式对象的一个根属性实现。

融合资产冲突解决机制

当分布式对象中包含的资产和关系型数据库中包含的资产指向同一个实体资产文件,即两个资产的Uri相同时,就会存在冲突,我们把这种资产称为融合资产。若想解决融合资产的冲突,需要先进行资产的绑定。当应用退出session后,绑定关系随之消失。

约束限制

  • 不同设备间只有相同bundleName的应用才能直接同步。

  • 分布式数据对象的数据同步发生在同一个应用程序下,且同sessionID之间。

  • 不建议创建过多的分布式数据对象,每个分布式数据对象将占用100-150KB内存。

  • 每个分布式数据对象大小不超过500KB。

  • 设备A修改1KB数据,设备B收到变更通知,50ms内完成。

  • 单个应用程序最多只能创建16个分布式数据对象实例。

  • 考虑到性能和用户体验,最多不超过3个设备进行数据协同。

  • 如对复杂类型的数据进行修改,仅支持修改根属性,暂不支持下级属性修改。资产同步机制中,资产类型的数据支持下一级属性修改。

  • 支持JS接口间的互通,与其他语言不互通。

接口说明

以下是分布式对象跨设备数据同步功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见分布式数据对象。

接口名称描述
create(context: Context, source: object): DataObject创建并得到一个分布式数据对象实例。
genSessionId(): string创建一个sessionId,可作为分布式数据对象的sessionId。
setSessionId(sessionId: string, callback: AsyncCallback<void>): void设置同步的sessionId,当可信组网中有多个设备时,多个设备间的对象如果设置为同一个sessionId,就能自动同步。
setSessionId(callback: AsyncCallback<void>): void退出所有已加入的session。
on(type: ‘change’, callback: (sessionId: string, fields: Array<string>) => void): void监听分布式数据对象的数据变更。
off(type: ‘change’, callback?: (sessionId: string, fields: Array<string>) => void): void取消监听分布式数据对象的数据变更。
on(type: ‘status’, callback: (sessionId: string, networkId: string, status: ‘online’ | ‘offline’ ) => void): void监听分布式数据对象的上下线。
off(type: ‘status’, callback?: (sessionId: string, networkId: string, status: ‘online’ |‘offline’ ) => void): void取消监听分布式数据对象的上下线。
save(deviceId: string, callback: AsyncCallback<SaveSuccessResponse>): void保存分布式数据对象。
revokeSave(callback: AsyncCallback<RevokeSaveSuccessResponse>): void撤回保存的分布式数据对象。
bindAssetStore(assetKey: string, bindInfo: BindInfo, callback: AsyncCallback<void>): void绑定融合资产。

开发步骤

跨设备数据同步

以一次分布式数据对象同步为例,说明开发步骤。

  1. 导入@ohos.data.distributedDataObject模块。

    import distributedDataObject from '@ohos.data.distributedDataObject';
    
  2. 请求权限。

    1. 需要申请ohos.permission.DISTRIBUTED_DATASYNC权限,配置方式请参见声明权限。
    2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见向用户申请授权。
  3. 创建并得到一个分布式数据对象实例。

    Stage模型示例:

    // 导入模块
    import distributedDataObject from '@ohos.data.distributedDataObject';
    import UIAbility from '@ohos.app.ability.UIAbility';
    import { BusinessError } from '@ohos.base';
    import window from '@ohos.window';
    
    class ParentObject {
      mother: string
      father: string
    
      constructor(mother: string, father: string) {
        this.mother = mother
        this.father = father
      }
    }
    class SourceObject {
      name: string | undefined
      age: number | undefined
      isVis: boolean | undefined
      parent: Object | undefined
    
      constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {
        this.name = name
        this.age = age
        this.isVis = isVis
        this.parent = parent
      }
    }
    
    class EntryAbility extends UIAbility {
      onWindowStageCreate(windowStage: window.WindowStage) {
        let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
        let source: SourceObject = new SourceObject("jack", 18, false, parentSource);
        let localObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, source);
      }
    }
    

    FA模型示例:

    // 导入模块
    import distributedDataObject from '@ohos.data.distributedDataObject';
    import featureAbility from '@ohos.ability.featureAbility';
    // 获取context
    let context = featureAbility.getContext();
    class ParentObject {
      mother: string
      father: string
      constructor(mother: string, father: string) {
        this.mother = mother
        this.father = father
      }
    }
    class SourceObject {
      name: string | undefined
      age: number | undefined
      isVis: boolean | undefined
      parent: ParentObject | undefined
      constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {
        this.name = name
        this.age = age
        this.isVis = isVis
        this.parent = parent
      }
    }
    let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');
    let source: SourceObject = new SourceObject("jack", 18, false, parentSource);
    // 创建对象,该对象包含4个属性类型:string、number、boolean和Object
    let localObject: distributedDataObject.DataObject = distributedDataObject.create(context, source);
    
  4. 加入同步组网。同步组网中的数据对象分为发起方和被拉起方。

    // 设备1加入sessionId
    let sessionId: string = '123456';
    
    localObject.setSessionId(sessionId);
    
    // 和设备1协同的设备2加入同一个session
    
    // 创建对象,该对象包含4个属性类型:string、number、boolean和Object
    let remoteSource: SourceObject = new SourceObject(undefined, undefined, undefined, undefined);
    let remoteObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, remoteSource);
    // 收到status上线后remoteObject同步数据,即name变成jack,age变成18
    remoteObject.setSessionId(sessionId);
    
  5. 监听对象数据变更。可监听对端数据的变更,以callback作为变更回调实例。

    localObject.on("change", (sessionId: string, fields: Array<string>) => {
      console.info("change" + sessionId);
      if (fields != null && fields != undefined) {
        for (let index: number = 0; index < fields.length; index++) {
          console.info(`The element ${localObject[fields[index]]} changed.`);
        }
      }
    });
    
  6. 修改对象属性,对象属性支持基本类型(数字类型、布尔类型、字符串类型)以及复杂类型(数组、基本类型嵌套等)。

    localObject["name"] = 'jack1';
    localObject["age"] = 19;
    localObject["isVis"] = false;
    let parentSource1: ParentObject = new ParentObject('jack1 mom', 'jack1 Dad');
    localObject["parent"] = parentSource1;
    

    说明:

    针对复杂类型的数据修改,目前仅支持对根属性的修改,暂不支持对下级属性的修改。

    // 支持的修改方式
    let parentSource1: ParentObject = new ParentObject('mom', 'Dad');
    localObject["parent"] = parentSource1;
    // 不支持的修改方式
    localObject["parent"]["mother"] = 'mom';
    
  7. 访问对象。可以通过直接获取的方式访问到分布式数据对象的属性,且该数据为组网内的最新数据。

    console.info(`name:${localObject['name']}`); 
    
  8. 删除监听数据变更。可以指定删除监听的数据变更回调;也可以不指定,这将会删除该分布式数据对象的所有数据变更回调。

    // 删除变更回调
    localObject.off('change', (sessionId: string, fields: Array<string>) => {
      console.info("change" + sessionId);
      if (fields != null && fields != undefined) {
        for (let index: number = 0; index < fields.length; index++) {
          console.info("changed !" + fields[index] + " " + localObject[fields[index]]);
        }
      }
    });
    // 删除所有的变更回调
    localObject.off('change'); 
    
  9. 监听分布式数据对象的上下线。可以监听对端分布式数据对象的上下线。

    localObject.on('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => {
      console.info("status changed " + sessionId + " " + status + " " +  networkId);
      // 业务处理
    });
    
  10. 保存和撤回已保存的数据对象。

    // 保存数据对象,如果应用退出后组网内设备需要恢复对象数据时调用
    localObject.save("local").then((result: distributedDataObject.SaveSuccessResponse) => {
      console.info(`Succeeded in saving. SessionId:${result.sessionId},version:${result.version},deviceId:${result.deviceId}`);
    }).catch((err: BusinessError) => {
      console.error(`Failed to save. Code:${err.code},message:${err.message}`);
    });
    
    // 撤回保存的数据对象
    localObject.revokeSave().then((result: distributedDataObject.RevokeSaveSuccessResponse) => {
      console.info(`Succeeded in revokeSaving. Session:${result.sessionId}`);
    }).catch((err: BusinessError) => {
      console.error(`Failed to revokeSave. Code:${err.code},message:${err.message}`);
    });
    
  11. 删除监听分布式数据对象的上下线。可以指定删除监听的上下线回调;也可以不指定,这将会删除该分布式数据对象的所有上下线回调。

    // 删除上下线回调
    localObject.off('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => {
      console.info("status changed " + sessionId + " " + status + " " + networkId);
      // 业务处理
    });
    // 删除所有的上下线回调
    localObject.off('status');
    
  12. 退出同步组网。分布式数据对象退出组网后,本地的数据变更对端不会同步。

    localObject.setSessionId(() => {
      console.info('leave all session.');
    });
    

跨设备资产同步

分布式对象中加入资产类型属性,可以触发资产同步机制,将资产类型属性所描述的文件同步到其他设备。持有资产文件的设备为发起端,得到资产文件的设备为接收端。

  1. 导入@ohos.data.distributedDataObject@ohos.data.commonType模块。

    import distributedDataObject from '@ohos.data.distributedDataObject';
    import commonType from '@ohos.data.commonType';
    
  2. 请求权限。

    1. 需要申请ohos.permission.DISTRIBUTED_DATASYNC权限。
    2. 同时需要在应用首次启动时弹窗向用户申请授权。
  3. 发起端创建包含资产的分布式对象并加入组网。

    import UIAbility from '@ohos.app.ability.UIAbility';
    import type window from '@ohos.window';
    import distributedDataObject from '@ohos.data.distributedDataObject';
    import commonType from '@ohos.data.commonType';
    import type { BusinessError } from '@ohos.base';
    
    class Note {
      title: string | undefined
      text: string | undefined
      attachment: commonType.Asset | undefined
    
      constructor(title: string | undefined, text: string | undefined, attachment: commonType.Asset | undefined) {
        this.title = title;
        this.text = text;
        this.attachment = attachment;
      }
    }
    
    class EntryAbility extends UIAbility {
      onWindowStageCreate(windowStage: window.WindowStage) {
        let attachment: commonType.Asset = {
          name: 'test_img.jpg',
          uri: 'file://com.example.myapplication/data/storage/el2/distributedfiles/dir/test_img.jpg',
          path: '/dir/test_img.jpg',
          createTime: '2024-01-02 10:00:00',
          modifyTime: '2024-01-02 10:00:00',
          size: '5',
          status: commonType.AssetStatus.ASSET_NORMAL
        }
        // 创建一个自定义笔记类型,其中包含一张图片资产
        let note: Note = new Note('test', "test", attachment);
        let localObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, note);
        localObject.setSessionId('123456');
      }
    }
    
  4. 接收端创建分布式对象并加入组网

    let note: Note = new Note(undefined, undefined, undefined);
    let receiverObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, note);
    receiverObject.on('change', (sessionId: string, fields: Array<string>) => {
      if (fields.includes('attachment')) {
        // 接收端监听到资产类型属性的数据变更时,代表其所描述的资产文件同步完成
        console.info('attachment synchronization completed');
      }
    });
    receiverObject.setSessionId('123456');
    
  5. 若资产为融合资产,可以创建绑定信息,绑定融合资产,以解决融合资产的冲突。

    const bindInfo: distributedDataObject.BindInfo = {
      storeName: 'notepad',
      tableName: 'note_t',
      primaryKey: {
        'uuid': '00000000-0000-0000-0000-000000000000'
      },
      field: 'attachment',
      assetName: attachment.name
    }
    
    localObject.bindAssetStore('attachment', bindInfo, (err: BusinessError) => {
      if (err) {
        console.error('bindAssetStore failed.');
      }
      console.info('bindAssetStore success.');
    });
    

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入理解 Vue3 中使用 v-model技术实现双向数据绑定

引言 在 Vue3 中&#xff0c;v-model 是一个非常有用的指令&#xff0c;它提供了一种简洁的方式来实现组件之间的双向数据绑定。本文将深入探讨 Vue3 中的 v-model 技术&#xff0c;包括它的工作原理、使用场景以及如何在自定义组件中应用 v-model。 一、v-model 的工作原理 在…

OpenHarmony实战:轻量带屏解决方案之恒玄芯片移植案例

本文章基于恒玄科技 BES2600W 芯片的欧智通 Multi-modal V200Z-R 开发板&#xff0c;进行轻量带屏开发板的标准移植&#xff0c;开发了智能开关面板样例&#xff0c;同时实现了 ace_engine_lite、arkui_ui_lite、aafwk_lite、appexecfwk_lite、HDF 等部件基于 OpenHarmony Lite…

探索Flutter框架对iOS应用打包与部署的最佳实践

本文探讨了使用Flutter开发的iOS应用能否上架&#xff0c;以及上架的具体流程。苹果提供了App Store作为正式上架渠道&#xff0c;同时也有TestFlight供开发者进行内测。合规并通过审核后&#xff0c;Flutter应用可以顺利上架。但上架过程可能存在一些挑战&#xff0c;因此可能…

HTML——5.表单、框架、颜色

一、表单 HTML 表单用于在网页中收集用户输入的数据&#xff0c;例如登录信息、搜索查询等。HTML 提供了一系列的表单元素&#xff0c;允许用户输入文本、选择选项、提交数据等。 <!DOCTYPE html> <html lang"en"> <head> <meta charset&q…

视频汇聚/安防监控/视频存储EasyCVR平台EasyPlayer播放器更新:新增【性能面板】

视频汇聚/安防监控/视频存储平台EasyCVR基于云边端架构&#xff0c;可以在复杂的网络环境中快速、灵活部署&#xff0c;平台视频能力丰富&#xff0c;可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云…

【嵌入式硬件】三极管伏安特性曲线-饱和区

1.三极管伏安特性 三极管工作电路如下图所示。 三极管伏安特性曲线 书本上的描述: 截止区:三极管工作在截止状态,当发射结的电压Ube 小于 导通电压(0.6V-0.7V),发射结没有导通;集电结处于反向偏置,没有放大作用。 放大区:三极管的发射极加正向电压(…

聚观早报 | 蔚来推出油车置换补贴;iPhone 16 Pro细节曝光

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 4月02日消息 蔚来推出油车置换补贴 iPhone 16 Pro细节曝光 小米SU7创始版第二轮追加开售 OpenAI将在日本设立办事…

计算机网络-HTTP相关知识-RSA和ECDHE及优化

HTTPS建立基本流程 客户端向服务器索要并验证服务器的公钥。通过密钥交换算法&#xff08;如RSA或ECDHE&#xff09;协商会话秘钥&#xff0c;这个过程被称为“握手”。双方采用会话秘钥进行加密通信。 RSA流程 RSA流程包括四次握手&#xff1a; 第一次握手&#xff1a;客户…

vue项目入门——index.html和App.vue

vue项目中的index.html文件 在Vue项目中&#xff0c;index.html文件通常作为项目的入口文件&#xff0c;它包含了Vue应用程序的基础结构和配置。 该文件的主要作用是引入Vue框架和其他必要的库&#xff0c;以及定义Vue应用程序的启动配置。 import Vue from vue import App …

uniapp-打包app-图标配置

依次找到manifest->App图标配置&#xff0c;然后点击浏览&#xff0c;从本地文件夹中选择你们项目的logo&#xff0c;然后点击自动生成所有图标并替换&#xff0c;即可&#xff1a;

JS-23-原型继承

一、JS的原型继承 在传统的基于Class的语言如Java、C中&#xff0c;继承的本质是扩展一个已有的Class&#xff0c;并生成新的Subclass。 但是&#xff0c;JavaScript由于采用原型继承&#xff0c;根本不存在Class这种类型。 但是办法还是有的。我们先回顾Student构造函数&am…

【最佳实践】高效调优目标检测模型

【最佳实践】高效调优目标检测模型 数据层面算法层面CNN还是Transformer&#xff1f;学习率和优化器损失函数的权重正负样本平衡模型微调与迁移学习模型性能监控与早停可视化与模型解释超参数进化其他方面总结 在深入繁复的计算机视觉领域&#xff0c;目标检测无疑是一项挑战且…

Flutter仿Boss-3.登录页

效果 介绍 在Flutter应用程序中创建登录页面对于用户认证和参与至关重要。登录页面作为用户访问应用程序功能的入口。它应该提供无缝的体验&#xff0c;同时确保安全和隐私。这里仿Boss应用设计的登录页面&#xff0c;我们将创建一个登录页面&#xff0c;允许用户使用手机号码…

Photoshop 2024 Mac/win---图像处理的新纪元,解锁无限创意

Photoshop 2024是一款功能强大的图像处理软件&#xff0c;以其卓越的性能和广泛的应用领域&#xff0c;赢得了设计师、摄影师、图形艺术家等各类创意工作者的青睐。它提供了丰富的绘画和编辑工具&#xff0c;让用户能够轻松进行图片编辑、合成、校色、抠图等操作&#xff0c;实…

网站官网-首页源码html+css+js

网页设计与网站建设作业htmlcssjs 预览 说明 单页面&#xff0c;轮播图 获取&#xff1a;https://hpc.baicaitang.cn/2080.html

部署项目遇到的各种问题总结

文章目录 前言一、后端问题 jar包运行出现错误宝塔面板使用jdk17二、数据库问题 版本问题三、前端问题 连不上后端总结 前言 在做完项目之后&#xff0c;为了让别人访问到自己的网站&#xff0c;就需要部署前端后端以及数据库&#xff0c;但是在部署的过程中出现了各种问题和困…

Vue-Next-Admin:适配手机、平板、PC的开源后台管理模板

摘要&#xff1a;随着移动设备和PC的普及&#xff0c;为了满足不同设备的需求&#xff0c;开发一个能够自适应手机、平板和PC的后台管理系统变得至关重要。本文将介绍一个基于Vue3.x、Typescript、Vite、Element Plus等技术的开源模板库——Vue-Next-Admin&#xff0c;帮助开发…

界面组件DevExpress WinForms v23.2 - 进一步增强HTML CSS支持

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…

CMakeLists.txt编写简单介绍:CMakeLists.txt同时编译.cpp和.cu

关于CMakeLists.txt的相关介绍,这里不赘诉,本人的出发点是借助于CMakeLists.txt掌握基本的C++构建项目流程,下面是本人根据网络资料以及个人实践掌握的资料。 CMakeList.txt构建C++项目 下图是一个使用CUDA实现hello world的项目,一般来说,一个标准的C++项目包括三个文件…

VScode debug python(服务器)

方法一&#xff1a; 创建launch.json文件&#xff1a; launch.json文件地址&#xff1a; launch.json文件内容&#xff1a; {"version": "0.2.0", //指定了配置文件的版本"configurations": [{"name": "Python: Current File&…