Flutter笔记:用于ORM的Floor框架简记

news2024/11/24 0:12:55
Flutter笔记
用于ORM的Floor框架简记

本文地址:https://blog.csdn.net/qq_28550263/article/details/133377191


【介绍】:最近想找用于Dart和Flutter的ORM框架,偶然间发现了Floor,觉得还不错,做一些记录。


  • 1. Floor 框架概述
    • 1.1 框架简介
    • 1.2 框架的构成
    • 1.3 安装
    • 1.4 架构体系
    • 2. 实体(Entity)
      • 支持的类型
      • 主键
      • 外键
      • 索引
      • 忽略字段
      • 继承
    • 数据库视图(Database Views)
  • 3. 数据访问对象(DAO - Data Access Object)
  • 4. 创建数据库
  • 5. 运行代码生成器
  • 6. 使用生成的代码


1. Floor 框架概述

1.1 框架简介

Floor 框架是一个用于在 Flutter 应用程序中使用 SQLite 数据库的开源库。它允许开发者轻松地将本地数据库集成到其应用程序中,并提供了一种类型安全、高效和易于使用的方法来执行数据库操作。

1.2 框架的构成

Floor 框架由多个关键模块组成,每个模块都有特定的功能,协同工作以实现在 Flutter 应用程序中使用 SQLite 数据库的目标。以下是 Floor 框架的主要模块以及它们的作用:

  1. floor(运行时库):

    • 作用: floor 是 Floor 框架的运行时库,包含用于运行时数据库操作的类和方法。
    • 关键类和方法:
    • FloorDatabase:抽象类,表示数据库的根类,生成的数据库类需要扩展它。
    • @Database:用于标记生成的数据库类,指定数据库版本号、实体列表和其他配置信息。
    • databaseBuilder:用于创建数据库实例的方法。
    • 数据库操作方法:生成的数据库类中包含与数据库表的交互方法,例如插入、查询、更新等。
  2. floor_generator(代码生成器):

    • 作用: floor_generator 是 Floor 框架的代码生成器模块,它会根据实体和 DAO 的定义生成数据库访问代码。
    • 生成的代码: 该模块生成包括数据库类、DAO 类以及其他与数据库交互相关的代码,使开发者能够轻松执行数据库操作,而无需手动编写大量的重复代码。
  3. floor_annotation(Floor 框架中的注解)

    • 作用: floor_annotation是Floor 框架中的注解。这些注解用于标记和配置数据库实体(Entities)、数据访问对象(DAOs)以及数据库操作,以便 Floor 框架的代码生成器 floor_generator 可以根据这些注解生成相应的数据库访问代码。
    • floor_annotation 提供了一种类型安全的方式来配置和定义数据库操作,并允许开发者在 Dart 代码中使用注解来描述数据库模型和操作,而无需手动编写大量的重复代码。
    • 例如:
      • @entity:用于标记 Dart 类,表示它是一个数据库实体(Entity)。实体类通常与数据库表相对应,用于定义表的结构和字段。
      • @dao:用于标记抽象类或接口,表示它是数据访问对象(DAO)。DAO 类包含了与数据库表相关的操作方法,如插入、查询、更新和删除。

等等

  1. sqflite(数据库驱动,由floor自动调用):
    • 作用: Floor 框架依赖于 sqflite 插件,它提供了在 Flutter 中与 SQLite 数据库进行底层交互的功能。
    • sqflite 插件负责管理 SQLite 数据库文件、执行原始 SQL 查询和处理数据库事务。

这些模块协同工作,使开发者能够轻松地在 Flutter 应用程序中集成和操作 SQLite 数据库。通过定义实体、DAO 和数据库,然后运行代码生成器,开发者可以获得类型安全、高效和易于维护的数据库访问代码,从而实现数据持久化和本地数据存储的需求。

  1. build_runner:build_runner 通常与 floor_generator 一起使用,以自动生成与数据库操作相关的 Dart 代码。

1.3 安装

flutter pub add floor
flutter pub add floor_generator build_runner --dev

其中:

  • floor: 包含了将在应用程序中使用的所有代码。
  • floor_generator: 包含了生成数据库类的代码。
  • build_runner: 允许以具体的方式生成源代码文件。

将在你的yaml中看到:

dependencies:
  flutter:
    sdk: flutter
  floor: ^1.4.0

dev_dependencies:
  floor_generator: ^1.4.0
  build_runner: ^2.1.2

版本可能随时间变化

1.4 架构体系

存储和访问数据的组件是 实体数据访问对象(DAO) 和 数据库

  • 一个 实体 代表一个 持久类,也就是一个 数据库表
  • Dao管理对实体的访问,并负责内存中 对象表行 之间的 映射
  • 数据库是底层 SQLite 数据库的中心访问点。它 保存Dao,除此之外,还负责 初始化数据库及其 模式。

https://raw.githubusercontent.com/vitusortner/floor/develop/img/floor-architecture.png

2. 实体(Entity)

一个实体是一个持久化类。Floor 自动创建内存中对象与数据库表行之间的映射。可以通过向 Entity 注释添加可选值来向 Floor 提供自定义元数据。它具有表名 tableName 的附加属性,该属性打开了为特定实体使用自定义名称的可能性,而不是使用类名。foreignKeys 允许向实体添加外键。有关如何使用这些内容的更多信息,请参阅外键部分。还支持索引。可以通过将索引添加到实体的 indices 值来使用它们。

  • @entity 注解用于标记了该类为持久类;
  • @PrimaryKey 将类的属性标记为主键列。该属性必须是 int 类型。当启用 autoGenerate 时,SQLite 可以自动生成该值;
  • 必须通过将 @primaryKey 注解添加到一个 int 属性上为你的表添加一个主键,例如:
    // entity/person.dart
    
    import 'package: floor/floor.dart';
    
    
    class Person {
      
      final int id;
    
      final String name;
    
      Person(this.id, this.name);
    }
    
  • @ColumnInfo 使单个表列的自定义映射变得可能。使用该注释,可以为列指定自定义名称。如果要将表的列设置为可空,请将实体字段标记为可空;
  • Floor 自动使用实体类中定义的第一个构造函数来从数据库行创建内存中对象

支持的类型

Floor 实体可以保存以下 Dart 类型的值,这些类型与它们对应的 SQLite 类型相互映射。

  • int - INTEGER
  • double - REAL
  • String - TEXT
  • bool - INTEGER(0 = false,1 = true)
  • Uint8List - BLOB
  • enum - INTEGER(按索引 0…n 记录)

如果要存储可以由上述类型之一表示的复杂 Dart 对象,请查看类型转换器。

主键

每当需要复合主键(例如 n-m 关系)时,设置主键的语法与前面提到的设置主键的方式不同。不是使用 @PrimaryKey 注释字段,而是使用 @Entity 注释的 primaryKey 属性。它接受一个由列名组成的列表,这些列名组成复合主键。

(primaryKeys: ['id', 'name'])
class Person {
  final int id;

  final String name;

  Person(this.id, this.name);
}

外键

向引用实体的 Entity 注释添加 ForeignKeys 列表。childColumns 定义当前实体的列,而 parentColumns 定义父实体的列。在 onUpdate 和 onDelete 属性中定义外键操作后,可以触发外键操作。

// Dog 类,表示数据库中的狗实体
(
  tableName: 'dog', // 指定表名为 'dog'
  foreignKeys: [
    ForeignKey(
      childColumns: ['owner_id'], // 子列名为 'owner_id'
      parentColumns: ['id'], // 父列名为 'id'
      entity: Person, // 引用的实体为 Person 类
    )
  ],
)
class Dog {
  () // PrimaryKey 注解,将 id 标记为主键列
  final int id;

  final String name; // 名称

  (name: 'owner_id') // ColumnInfo 注解,自定义列名为 'owner_id'
  final int ownerId; // 所有者的 ID

  // 构造函数,接受 id、name 和 ownerId 参数
  Dog(this.id, this.name, this.ownerId);
}
  • @Entity 注解标记了 Dog 类,指定了表名为 ‘dog’,同时指定了一个外键(foreign key),该外键将在 ‘owner_id’ 列上创建,它引用了另一个实体 Person。这意味着在数据库中将创建名为 ‘dog’ 的表,并在 ‘owner_id’ 列上创建一个外键,以确保数据的完整性。
  • @PrimaryKey 注解标记了 id 字段作为主键列,这意味着它在数据库中将用作主键。
  • @ColumnInfo 注解标记了 ownerId 字段,并自定义了列名为 ‘owner_id’,这允许在数据库表中使用不同的列名,而不是默认的字段名。
  • 构造函数接受 idnameownerId 参数,并用于创建 Dog 实例。

这些注释和注释说明了 Dog 类的结构,以及如何配置表名、外键和自定义列名。

索引

索引有助于加速查询、连接和分组操作。有关 SQLite 索引的更多信息,请参阅官方文档。要在 floor 中创建索引,请将索引列表添加到 @Entity 注释。下面的示例演示了如何在实体的 custom_name 列上创建索引。

此外,可以使用其 name 属性命名索引。要将索引设置为唯一索引,请使用 unique 属性。

// Person 类,表示数据库中的个人实体
(tableName: 'person', indices: [Index(value: ['custom_name'])])
class Person {
   // PrimaryKey 注解,将 id 标记为主键列
  final int id;

  (name: 'custom_name') // ColumnInfo 注解,自定义列名为 'custom_name'
  final String name; // 姓名

  // 构造函数,接受 id 和 name 参数
  Person(this.id, this.name);
}
  • @Entity 注解标记了 Person 类,指定了表名为 ‘person’,同时指定了一个索引,该索引将在 ‘custom_name’ 列上创建。这意味着在数据库中将创建名为 ‘person’ 的表,并在 ‘custom_name’ 列上创建一个索引,以提高查询性能。
  • @primaryKey 注解标记了 id 字段作为主键列,这意味着它在数据库中将用作主键。
  • @ColumnInfo 注解标记了 name 字段,并自定义了列名为 ‘custom_name’,这允许在数据库表中使用不同的列名,而不是默认的字段名。
    构造函数接受 id 和 name 参数,并用于创建 Person 实例。

忽略字段

默认情况下,实体的 getter、setter 和所有静态字段都会被忽略,并因此从库的映射中排除。如果要进一步忽略字段,应使用 @ignore 注释,并按以下示例中所示应用它。

// 人员类,表示数据库中的人员实体
class Person {
   // PrimaryKey 注解,将 id 标记为主键列
  final int id;

  final String name; // 姓名

   // Ignore 注解,指定 nickname 字段将被忽略
  String nickname; // 昵称

  // 默认情况下被忽略的字段,不会包含在库的映射中
  String get combinedName => "$name ($nickname)";

  // 构造函数,接受 id 和 name 参数
  Person(this.id, this.name);
}

继承

与 Dao 一样,实体(和数据库视图)可以从共同的基类继承并使用它们的字段。实体只需扩展基类。这个构造将被视为如果基类的所有字段都是实体的一部分,这意味着数据库表将包含实体和基类的所有列。

基类不必为类单独注释。它的字段可以像正常的实体列一样进行注释。外键和索引必须在实体中声明,不能在基类中定义。

// 基础对象类,用于继承到其他实体
class BaseObject {
  // 主键标记,指定 id 作为主键列
  ()
  final int id;

  // ColumnInfo 注解,用于自定义列名 create_time
  (name: 'create_time')
  final String createTime;

  // ColumnInfo 注解,用于自定义列名 update_time
  (name: 'update_time')
  final String updateTime;

  // 构造函数,接受 id、updateTime 和可选的 createTime 参数
  BaseObject(
    this.id,
    this.updateTime, {
    String createTime,
  }) : this.createTime = createTime ?? DateTime.now().toString();

  // 重写父类的 props 方法,通常用于数据比较
  
  List<Object> get props => [];
}

// Comment 类,继承自 BaseObject 类
(tableName: 'comments') // Entity 注解,指定表名为 'comments'
class Comment extends BaseObject {
  final String author; // 评论作者

  final String content; // 评论内容

  // 构造函数,接受 author、可选的 id、content、createTime 和 updateTime 参数
  Comment(
    this.author, {
    int id,
    this.content = '', // 默认为空字符串
    String createTime,
    String updateTime,
  }) : super(id, updateTime, createTime: createTime); // 调用父类构造函数,传递 id、updateTime 和 createTime 参数
}

数据库视图(Database Views)

如果希望定义返回与实体不同类型的静态 SELECT 语句,最佳选择是使用 @DatabaseView。数据库视图可以被理解为虚拟表,可以像真实表一样进行查询。

在 Floor 中,数据库视图的定义和使用方式与实体类似,主要区别在于只读访问,这意味着无法执行更新、插入和删除操作。与实体类似,如果未设置 viewName,则使用类名。

('SELECT distinct(name) AS name FROM person', viewName: 'name')
class Name {
  final String name;

  Name(this.name);
}

数据库视图不具有任何外键、主键或索引。相反,您应该手动定义与您的语句匹配的索引,并将它们放入涉及实体的 @Entity 注释中。

与实体类似,setter、getter 和静态字段将自动被忽略(与实体类似),您可以使用 @ignore 注解来指定要忽略的附加字段。

在代码中定义数据库视图后,您需要将它添加到 @Database 注释的 views 字段中:

(version: 1, entities: [Person], views: [Name])
abstract class AppDatabase extends FloorDatabase {
  // DAO 获取器
}

然后,您可以通过 DAO 函数查询视图,就像查询实体一样。

数据库视图可以像实体一样从基类继承共同字段。

现在可以从 DAO 方法返回一个 Stream 对象,该方法查询数据库视图。但是,它将在整个数据库中的任何 @update@insert@delete 事件触发时发出,这可能会对运行时产生相当大的负担。请仅在明白自己在做什么时才添加它!这主要是因为检测哪些实体涉及到数据库视图的复杂性所致。

3. 数据访问对象(DAO - Data Access Object)

这个组件负责管理对底层的 SQLite 数据库的访问。抽象类包含了查询数据库的方法签名,这些方法必须返回 Future 或 Stream。

你可以通过将 @Query 注解添加到方法上来定义查询。SQL 语句必须放在括号中。方法必须返回你查询的实体的 Future 或 Stream。
@insert 标记了一个方法作为插入方法。

// dao/person_dao.dart

import 'package:floor/floor.dart';


abstract class PersonDao {
  // 查询并返回所有的 Person 实体对象列表
  ('SELECT * FROM Person')
  Future<List<Person>> findAllPersons();

  // 根据提供的 id 查询并返回匹配的 Person 实体对象
  // 这是一个异步数据流,当数据发生变化时,会自动更新
  ('SELECT * FROM Person WHERE id = :id')
  Stream<Person?> findPersonById(int id);

  // 将提供的 Person 对象插入到数据库中
  // 这是一个异步操作,不返回任何数据
  
  Future<void> insertPerson(Person person);
}

4. 创建数据库

它必须是一个继承自 FloorDatabase 的抽象类。此外,必须在类的签名中添加 @Database()。确保将创建的实体添加到 @Database 注解的 entities 属性中。为了使生成的代码工作,还需要添加列出的导入项。

确保在这个文件的导入项下面添加 part ‘database.g.dart’;。重要的是要注意 ‘database’ 必须与数据库定义的文件名进行交换。在这种情况下,文件名命名为 database.dart。

// database.dart

import 'dart:async';
import 'package:floor/floor.dart';
import 'package:sqflite/sqflite.dart' as sqflite; 

import 'dao/person_dao.dart'; 
import 'entity/person.dart'; 

part 'database.g.dart'; // Floor代码生成器将在这里生成相关的代码

(version: 1, entities: [Person]) // 声明数据库版本号和包含的实体类
abstract class AppDatabase extends FloorDatabase {
  PersonDao get personDao; // 获取PersonDao实例,用于执行数据库操作
}

5. 运行代码生成器

使用以下命令来运行生成器:

flutter packages pub run build_runner build

上面的命令用于在 Flutter 项目中运行代码生成工具以手动构建生成的代码。build命令 只会运行一次生成过程,然后退出。还可以在 Flutter 项目中使用 build_runner 模块的观察模式,以便在开发过程中自动更新生成的代码。如果想要在文件更改时自动运行它,请使用以下命令:

flutter packages pub run build_runner watch

6. 使用生成的代码

要获取数据库的实例,使用生成的 $FloorAppDatabase 类,它允许访问数据库生成器。名称由 $Floor 和数据库类名组成。传递给 databaseBuilder() 的字符串将是数据库文件的名称。要初始化数据库,调用 build() 并确保等待结果。

为了检索 PersonDao 实例,只需在数据库实例上调用 persoDao getter 即可。
可以参考下面的代码。

// 使用数据库生成器创建数据库实例,传递数据库文件的名称('app_database.db')
final database = await $FloorAppDatabase.databaseBuilder('app_database.db').build();

// 从数据库实例中获取 PersonDao 实例
final personDao = database.personDao;

// 创建一个 Person 对象
final person = Person(1, 'Frank');

// 将新的 Person 对象插入到数据库中
await personDao.insertPerson(person);

// 通过 ID 查找数据库中的 Person 记录
final result = await personDao.findPersonById(1);

// 输出查找到的 Person 记录
print('Found Person: $result');

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

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

相关文章

likeadmin和fastapi的bug

以下内容写于2023年8月11日 bug 1 请求体 - 多个参数 - FastAPI (tiangolo.com)中“请求体中的单一值”处&#xff0c;选python3.6&#xff0c;接口示例代码是 from typing import Unionfrom fastapi import Body, FastAPI from pydantic import BaseModel from typing_exte…

26662-2011 磁粉制动器 阅读笔记

声明 本文是学习GB-T 26662-2011 磁粉制动器. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了磁粉制动器(以下简称制动器)的术语&#xff0c;技术要求&#xff0c;试验方法&#xff0c;检验规则&#xff0c;标志、包装、运 输和…

【项目实战】单数据源多数据库实现多租户

文章目录 前言多租户的四种实现方案单数据源多数据库实现思路代码实现 总结 前言 多租户&#xff08;Multi-Tenancy&#xff09;是一种软件架构设计模式&#xff0c;旨在使单个应用程序可以同时为多个租户&#xff08;如不同组织、用户或客户&#xff09;提供服务&#xff0c;…

Python 小爬虫入门 -- 爬取专栏文章标题保存到 CSV 文件中

爬取专栏文章标题保存到 CSV 文件中目标分析网页代码及理解代码段一代码段二成果展示爬取专栏文章标题保存到 CSV 文件中 目标 从一个网页上抓取数据,并保存到一个 CSV 文件中。 具体是爬取 微机系统与接口上机实验_TD PITE型 专栏里的所有 文章标题 并 保存到 csv 文件 中…

估计、偏差和方差

一、介绍 统计领域为我们提供了很多工具来实现机器学习目标&#xff0c;不仅可以解决训练集上的任务&#xff0c;还可以泛化。基本的概念&#xff0c;例如参数估计、偏差和方差&#xff0c;对于正式地刻画泛化、欠拟合和过拟合都非常有帮助。 二、参数估计 参数估计 是统计学…

35 LRU缓存

LRU缓存 题解1 双map&#xff08;差2个testcases&#xff09;题解2 哈希表双向链表&#xff08;参考&#xff09;题解3 STL:listunordered_map 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正…

通讯网关软件016——利用CommGate X2Access实现OPC数据转储Access

本文介绍利用CommGate X2ACCESS实现从OPC Server读取数据并转储至ACCESS数据库。CommGate X2ACCESS是宁波科安网信开发的网关软件&#xff0c;软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示&#xff0c;实现从OPC Server读取数据并转储至ACCESS…

【前段基础入门之】=>CSS 常用的字体文本属性

导读&#xff1a; 这一章&#xff0c;主要分享一些 CSS 中的一些&#xff0c;常用的 字体和文本方面的属性。 文章目录 字体属性字体大小字体族字体风格字体粗细字体复合写法 文本属性文本间距文本修饰文本缩进文本水平对齐行高vertical-align 字体属性 字体大小 属性名&…

进入Linux的世界

了解Linux的历史 一、Linux发展史二、企业应用现状三、Linux操作系统的各种版本 一、Linux发展史 了解一下硅谷模式: 1945年——1991年是美苏冷战的时间&#xff0c;在这个环境背景下&#xff0c;计算机诞生了。 Linux的发展史&#xff1a; 查看Linux纯源代码 二、企业…

No144.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

完整的 pixel 6a 刷入 AOSP 源码过程记录

基础环境 虚拟机&#xff1a;VMware Workstation 16 Pro 16.0.0 build-16894299 Linux版本&#xff1a;ubuntu-16.04.7-desktop-amd64 设备&#xff1a;pixel 6a&#xff1b;代号&#xff1a;bluejay&#xff1b; 基础软件安装 安装 Git 命令&#xff1a;sudo apt install git …

ATA-M系列功率放大器——应用场景介绍

ATA-M系列是一款理想的单通道功率放大器。最大输出690Vrms电压&#xff0c;800VA功率&#xff0c;可驱动0~100%的阻性或非阻性负载。输出阻抗匹配多个档位可选&#xff0c;客户可根据测试需求调节。 图&#xff1a;ATA-M系列功率放大器 国产品牌安泰电子自主研发的ATA-M系列功率…

MySQL MMM高可用架构

MySQL MMM高可用架构一、MMM概述1、MMM简介2、MMM高可用架构3、MMM故障切换流程 二、MMM高可用双主双从架构部署1、配置主主复制&#xff08;master&#xff09;&#xff0c;主从复制&#xff08;slave&#xff09;1&#xff09;修改 Master1的MySQL配置文件2&#xff09;把配置…

Linux 压缩和解压

1、tar命令&#xff08;复杂&#xff09; 使用tar命令均可以进行压缩和解压缩的操作 语法&#xff1a;tar [-c -v -x -f -z -C] 参数1 参数2 ... 参数N -c&#xff0c;创建压缩文件&#xff0c;用于压缩模式 -v&#xff0c;显示压缩、解压过程&#xff0c;用于查看进度 -x&am…

redis查看耗时久的命令

redis查看耗时久的命令主要有两招&#xff1a;latency和slow log 【latency】 在Redis中&#xff0c;latency命令用于监视和测量Redis实例的延迟。 先进入redis: redis-cli -h 127.0.0.1 -p 24000[查看延迟监视器阈值] CONFIG GET latency-monitor-threshold这个值返回0&…

N9917A|是德科技keysight N9917A微波分析仪

181/2461/8938毫米波频率测量需要精确和谨慎。幸运的是&#xff0c;随着更多的毫米测试设备问世&#xff0c;工程挑战的难度略有下降。信号分析仪现已将同轴器件的直接覆盖范围扩大到110 GHz。这提供了低噪声、高精度和宽带宽的优势&#xff0c;使工程师能够专注于他们的设计和…

ESP32IDF出现Syntax Warning in cmake code at column 47报错

前言 &#xff08;1&#xff09;ESP32的资料还是挺难找的&#xff0c;遇到bug处理起来挺折磨人的。今天分享一个我遇到的bug&#xff0c;以及处理思路。 报错日志 &#xff08;1&#xff09;前天在些博客的时候&#xff0c;做测试发现了一个奇怪的bug&#xff0c;报错日志如下。…

Linux 本地 Docker Registry本地镜像仓库远程连接

目录 Linux 本地 Docker Registry本地镜像仓库远程连接 1. 部署Docker Registry 2. 本地测试推送镜像 3. Linux 安装cpolar 4. 配置Docker Registry公网访问地址 5. 公网远程推送Docker Registry 6. 固定Docker Registry公网地址 Linux 本地 Docker Registry本地镜像仓库…

Linux Kernel 之十 虚拟化、VirtIO 架构及规范、VirtQueue VRing

VirtIO 是一种 IO 半虚拟化解决方案&#xff0c;它提供 Guest OS 与 Hypervisor 虚拟化设备之间的通信框架和编程接口。其主要的优势是能提高性能且减少跨平台带来的兼容性问题。本文重点结合 VirtIO 规范 1.1 版以及 Linux 中的源码来分析 VirtIO 框架。 本文是我自己学习虚拟…

【Vue.js】使用Element入门搭建登入注册界面axios中GET请求与POST请求跨域问题

一&#xff0c;ElementUI是什么&#xff1f; Element UI 是一个基于 Vue.js 的桌面端组件库&#xff0c;它提供了一套丰富的 UI 组件&#xff0c;用于构建用户界面。Element UI 的目标是提供简洁、易用、美观的组件&#xff0c;同时保持灵活性和可定制性 二&#xff0c;Element…