当前位置: 首页 > news >正文

Maui学习笔记- SQLite简单使用案例02添加详情页

  • 我们继续上一个案例,实现一个可以修改当前用户信息功能。

    当用户点击某个信息时,跳转到信息详情页,然后可以点击编辑按钮导航到编辑页面。

创建项目 

  • 我们首先在ViewModels目录下创建UserDetailViewModel。

  • 实现从详情信息页面导航到编辑页面。

  • 这里要使用一个字典来传输对象。

public partial class UserDetailViewModel:ObservableObject,IQueryAttributable
{[ObservableProperty] private User itemUser;public Func<User, Task> ParentRefreshAction { get;set; }[RelayCommand]async Task ShowEditFormAsync(){var context = new UserContext();var editedItem = context.Users.FirstOrDefault(u => u.Id == ItemUser.Id);await Shell.Current.GoToAsync(nameof(UserEditPage),parameters: new Dictionary<string, object>{{ "ParentRefreshAction", (Func<User, Task>)ItemEditedAsync },{ "Item", editedItem }});}async Task ItemEditedAsync(User user) {ItemUser = user;await ParentRefreshAction(user);}public virtual void ApplyQueryAttributes(IDictionary<string, object> query){if (query.TryGetValue("Item",out object currentItem)){ItemUser = (User)currentItem;}if (query.TryGetValue("ParentRefreshAction",out object parentRefreshAction)){ParentRefreshAction = (Func<User, Task>)parentRefreshAction;}query.Clear();}
}
  • 创建用户详情页面,用来显示用户的全部信息。

  • 在ToolbarItem中添加一个命令导航到编辑页面。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:vm="clr-namespace:MauiApp3.ViewModels"Title="用户详情"x:Class="MauiApp3.Views.UserDetailPage"><ContentPage.BindingContext><vm:UserDetailViewModel/></ContentPage.BindingContext><ContentPage.ToolbarItems><ToolbarItem Text="编辑" Command="{Binding ShowEditFormCommand}"/></ContentPage.ToolbarItems><VerticalStackLayout><Label Text="{Binding ItemUser.Id}" FontSize="Large"/><Label Text="{Binding ItemUser.Name}" FontSize="Large"/><Label Text="{Binding ItemUser.Phone}" FontSize="Large"/><Label Text="{Binding ItemUser.Email}" FontSize="Large"/></VerticalStackLayout>
</ContentPage>
  • 更新UserEditViewModel,我们让他直接继承自UserDetailViewModel.

public partial class UserEditViewModel:UserDetailViewModel
{[ObservableProperty] private bool isNewItem;[RelayCommand]private async Task SaveAsync(){await using var context = new UserContext();if (IsNewItem){context.Users.Add(ItemUser);}else{context.Users.Attach(ItemUser);context.Entry(ItemUser).State = Microsoft.EntityFrameworkCore.EntityState.Modified;}context.SaveChangesAsync();await ParentRefreshAction(ItemUser);await Shell.Current.GoToAsync("..");}public override void ApplyQueryAttributes(IDictionary<string, object> query) {if (query.TryGetValue("IsNewItem", out object isNew)) {IsNewItem = (bool)isNew;}base.ApplyQueryAttributes(query);}
}

 

  • 修改MainViewModel。

public partial class MainViewModel:ObservableObject
{[ObservableProperty]ObservableCollection<User> users;[ObservableProperty] private bool refreshing;[RelayCommand]private async Task LoadUsersAsync(){await Task.Run(() =>{using var context = new UserContext();Users = new ObservableCollection<User>(context.Users);});Refreshing = false;}[RelayCommand]private void Showing(){Refreshing = true;}[RelayCommand]private void DeleteUser(User user){var context = new UserContext();context.Users.Remove(user);context.SaveChanges();Users.Remove(user);}[RelayCommand]private async Task ShowNewFormAsync(){await Shell.Current.GoToAsync(nameof(UserEditPage),parameters:new Dictionary<string, object>{{"ParentRefreshAction",(Func<User,Task>)RefreshAddedAsync},{"Item",new User()},{"IsNewItem",true}});}Task RefreshAddedAsync(User addedUser){Users.Add(addedUser);return Task.CompletedTask;}[RelayCommand]async Task ShowDetailFormAsync(User user){await Shell.Current.GoToAsync(nameof(UserDetailPage),parameters:new Dictionary<string, object>{{"ParentRefreshAction",(Func<User,Task>)RefreshEditedAsync},{"Item",user},});}async Task RefreshEditedAsync(User updataUser){int editedItemIndex = -1;await Task.Run(() =>{editedItemIndex = Users.Select((user, index) => new { user, index }).First(x => x.user.Id == updataUser.Id).index;});if (editedItemIndex==-1){return;}Users[editedItemIndex] = updataUser;}
}
  • 注册路由

public partial class AppShell : Shell
{public AppShell(){InitializeComponent();Routing.RegisterRoute(nameof(UserEditPage),typeof(UserEditPage));Routing.RegisterRoute(nameof(UserDetailPage),typeof(UserDetailPage));}
}

 

  • 在主页添加GestureRecognizers,对详情页面的跳转

<Grid RowDefinitions="40,40"ColumnDefinitions="*,*"Padding="10"><Grid.GestureRecognizers><TapGestureRecognizerCommand="{Binding Path=BindingContext.ShowDetailFormCommand,Source={RelativeSource Mode=FindAncestor,AncestorType={x:Type ContentPage}}}"CommandParameter="{Binding}"/></Grid.GestureRecognizers>
 IOS下运行程序

 

我们实现了查看用户详情,并且可以修改

优化代码 

  • 我们要在数据库和视图模型之间提供一个抽象层,它能使项目有不同的模块区分,更明确的分离开。

  • 在项目中往往需要对多张表进行操作,我们创建一个泛型接口,来抽象对数据库中CURD。

  • 在Models中添加一个IRepository.cs文件

public interface IRepository<T> where T:class
{Task<T> GetByIdAsync(int id);Task<IEnumerable<T>> GetAllAsync();Task AddAsync(T item);Task UpdateAsync(T item);Task DeleteAsync(T item);}
  • 实现接口

public class UserRepository: IRepository<User>
{private readonly DbSet<User> DbSet;private readonly UserContext Context;public UserRepository(UserContext context){Context = context;DbSet = Context.Set<User>();}public async Task<User> GetByIdAsync(int id){return await Task.Run(() => DbSet.Find(id));}public async Task<IEnumerable<User>> GetAllAsync(){return await Task.Run(() => DbSet.ToList());}public async Task AddAsync(User item){DbSet.Add(item);await Task.CompletedTask;}public async Task UpdateAsync(User item){DbSet.Attach(item);Context.Entry(item).State = EntityState.Modified;await Task.CompletedTask;}public async Task DeleteAsync(User item){DbSet.Remove(item);await Task.CompletedTask;}
  • 创建工作单元,它的作用主要是作用于不同的数据表。

public class DbUnitOfWork:IDisposable,IUnitOfWork<User>
{readonly UserContext Context=new UserContext();private IRepository<User> userRepository;public IRepository<User> Items => userRepository ??= new UserRepository(Context);public void Dispose(){Context.Dispose();}public async Task SaveAsync(){await Task.Run(() => Context.SaveChangesAsync());}
}public interface IUnitOfWork<T> where T : class {IRepository<T> Items { get; } Task SaveAsync(); 
}
修改MainViewModel
  • LoadUserAsync

[RelayCommand]
private async Task LoadUsersAsync()
{using var uniOfWork = new DbUnitOfWork();Users = new ObservableCollection<User>(await uniOfWork.Items.GetAllAsync());Refreshing = false;
}
  • DeleteUserAsync

[RelayCommand]
private async Task DeleteUser(User user)
{using var uniOfWork = new DbUnitOfWork();await uniOfWork.Items.DeleteAsync(user);await uniOfWork.SaveAsync();Users.Remove(user);
}
修改CustomerEditViewModel
  • SaveAsync

[RelayCommand]
private async Task SaveAsync()
{using var unitOfWork = new DbUnitOfWork();if (IsNewItem)await unitOfWork.Items.AddAsync(ItemUser);elseawait unitOfWork.Items.UpdateAsync(ItemUser);await unitOfWork.SaveAsync();await ParentRefreshAction(ItemUser);await Shell.Current.GoToAsync("..");
}
修改UserDetailViewModel
  • ShowEditFormAsync

[RelayCommand]
async Task ShowEditFormAsync()
{using var unitOfWork = new DbUnitOfWork();var editedItem = await unitOfWork.Items.GetByIdAsync(ItemUser.Id);await Shell.Current.GoToAsync(nameof(UserEditPage),parameters: new Dictionary<string, object>{{ "ParentRefreshAction", (Func<User, Task>)ItemEditedAsync },{ "Item", editedItem }});
}

 数据库验证错误

  • 很多时候我们需要对用户输入的数据进行验证,有很多方法和形式,我们来看看在数据库层面如何做错误验证处理。并反馈在页面给用户。

  • 我们在UserContext中进行简单的数据约束

  • 使用try catch来捕获异常

protected override void OnModelCreating(ModelBuilder modelBuilder)
{//用户邮箱唯一modelBuilder.Entity<User>().HasIndex(u => u.Email).IsUnique();//用户名不能为空modelBuilder.Entity<User>().Property(u => u.Name).IsRequired();//初始化数据modelBuilder.Entity<User>().HasData(new User{Id = 1,Name = "张三",Email = "张三@163.com",Phone = "123456789"});base.OnModelCreating(modelBuilder);
}
修改MainViewModel
  • DeleteCustomerAsync

[RelayCommand]
private async Task DeleteUserAsync(User user)
{using var uniOfWork = new DbUnitOfWork();try{await uniOfWork.Items.DeleteAsync(user);await uniOfWork.SaveAsync();}catch (Exception ex){await Shell.Current.DisplayAlert("Error", ex.Message, "OK");return;}Users.Remove(user);
}
修改UserEditViewModel
  • SaveAsync

[RelayCommand]
private async Task SaveAsync()
{using var unitOfWork = new DbUnitOfWork();try{if (IsNewItem)await unitOfWork.Items.AddAsync(ItemUser);elseawait unitOfWork.Items.UpdateAsync(ItemUser);await unitOfWork.SaveAsync();}catch (Exception ex){await Shell.Current.DisplayAlert("Error", ex.Message, "OK");return;}await ParentRefreshAction(ItemUser);await Shell.Current.GoToAsync("..");
}
IOS下运行程序
  • 这里如果用户没有添加用户名,程序会提出错误信息。

在UI中验证数据 

  • 程序中,在将数据提交到数据库之前可以在UI层执行某些验证规则,可以在用户保存某些修改时告知用户,从而改善用户体验。

修改UserEditViewModel
  • 在编辑用户页面我们添加对用户名和邮箱的验证,并关联到保存命令上。

[NotifyCanExecuteChangedFor(nameof(SaveCommand))] [ObservableProperty]
private bool isEmailValid;[NotifyCanExecuteChangedFor(nameof(SaveCommand))] [ObservableProperty]
private bool isNameValid;bool CanSave() => IsEmailValid&& IsNameValid;[RelayCommand(CanExecute = nameof(CanSave))]
private async Task SaveAsync()
{using var unitOfWork = new DbUnitOfWork();try{if (IsNewItem)await unitOfWork.Items.AddAsync(ItemUser);elseawait unitOfWork.Items.UpdateAsync(ItemUser);await unitOfWork.SaveAsync();}catch (Exception ex){await Shell.Current.DisplayAlert("Error", ex.Message, "OK");return;}await ParentRefreshAction(ItemUser);await Shell.Current.GoToAsync("..");
}

 

  • 在UserEditPage文件中,我们添加一个样式来反馈给用户,并使用工具包中的ValidationBehavior来进行验证和绑定命令。

  • 这个验证很简单,当用户输入的内容不满足条件时,会使用我们设置的样式颜色来显示,保存按钮无法点击,当满足条件时颜色变为正常并可以保存内容。

  • <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"xmlns:vm="clr-namespace:MauiApp3.ViewModels;assembly=MauiApp3"xmlns:toolkit = "http://schemas.microsoft.com/dotnet/2022/maui/toolkit"x:Class="MauiApp3.Views.UserEditPage"Title="新用户"><ContentPage.BindingContext><vm:UserEditViewModel/></ContentPage.BindingContext><ContentPage.Resources><Style TargetType="Entry" x:Key="invalidEntryStyle"><Setter Property="TextColor" Value="Red"></Setter></Style></ContentPage.Resources><Grid><VerticalStackLayout VerticalOptions="Start"><Entry Placeholder="用户名"Text="{Binding ItemUser.Name}"><Entry.Behaviors><toolkit:TextValidationBehavior InvalidStyle="{StaticResource invalidEntryStyle}"IsValid="{Binding IsNameValid}"Flags="ValidateOnValueChanged,ValidateOnAttaching"//内容长度不能小于5MinimumLength="5"/></Entry.Behaviors></Entry><Entry Placeholder="电话"Text="{Binding ItemUser.Phone}"/><Entry Placeholder="Email"Text="{Binding ItemUser.Email}"ReturnCommand="{Binding SaveCommand}"><Entry.Behaviors>//验证是否时正常的邮箱格式<toolkit:EmailValidationBehaviorInvalidStyle="{StaticResource invalidEntryStyle}"IsValid="{Binding IsEmailValid}"Flags="ValidateOnValueChanged,ValidateOnAttaching"/></Entry.Behaviors></Entry><Button Text="保存" Command="{Binding SaveCommand}"/></VerticalStackLayout><ActivityIndicator VerticalOptions="Center"HorizontalOptions="Center"IsRunning="{Binding SaveCommand.IsRunning}"/></Grid>
    </ContentPage>

IOS下运行程序 

相关文章:

Maui学习笔记- SQLite简单使用案例02添加详情页

我们继续上一个案例&#xff0c;实现一个可以修改当前用户信息功能。 当用户点击某个信息时&#xff0c;跳转到信息详情页&#xff0c;然后可以点击编辑按钮导航到编辑页面。 创建项目 我们首先在ViewModels目录下创建UserDetailViewModel。 实现从详情信息页面导航到编辑页面…...

VMware 中Ubuntu无网络连接/无网络标识解决方法【已解决】

参考文档 Ubuntu无网络连接/无网络标识解决方法_ubuntu没网-CSDN博客 再我们正常使用VMware时&#xff0c;就以Ubuntu举例可能有时候出现无网络连接&#xff0c;甚至出现无网络标识的情况&#xff0c;那么废话不多说直接上教程 环境&#xff1a;无网络 解决方案&#…...

完美世界前端面试题及参考答案

如何设置事件捕获和事件冒泡? 在 JavaScript 中,可以通过addEventListener方法来设置事件捕获和事件冒泡。该方法接收三个参数,第一个参数是事件类型,如click、mousedown等;第二个参数是事件处理函数;第三个参数是一个布尔值,用于指定是否使用事件捕获机制。当这个布尔值…...

新时代架构SpringBoot+Vue的理解(含axios/ajax)

文章目录 引言SpringBootThymeleafVueSpringBootSpringBootVue&#xff08;前端&#xff09;axios/ajaxVue作用响应式动态绑定单页面应用SPA前端路由 前端路由URL和后端API URL的区别前端路由的数据从哪里来的 Vue和只用三件套axios区别 引言 我是一个喜欢知其然又知其所以然的…...

代理模式 -- 学习笔记

代理模式学习笔记 什么是代理&#xff1f; 代理是一种设计模式&#xff0c;用户可以通过代理操作&#xff0c;而真正去进行处理的是我们的目标对象&#xff0c;代理可以在方法增强&#xff08;如&#xff1a;记录日志&#xff0c;添加事务&#xff0c;监控等&#xff09; 拿一…...

gif动画图像优化,相同的图在第2,4,6帧中重复出现,会增加图像体积吗?

对于 GIF 图像&#xff0c;情况与 Git 文件存储有所不同。GIF 是一种图像格式&#xff0c;其体积主要取决于图像的内容、颜色数量、优化设置等因素。如果在 GIF 动画中&#xff0c;相同的图像在第 2、4、6 帧中重复出现&#xff0c;是否会增加图像体积&#xff0c;取决于以下几…...

Harmony Next 跨平台开发入门

ArkUI-X 官方介绍 官方文档&#xff1a;https://gitee.com/arkui-x/docs/tree/master/zh-cn ArkUI跨平台框架(ArkUI-X)进一步将ArkUI开发框架扩展到了多个OS平台&#xff1a;目前支持OpenHarmony、Android、 iOS&#xff0c;后续会逐步增加更多平台支持。开发者基于一套主代码…...

阿里巴巴Qwen团队发布AI模型,可操控PC和手机

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

android 音视频系列引导

音视频这块的知识点自己工作中有用到&#xff0c;一直没有好好做一个总结&#xff0c;原因有客观和主观的。 客观是工作太忙&#xff0c;没有成段时间做总结。 主观自己懒。 趁着这次主动离职拿了n1的钱&#xff0c;休息一下&#xff0c;对自己的人生做一下总结&#xff0c;…...

STM32调试手段:重定向printf串口

引言 C语言中经常使用printf来输出调试信息&#xff0c;打印到屏幕。由于在单片机中没有屏幕&#xff0c;但是我们可以重定向printf&#xff0c;把数据打印到串口&#xff0c;从而在电脑端接收调试信息。这是除了debug外&#xff0c;另外一个非常有效的调试手段。 一、什么是pr…...

基于 Jenkins 的测试报告获取与处理并写入 Jira Wiki 的技术总结

title: 基于 Jenkins 的测试报告获取与处理并写入 Jira Wiki 的技术总结 tags: - jenkins - python categories: - jenkins在软件开发的持续集成与持续交付&#xff08;CI/CD&#xff09;流程里&#xff0c;及时、准确地获取并分析测试报告对保障软件质量至关重要。本文将详细…...

Vue.js组件开发-实现导出PDF文件可自定义添加水印及水印样式方向

使用 Vue 实现导出 PDF 文件并添加水印&#xff0c;同时支持设置水印样式、方向和自定义水印内容。 步骤 安装依赖&#xff1a;使用 html2canvas 将 HTML 内容转换为 canvas&#xff0c;使用 jspdf 生成 PDF 文件。创建 Vue 组件&#xff1a;在组件中实现水印生成、HTML 转 c…...

css中的animation

css的animation animation是一个综合属性,是animation-name, animation-duration, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, animation-fill-mode, animation-play-state, and animation-timeline这些属性的简写 不过在…...

四.3 Redis 五大数据类型/结构的详细说明/详细使用( hash 哈希表数据类型详解和使用)

四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09; 文章目录 四.3 Redis 五大数据类型/结构的详细说明/详细使用&#xff08; hash 哈希表数据类型详解和使用&#xff09;2.hash 哈希表常用指令(详细讲解说明)2.1 hset …...

基于Springboot + vue实现的洗衣店订单管理系统

“前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff1a;人工智能学习网站” &#x1f496;学习知识需费心&#xff0c; &#x1f4d5;整理归纳更费神。 &#x1f389;源码免费人人喜…...

用 Scoop 优雅管理 Windows 软件:安装、配置与使用全指南

本篇将主要讲讲如何用「Scoop」优雅管理 Windows 软件&#xff1a;安装、配置与使用全指南 一、Scoop 是什么&#xff1f; Scoop 是一款专为 Windows 设计的命令行软件包管理工具&#xff0c;它能让你像 Linux 系统一样通过命令快速安装、更新和卸载软件。其核心优势包括&…...

深度学习中常用的评价指标方法

深度学习中常用的评价指标方法因任务类型&#xff08;如分类、回归、分割等&#xff09;而异。以下是一些常见的评价指标&#xff1a; 1. 分类任务 准确率&#xff08;Accuracy&#xff09; 定义&#xff1a;正确预测的样本数占总样本数的比例。 公式&#xff1a;AccuracyTPT…...

多协议网关BL110钡铼6路RS485转MQTT协议云网关

多协议网关BL110钡铼6路RS485转MQTT协议云网关是一款集成了多种通信协议的工业级网关设备&#xff0c;专为物联网&#xff08;IoT&#xff09;应用设计。该网关能够将RS485总线设备的数据转化为MQTT协议&#xff0c;通过网络传输到云平台&#xff0c;实现远程监控和数据管理。以…...

Nginx 安装配置指南

Nginx 安装配置指南 引言 Nginx 是一款高性能的 HTTP 和反向代理服务器&#xff0c;同时也可以作为 IMAP/POP3/SMTP 代理服务器。由于其稳定性、丰富的功能集以及低资源消耗而被广泛应用于各种场景。本文将为您详细介绍 Nginx 的安装与配置过程。 系统要求 在安装 Nginx 之…...

二叉树介绍

一.树的概念 树的图&#xff1a; 1.结点的度&#xff1a;一个结点含有子树的个数称为该结点的度&#xff1b; 如上图&#xff1a;A的度为6 2.树的度&#xff1a;一棵树中&#xff0c;所有结点度的最大值称为树的度&#xff1b; 如上图&#xff1a;树的度为6 3.叶子结点或终…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

图解JavaScript原型:原型链及其分析 | JavaScript图解

​​ 忽略该图的细节&#xff08;如内存地址值没有用二进制&#xff09; 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么&#xff1a;保存在堆中一块区域&#xff0c;同时在栈中有一块区域保存其在堆中的地址&#xff08;也就是我们通常说的该变量指向谁&…...

OpenGL-什么是软OpenGL/软渲染/软光栅?

‌软OpenGL&#xff08;Software OpenGL&#xff09;‌或者软渲染指完全通过CPU模拟实现的OpenGL渲染方式&#xff08;包括几何处理、光栅化、着色等&#xff09;&#xff0c;不依赖GPU硬件加速。这种模式通常性能较低&#xff0c;但兼容性极强&#xff0c;常用于不支持硬件加速…...