首页 文章 精选 留言 我的

精选列表

搜索[技能变化],共10002篇文章
优秀的个人博客,低调大师

.NET 8.0 中有哪些新的变化?

1性能提升 .NET 8在整个堆栈中带来了数千项性能改进 。默认情况下会启用一种名为动态配置文件引导优化 (PGO) 的新代码生成器,它可以根据实际使用情况优化代码,并且可以将应用程序的性能提高高达 20%。现在支持的 AVX-512 指令集能够对 512 位数据向量执行并行操作,这意味着可以在更短的时间内处理更多的数据。原始类型(数字及其他类型)现在实现了新的可格式化和可解析接口,这使它们能够直接格式化和解析为 UTF-8,而无需任何转码开销。 2.NET Aspire .NET Aspire 是一个用于使用 .NET 构建弹性、可观察和可配置的云原生应用程序的堆栈。它包括一组针对云原生而增强的精选组件,默认情况下包括遥测、弹性、配置和运行状况检查。结合复杂而简单的本地开发人员体验,.NET Aspire 可以在第 1 天和第 100 天轻松发现、获取和配置云原生应用程序的基本依赖项。 点击这里查看.NET Aspire的预览版本。 3.NET 8 容器增强功能 – 更安全、更紧凑、更高效 使用 .NET 比以往更轻松、更安全地使用容器打包应用程序。每个 .NET 映像都包含一个非 root 用户,从而通过单行配置启用更安全的容器。.NET SDK 工具无需 Dockerfile 即可发布容器映像,并且默认情况下是非 root 的。由于 .NET 基础映像更小,因此可以更快地部署容器化应用程序 - 包括我们映像的新实验变体,这些变体可为本机 AOT 提供真正最小的应用程序大小。选择使用新的 Chiseled Ubuntu 映像变体进行更多安全强化,以进一步减少攻击面。使用 Dockerfile 或 SDK 工具,为任何架构构建应用程序和容器映像。 4原生 AoT – 迈向更高密度可持续计算的旅程 无需等待 JIT(即时)编译器在运行时编译代码。无需部署JIT编译器和IL代码。AOT 应用程序仅部署应用程序所需的代码。应用程序现在可以在不允许使用 JIT 编译器的受限环境中运行。 5人工智能 – 将 AI 融入您的 .NET 应用程序 生成式人工智能和大型语言模型正在改变人工智能领域,使开发人员能够在其应用程序中创建独特的人工智能体验。.NET 8 可以通过 .NET SDK 中一流的开箱即用 AI 功能以及与多种工具的无缝集成来轻松利用 AI。 .NET 8 为该 库带来了多项增强功能,以提高其与生成式 AI 工作负载的兼容性,例如集成 Tensor Primitives。随着人工智能应用程序的兴起,新的工具和 SDK 出现了。我们与众多内部和外部合作伙伴合作,例如Azure OpenAI、Azure Cognitive Search、Milvus、Qdrant和Microsoft Teams,以确保 .NET 开发人员可以通过各自的 SDK 轻松访问各种 AI 模型、服务和平台。此外,开源语义内核SDK 简化了这些 AI 组件与新的和现有应用程序的集成,以帮助您提供创新的用户体验。System.Numerics 现在提供各种示例和参考模板,展示模式和实践,以便开发人员轻松入门: 客户聊天机器人 检索增强生成 使用 Azure AI 服务开发应用程序 6Blazor – 使用 .NET 构建全栈 Web 应用程序 .NET 8 中的 Blazor 可以同时使用服务器和客户端来处理您的所有 Web UI 需求。这是全栈 Web UI!通过专注于优化页面加载时间、可扩展性和提升用户体验的多项新增强功能,开发人员现在可以在同一应用程序中使用Blazor Server 和 Blazor WebAssembly,在运行时自动将用户从服务器转移到客户端。得益于新的基于“Jiterpreter”的运行时和新的内置组件,您的 .NET 代码在 WebAssembly 上的运行速度显着加快。作为增强.NET 8 中整体身份验证、授权和身份管理的一部分,Blazor 现在支持生成完整的基于 Blazor 的身份 UI。 7.NET MAUI – 提升性能、可靠性和开发人员体验 .NET MAUI 提供单一项目系统和单一代码库来构建 WinUI、Mac Catalyst、iOS 和 Android 应用程序。本机 AOT(实验性)现在支持针对类似 iOS 的平台。适用于 .NET MAUI 的新 Visual Studio Code 扩展为您提供了开发跨平台 .NET 移动和桌面应用程序所需的工具。现在支持 Xcode 15 和 Android API 34,允许您瞄准最新版本的 iOS 和 Android。在性能、控件和 UI 元素以及特定于平台的行为方面进行了大量的质量改进,例如桌面交互添加了更好的点击处理、键盘侦听器等。 8 C# 12 功能 – 简化语法以提高开发人员的工作效率 C# 12 让您的编码体验更加高效和愉快。现在,您可以使用简单而优雅的语法在任何类和结构中创建主构造函数。不再需要样板代码来初始化您的字段和属性。使用简洁且富有表现力的语法创建数组、跨度和其他集合类型时会感到高兴。对 lambda 表达式中的参数使用新的默认值。不再需要重载或空检查来处理可选参数。您甚至可以使用usingalias 指令为任何类型添加别名,而不仅仅是命名类型! 8.1集合表达式 在 C# 12 之前,创建集合需要针对不同场景使用不同的语法。初始化所需的与or不同的语法。以下是创建集合的几种方法:List<int>int[]Span<int> int[] x1 = new int[] { 1, 2, 3, 4 }; int[] x2 = Array.Empty<int>(); WriteByteArray(new[] { (byte)1, (byte)2, (byte)3 }); List<int> x4 = new() { 1, 2, 3, 4 }; Span<DateTime> dates = stackalloc DateTime[] { GetDate(0), GetDate(1) }; WriteByteSpan(stackalloc[] { (byte)1, (byte)2, (byte)3 }); 8.2任何类或结构上的主构造函数 C# 12 扩展了主构造函数以适用于所有类和结构,而不仅仅是记录。主构造函数允许在声明类时定义构造函数参数: public class BankAccount(string accountID, string owner) { public string AccountID { get; } = accountID; public string Owner { get; } = owner; public override string ToString() => $"Account ID: {AccountID}, Owner: {Owner}"; } 主构造函数参数最常见的用途是: 作为 base() 构造函数调用的参数。 初始化成员字段或属性。 在实例成员中引用构造函数参数。 删除依赖注入中的样板。 8.3别名任意类型 别名类型是从代码中删除复杂类型签名的便捷方法。using从 C# 12 开始,其他类型在别名指令中有效。例如,这些别名在早期版本的 C# 中无效: using intArray = int[]; // Array types. using Point = (int x, int y); // Tuple type using unsafe ArrayPtr = int*; // Pointer type (requires "unsafe") 8.4默认 lambda 参数 从 C# 12 开始,您可以在 lambda 表达式中声明默认参数: var IncrementBy = (int source, int increment = 1) => source + increment; Console.WriteLine(IncrementBy(5)); // 6 Console.WriteLine(IncrementBy(5, 2)); // 7 8.5内联数组 运行时团队和其他库作者使用内联数组来提高应用的性能。 内联数组使开发人员能够创建固定大小的 struct 类型数组。 具有内联缓冲区的结构应提供类似于不安全的固定大小缓冲区的性能特征。 你可能不会声明自己的内联数组,但当它们从运行时 API 作为 System.Span<T> 或 System.ReadOnlySpan<T> 对象公开时,你将透明地使用这些数组。 [System.Runtime.CompilerServices.InlineArray(10)] public struct Buffer { private int _element0; } 它们的用法与任何其他数组类似: var buffer = new Buffer(); for (int i = 0; i < 10; i++) { buffer[i] = i; } foreach (var i in buffer) { Console.WriteLine(i); } 区别在于编译器可以利用有关内联数组的已知信息。 你可能会像使用任何其他数组一样使用内联数组。 有关如何声明内联数组的详细信息,请参阅有关 struct 类型的语言参考。 9.反射改进 .NET 5 中引入了函数指针,但当时未添加对反射的相应支持。 对函数指针使用 typeof 或反射时(例如分别使用 typeof(delegate*<void>()) 或 FieldInfo.FieldType),返回了 IntPtr。 从 .NET 8 开始,将改为返回 System.Type 对象。 此类型提供对函数指针元数据的访问,包括调用约定、返回类型和参数。 新功能目前仅在 CoreCLR 运行时和 MetadataLoadContext 中实现。已将新的 API 添加到 System.Type(例如 IsFunctionPointer)以及 System.Reflection.PropertyInfo、System.Reflection.FieldInfo 和 System.Reflection.ParameterInfo。 以下代码演示如何使用一些新 API 进行反射。 // Sample class that contains a function pointer field. public unsafe class UClass { public delegate* unmanaged[Cdecl, SuppressGCTransition]<in int, void> _fp; } // ... FieldInfo fieldInfo = typeof(UClass).GetField(nameof(UClass._fp)); // Obtain the function pointer type from a field. Type fpType = fieldInfo.FieldType; // New methods to determine if a type is a function pointer. Console.WriteLine($"IsFunctionPointer: {fpType.IsFunctionPointer}"); Console.WriteLine($"IsUnmanagedFunctionPointer: {fpType.IsUnmanagedFunctionPointer}"); // New methods to obtain the return and parameter types. Console.WriteLine($"Return type: {fpType.GetFunctionPointerReturnType()}"); foreach (Type parameterType in fpType.GetFunctionPointerParameterTypes()) { Console.WriteLine($"Parameter type: {parameterType}"); } // Access to custom modifiers and calling conventions requires a "modified type". Type modifiedType = fieldInfo.GetModifiedFieldType(); // A modified type forwards most members to its underlying type. Type normalType = modifiedType.UnderlyingSystemType; // New method to obtain the calling conventions. foreach (Type callConv in modifiedType.GetFunctionPointerCallingConventions()) { Console.WriteLine($"Calling convention: {callConv}"); } // New method to obtain the custom modifiers. foreach (Type modreq in modifiedType.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers()) { Console.WriteLine($"Required modifier for first parameter: {modreq}"); } 输出: IsFunctionPointer: True IsUnmanagedFunctionPointer: True Return type: System.Void Parameter type: System.Int32& Calling convention: System.Runtime.CompilerServices.CallConvSuppressGCTransition Calling convention: System.Runtime.CompilerServices.CallConvCdecl Required modifier for first parameter: System.Runtime.InteropServices.InAttribute 10.配置绑定源生成器 .NET 8 引入了一个源生成器,用于在 ASP.NET Core 中提供 AOT 和适合剪裁的配置。 该生成器是现有的基于反射的实现的替代方法。 源生成器探测 Configure(TOptions)、Bind 和 Get 调用来从中检索类型信息。 在项目中启用生成器后,编译器将隐式选择生成的方法,而非预先存在的基于反射的框架实现。 无需更改源代码即可使用生成器。 AOT Web 应用中默认启用该生成器。 对于其他项目类型,源生成器默认关闭,但你可通过在项目文件中将 EnableConfigurationBindingGenerator 属性设置为 true 来选择使用它: <PropertyGroup> <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator> </PropertyGroup> 以下代码演示了调用绑定器的示例: using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); IConfigurationSection section = builder.Configuration.GetSection("MyOptions"); // !! Configure call - to be replaced with source-gen'd implementation builder.Services.Configure<MyOptions>(section); // !! Get call - to be replaced with source-gen'd implementation MyOptions options0 = section.Get<MyOptions>(); // !! Bind call - to be replaced with source-gen'd implementation MyOptions options1 = new MyOptions(); section.Bind(options1); WebApplication app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.Run(); public class MyOptions { public int A { get; set; } public string S { get; set; } public byte[] Data { get; set; } public Dictionary<string, string> Values { get; set; } public List<MyClass> Values2 { get; set; } } public class MyClass { public int SomethingElse { get; set; } } 11.针对 Android 应用的 AOT 编译 为了减小应用大小,面向 Android 的 .NET 和 .NET MAUI 应用在发布模式下构建时使用分析的预先 (AOT) 编译模式。 与常规 AOT 编译相比,分析的 AOT 编译所影响的方法更少。 .NET 8 引入了 <AndroidStripILAfterAOT> 属性,你可使用它进一步对 Android 应用进行 AOT 编译,从而更进一步减少应用大小。 <PropertyGroup> <AndroidStripILAfterAOT>true</AndroidStripILAfterAOT> </PropertyGroup> 默认情况下,将 AndroidStripILAfterAOT 设置为 true 会替代默认的 AndroidEnableProfiledAot 设置,从而允许剪裁已 AOT 编译的(几乎)所有方法。 还可通过将两个属性都显式设置为 true 来结合使用分析的 AOT 和 IL 条带化: <PropertyGroup> <AndroidStripILAfterAOT>true</AndroidStripILAfterAOT> <AndroidEnableProfiledAot>true</AndroidEnableProfiledAot> </PropertyGroup> 12.代码分析 .NET 8 包括几个新的代码分析器和修复程序,可帮助验证是否正确且高效地使用 .NET 库 API。 下表总结了新的分析器。 规则 ID 类别 说明 CA1856 性能 未在参数上正确应用 ConstantExpectedAttribute 属性时触发。 CA1857 性能 当参数使用 ConstantExpectedAttribute 添加批注但提供的参数不是常量时触发。 CA1858 性能 若要确定字符串是否以给定前缀开头,最好调用 String.StartsWith,而不是调用 String.IndexOf,然后将结果与零进行比较。 CA1859 性能 此规则建议尽可能将特定局部变量、字段、属性、方法参数和方法返回类型从接口或抽象类型升级到具体类型。 使用具体类型可生成更高质量的代码。 CA1860 性能 若要确定集合类型是否具有任何元素,最好使用 Length、Count 或 IsEmpty,而不是调用 Enumerable.Any。 CA1861 性能 重复调用时,不会重复使用作为参数传递的常量数组,这意味着每次都会创建一个新数组。 若要提高性能,请考虑将数组提取到静态只读字段。 CA1865-CA1867 性能 对于单字符串,char 重载的性能更好。 CA2021 可靠性 Enumerable.Cast(IEnumerable) 和 Enumerable.OfType(IEnumerable) 需要兼容的类型才能正常运行。 泛型类型不支持扩大转换和用户定义的转换。 CA1510-CA1513 可维护性 在构造新的异常实例方面,引发帮助程序比 if 块更简单、更高效。 这四个分析器是为以下例外情况创建的:ArgumentNullException、ArgumentException、ArgumentOutOfRangeException 和 ObjectDisposedException。 13.Core .NET 库 13.1时间抽象 新的 TimeProvider 类和 ITimer 接口添加了时间抽象功能,让你可以在测试方案中模拟时间。 此外,还可以使用时间抽象,通过 Task.Delay 和 Task.WaitAsync 来模拟依赖于时间进度的 Task 操作。 时间抽象支持以下基本时间操作: 检索本地和 UTC 时间 获取用于测量性能的时间戳 创建计时器 以下代码片段演示了一些使用情况示例。 // Get system time. DateTimeOffset utcNow = TimeProvider.System.GetUtcNow(); DateTimeOffset localNow = TimeProvider.System.GetLocalNow(); // Create a time provider that works with a // time zone that's different than the local time zone. private class ZonedTimeProvider : TimeProvider { private TimeZoneInfo _zoneInfo; public ZonedTimeProvider(TimeZoneInfo zoneInfo) : base() { _zoneInfo = zoneInfo ?? TimeZoneInfo.Local; } public override TimeZoneInfo LocalTimeZone => _zoneInfo; public static TimeProvider FromLocalTimeZone(TimeZoneInfo zoneInfo) => new ZonedTimeProvider(zoneInfo); } // Create a timer using a time provider. ITimer timer = timeProvider.CreateTimer(callBack, state, delay, Timeout.InfiniteTimeSpan); // Measure a period using the system time provider. long providerTimestamp1 = TimeProvider.System.GetTimestamp(); long providerTimestamp2 = TimeProvider.System.GetTimestamp(); var period = GetElapsedTime(providerTimestamp1, providerTimestamp2); 13.2UTF8 改进 如果要启用将类型的类似字符串的表示形式写出到目标范围,请在类型上实现新的 IUtf8SpanFormattable 接口。 此新接口与 ISpanFormattable 密切相关,但面向 UTF8 和 Span<byte>,而不是 UTF16 和 Span<char>。 IUtf8SpanFormattable 已在所有基元类型(以及其他)上实现,无论是面向 string、Span<char> 还是 Span<byte>,其共享逻辑完全一致。 它完全支持所有格式(包括新的“B”二进制说明符)和所有区域性。 这意味着现在可以从 Byte、Complex、Char、DateOnly、DateTime、DateTimeOffset、Decimal、Double、Guid、Half、IPAddress、IPNetwork、Int16、Int32、Int64、Int128、IntPtr、NFloat、SByte、Single、Rune、TimeOnly、TimeSpan、UInt16、UInt32、UInt64、UInt128、UIntPtr 和 Version 直接格式化为 UTF8。 新的 Utf8.TryWrite 方法向现有 MemoryExtensions.TryWrite 方法(基于 UTF16)提供基于 UTF8 的对应方法。 可以使用内插字符串语法将复杂表达式直接格式化为 UTF8 字节范围,例如: static bool FormatHexVersion( short major, short minor, short build, short revision, Span<byte> utf8Bytes, out int bytesWritten) => Utf8.TryWrite( utf8Bytes, CultureInfo.InvariantCulture, $"{major:X4}.{minor:X4}.{build:X4}.{revision:X4}", out bytesWritten); 13.3加密 .NET 8 添加了对 SHA-3 哈希基元的支持。 (目前,具有 OpenSSL 1.1.1 或更高版本和 Windows 11 Build 25324 或更高版本的 Linux 支持 SHA-3。)可在其中使用 SHA-2 的 API 现在提供对 SHA-3 的补充。 对于哈希,这包括 SHA3_256、SHA3_384 和 SHA3_512;对于 HMAC,这包括 HMACSHA3_256、HMACSHA3_384 和 HMACSHA3_512;对于其中可配置算法的哈希,这包括 HashAlgorithmName.SHA3_256、HashAlgorithmName.SHA3_384 和 HashAlgorithmName.SHA3_512;对于 RSA OAEP 加密,这包括 RSAEncryptionPadding.OaepSHA3_256、RSAEncryptionPadding.OaepSHA3_384 和 RSAEncryptionPadding.OaepSHA3_512。 以下示例演示如何使用 API(包括 SHA3_256.IsSupported 属性)来确定平台是否支持 SHA-3。 // Hashing example if (SHA3_256.IsSupported) { byte[] hash = SHA3_256.HashData(dataToHash); } else { // ... } // Signing example if (SHA3_256.IsSupported) { using ECDsa ec = ECDsa.Create(ECCurve.NamedCurves.nistP256); byte[] signature = ec.SignData(dataToBeSigned, HashAlgorithmName.SHA3_256); } else { // ... } 13.4基于流的 ZipFile 方法 .NET 8 包含 ZipFile.CreateFromDirectory 的新重载,通过它可以收集目录中包含的所有文件并压缩这些文件,然后将生成的 zip 文件存储到提供的流中。 同样,通过新的 ZipFile.ExtractToDirectory 重载,可提供包含压缩文件的流,并将其内容提取到文件系统中。 下面是新的重载: namespace System.IO.Compression; public static partial class ZipFile { public static void CreateFromDirectory(string sourceDirectoryName, Stream destination); public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, CompressionLevel compressionLevel, bool includeBaseDirectory); public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, CompressionLevel compressionLevel, bool includeBaseDirectory, Encoding? entryNameEncoding); public static void ExtractToDirectory(Stream source, string destinationDirectoryName) { } public static void ExtractToDirectory(Stream source, string destinationDirectoryName, bool overwriteFiles) { } public static void ExtractToDirectory(Stream source, string destinationDirectoryName, Encoding? entryNameEncoding) { } public static void ExtractToDirectory(Stream source, string destinationDirectoryName, Encoding? entryNameEncoding, bool overwriteFiles) { } } 参考资料: https://learn.microsoft.com/zh-cn/dotnet/core/whats-new/dotnet-8#networking 扩展链接: 如何使用 Blazor 框架在前端浏览器中导入/导出 Excel XLSX 如何在.NET电子表格应用程序中创建流程图 如何将实时数据显示在前端电子表格中

优秀的个人博客,低调大师

管理不断变化的 state,Redux + React

「写在前面:Redux 和 React 之间没有关系」 安装 npminstall--saveredux React 绑定库和开发者工具 npminstall--savereact-redux npminstall--save-devredux-devtools redux 三大原则 单一数据源 state 是只读的 使用纯函数执行修改 action创建函数 action 来描述“发生了什么” store 数据的唯一来源 actions.js exportconstSET_LOG_USER='SET_LOG_USER'; //用户登录,存储用户信息 exportconstloginUserTodo=(userData)=>({ type:SET_LOG_USER, userData, }); reducer reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。 使用 reducers 来根据 action 更新 state (previousState,action)=>newState loginReducers.js import{getAuth,getUserData}from'@/utils/localStroage'; import{SET_LOG_USER}from'../actionTypes'; constinitState={ auth:getAuth(),//登录信息储存到localstroage userData:getUserData(), }; exportconstloginReducer=(state=initState,action)=>{ switch(action.type){ caseSET_LOG_USER:{ setAuth(true); setUserData(action.state); return{...state,...action.state,auth:true}; } default: returnstate; } }; reducers.js import{combineReducers}from'redux'; import{loginReducer}from'./loginReducers'; constcombineAllReducers=combineReducers({ loginReducer, }); exportdefaultcombineAllReducers; Store Store 就是把reducer 和 action联系到一起的对象 维持应用的 state; 提供 getState() 方法获取 state; 提供 dispatch(action) 方法更新 state; 通过 subscribe(listener) 注册监听器; 通过 subscribe(listener) 返回的函数注销监听器。 Redux 应用只有一个单一的 store store.js import{createStore}from'redux'; importcombineAllReducersfrom'./reducers'; conststore=createStore(combineAllReducers); exportdefaultstore; 数据流 严格的单向数据流是 Redux 架构的设计核心。 Redux 应用中数据的生命周期遵循下面 4 个步骤: 调用 store.dispatch(action) Redux store 调用传入的 reducer 函数。 根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。 Redux store 保存了根 reducer 返回的完整 state 树 搭配React 安装 react-redux Redux 默认并不包含 React 绑定库,需要单独安装 npminstall--savereact-redux 实现容器组件 容器组件就是使用 store.subscribe() 从 Redux state 树中读取部分数据,并通过 props 来把这些数据提供给要渲染的组件。你可以手工来开发容器组件,但建议使用 React Redux 库的 connect() 方法来生成,这个方法做了性能优化来避免很多不必要的重复渲染。 使用 connect() 前,需要先定义 mapStateToProps 这个函数来指定如何把当前 Redux store state 映射到展示组件的 props 中。例如,VisibleTodoList 需要计算传到 TodoList 中的 todos,所以定义了根据 state.visibilityFilter 来过滤 state.todos 的方法,并在 mapStateToProps 中使用。 import{connect}from'react-redux'; import{loginUserTodo}from'@/redux/actions'; importLoginfrom'./components'; constmapStateToProps=(state)=>({ auth:state.loginReducer.auth, }); constmapDispatchToProps=(dispatch)=>({ loginIn:(data)=>{ dispatch(loginUserTodo(data)); }, }); exportdefaultconnect(mapStateToProps,mapDispatchToProps)(Login); 传入 store 所有容器组件都可以访问 Redux store,所以可以手动监听它。一种方式是把它以 props 的形式传入到所有容器组件中。但这太麻烦了,因为必须要用 store 把展示组件包裹一层,仅仅是因为恰好在组件树中渲染了一个容器组件。 建议的方式是使用指定的 React Redux 组件 来 魔法般的 让所有容器组件都可以访问 store,而不必显示地传递它。只需要在渲染根组件时使用即可。 importReactfrom'react'; importReactDomfrom'react-dom'; import{Provider}from'react-redux'; importAppfrom'./pages/App'; importstorefrom'./redux/store'; ReactDom.render( <Providerstore={store}> <App/> </Provider>, document.getElementById('root'), ); module.hot.accept();

优秀的个人博客,低调大师

Habitica 4.128.0 发布,针对新年的变化

Habitica 4.128.0 发布了。Habitica 是一个习惯养成及生产力应用,让你“游戏人生”。游戏里的奖惩措施能激励用户完成任务,还有一个强大的互动社区为完成任务提供好建议,帮助达成目标,变得健康、勤奋与快乐。 此版本针对 2020 年新年进行了一些相应变更,此外还有其它更改: API Content:2019 年至 2020 年新年庆典的新庆祝帽 Content:在季节性商店和新年贺卡中启用雪球 Chore:Bailey 新闻公告 Client Content:新年装饰品 更新说明: https://github.com/HabitRPG/habitica/releases/tag/v4.128.0

优秀的个人博客,低调大师

IT技术变化快,技术人该何去何从?

正确答案应该是不可知的,毕竟人与人不同,每个人都有自己的难处和想法,以下都是我设想的出路: 第一个大方向是靠跳槽,包括几个选择: (1)向上跳,比如非FLAG跳FLAG;收入上升,技术上升,能力和评价上升; (2)平行跳,FB跳Google,Google跳FB;跳槽,就是在自己工作腻了的地方,去别人工作腻了的地方;平行跳到Google,然后混日子,假设过了20年Google倒了肯定还有新Google出来,接着面试接着混; (3)向下跳,去更次一级的公司混日子,公司倒了就接着跳更差的,实在没人要做contractor; 跳槽是一种心态,反正跳惯了面试比吃鸡还容易还随意。每个公司耗几年,大半辈子就过去了,也快退休了,想那么多干嘛。要求无非是千题在手,大包我有,各公司横着走。 第二个大方向是努力工作: (1)当码工的什么都懂、什么都会,名声好人员熟,自然不容易被淘汰; (2)有些时候,有机会可能还混成manager,管人了,当然,这也并不容易;说不定还把技术给弄夹生了; 第三个大方向是创业,即使能力一般: (1)别人创业,跟着去加入; (2)自己打零工,接私活,写App,做FreeLancer; 第四个大方向是理财: (1)买出租房; (2)学炒股票、玩基金,很多乱七八糟的路子; 第五个大方向是继续改行: (1)卖房子; (2)修下水道; (3)翻修房子; 第六个大方向是什么都不做,能乐呵一天算一天,想: (1)万一还没到淘汰那天,我自己先…… (2)实在不行被裁员了去开Uber,开18轮; (3)熬到59.5岁,开始吃早年的401K,假设那时候还没崩; (4)实在不行了,吃福利; 第七个大方向是回国,不解释,挺好的,机会多,消费低。 综上所述,假设到了36岁以后,我自己个人比较看好的几条路是(按我认为的可行性排序): (1)跳槽; (2)出租房; (3)卖房子当Agent; (4)修下水道; (5)回国; (6)接私活; (7)创业; 还有一定概率技术没过时,人先那什么了,比如65岁还是四处乱蹦跶的程序员但……就悲剧了。 欢迎工作一到五年的Java工程师朋友们加入Java架构开发:744677563 本群提供免费的学习指导 架构资料 以及免费的解答 不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导

优秀的个人博客,低调大师

Android 屏幕旋转时Activity的变化

Android开发文档上专门有一小节解释这个问题。简单来说,Activity是负责与用户交互的最主要机制,任何“设置”(Configuration)的改变都可能对Activity的界面造成影响,这时系统会销毁并重建Activity以便反映新的Configuration。“屏幕方向”(orientation)是一个Configuration,通过查看Configuration类的javadoc可以看到其他Configuration还有哪些:如fontScale、keyboardHidden和locale等等。当屏幕旋转时,这个Configuration就发生了改变,因此当前显示的Activity需要被重建,Activity对象会被终止,它的onPause()、onStop()和onDestroy()方法依次触发,然后一个新的Activity对象被创建,onCreate()方法被触发。假设屏幕旋转前,用户正在手机上填写一个注册表单,如果处理不当,用户会发现旋转后的表单变成空白的了,严重影响使用体验。要解决这个问题有三种方法:方法1:禁止旋转屏幕毫无疑问,这是最懒的办法了,相当于回避了本文提出的问题,方法如下看看就好: 1 2 3 < activity android:name = ".MyActivity" android:screenOrientation = "portrait" android:label = "@string/app_name" > 方法2:旋转后恢复现场既然Activity会被销毁,那么我们就可以使用前文介绍过的“持久化/恢复现场”方法来解决。即在onPause()里将用户当前已经输入的内容保存到数据库或Preference,在onCreate()方法里读取并填充到表单中,这也是官方推荐的方法。需要补充一点,如果Activity重建需要耗费大量资源或需要访问网络导致时间很长,可以实现onRetainNonConfigurationInstance()方法将所需数据先保存到一个对象里,像下面这样: 1 2 3 4 5 @Override public Object onRetainNonConfigurationInstance() { final MyDataObject data = collectMyLoadedData(); return data; } 重建时,在onCreate()方法里通过getLastNonConfigurationInstance()方法获得之前保存的数据,如下所示: 1 2 3 4 5 6 7 8 9 10 @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance(); if (data == null ) { //表示不是由于Configuration改变触发的onCreate() data = loadMyData(); } ... } 方法3:手工处理旋转一般情况下Configuration的改变会导致Activity被销毁重建,但也有办法让指定的Configuration改变时不重建Activity,方法是在AndroidManifest.xml里通过android:configChanges属性指定需要忽略的Configuration名字,例如下面这样: 1 2 3 < activity android:name = ".MyActivity" android:configChanges = "orientation|keyboardHidden" android:label = "@string/app_name" > 这样设置以后,当屏幕旋转时Activity对象不会被销毁——作为替代,Activity的onConfigurationChanged()方法被触发,在这里开发者可以获取到当前的屏幕方向以便做必要的更新。既然这种情况下的Activity不会被销毁,旋转后Activity里正显示的信息(例如文本框中的文字)也就不会丢失了。假如你的应用里,横屏和竖屏使用同一个layout资源文件,onConfigurationChanged()里甚至可以什么都不做。但如果横屏与竖屏使用不同的layout资源文件,例如横屏用res/layout-land/main.xml,竖屏用res/layout-port/main.xml,则必须在onConfigurationChanged()里重新调用setContentView()方法以便新的layout能够生效,这时虽然Activity对象没有销毁,但界面上的各种控件都被销毁重建了,你需要写额外的代码来恢复界面信息。 1 2 3 4 5 6 7 8 9 10 @Override public void onConfigurationChanged(Configuration newConfig) { super .onConfigurationChanged(newConfig); // Checks the orientation of the screen if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { Toast.makeText( this , "横屏模式" , Toast.LENGTH_SHORT).show(); } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ Toast.makeText( this , "竖屏模式" , Toast.LENGTH_SHORT).show(); } } 官方的Android开发文档不建议使用这种方式处理Configuration改变:Note: Using this attribute should be avoided and used only as a last-resort. Please read Handling Runtime Changes for more information about how to properly handle a restart due to a configuration change.最佳实践考虑到旋转屏幕并不是使Activity被销毁重建的唯一因素,仍然推荐前文介绍过的方法:在onPause()里持久化Activity状态,在onCreate()里恢复现场,可以做到一举多得;虽然Google不推荐设置android:configChanges属性的方式,但如果你的Activity横向纵向共用同一个layout文件,方法3无疑是最省事的。 本文转自 我不会抽烟 51CTO博客,原文链接:http://blog.51cto.com/zhouhongyu1989/1284497,如需转载请自行联系原作者

优秀的个人博客,低调大师

jianying-editor技能安装教程

我用AI自动剪了一条35秒短视频,全程没碰剪映时间线 在这里插入图片描述 前几天有个朋友跟我吐槽: "剪映是真好用,但每次导入素材、拖时间线、加字幕、配BGM,一套操作下来,比拍视频还累。" 我太懂这种感觉了。 直到我最近翻到一个开源小工具——jianying-editor,它能让你用Python脚本,一句话就把素材、字幕、配音、BGM全塞进剪映草稿里。 指令 1:豆包你好,帮我安装 Jianyi

优秀的个人博客,低调大师

技能篇:docker的简易教程

虚拟机技术每家公司发展到一定规模都必须考虑的,更好的环境隔离,更好的事故排查,更好的服务部署 docker的原理 docker更换阿里源 docker容器的相关命令 Dockerfile文件编写 docker镜像的相关命令 1 docker的原理 Docker 利用Linux 中的核心分离机制,例如Cgroups,以及Linux的核心Namespace(名字空间)来创建独立的容器。一句话概括起来Docker就是利用Namespace做资源隔离,用Cgroup做资源限制,利用Union FS做容器文件系统的轻量级虚拟化技术。Docker容器的本质还是一个直接运行在宿主机上面的特殊进程,看到的文件系统是隔离后的,但是操作系统内核是共享宿主机OS,所以说Docker是轻量级的虚拟化技术 docker 镜像分层 docker 容器,理解为一个或多个运行进程,而这些运行进程将占有相应的内存,相应的CPU计算资源,相应的虚拟网络设备以及相应的文件系统资源。而 docker 镜像,则像一个未运行的软件包,为 docker 容器的运行提供文件资源。 docker 镜像是分层的。启动镜像时,一个新的可写层会加载到镜像的顶层。这一层通常称为"容器层",之下是"镜像层",可能存在多个层。容器层可以读写,容器所有发生文件变更写都发生在这一层。镜像层只允许读取,read-only 在容器层的文件增删操作,命令安装,都会被记录下来。我们可以通过 commit 命令来创建一个新的镜像 2 docker 更换阿里源 # vi /etc/apt/sources.list ~~更新源 # sudo apt-get clean # sudo apt-get update ~~ 安装vim # apt-get install vim 复制代码 ubuntu 18 的阿里镜像文件。使用下面的数据替换原 sources.list 里的数据 deb http://mirrors.aliyun.com/ubuntu/ trusty main multiverse restricted universe deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main multiverse restricted universe deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main multiverse restricted universe deb http://mirrors.aliyun.com/ubuntu/ trusty-security main multiverse restricted universe deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main multiverse restricted universe deb-src http://mirrors.aliyun.com/ubuntu/ trusty main multiverse restricted universe deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main multiverse restricted universe deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main multiverse restricted universe deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main multiverse restricted universe deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main multiverse restricted universe 复制代码 3 docker容器的相关命令 run 实例化容器 创建一个新的容器并运行:docker run [options] image [command]。image 可以是镜像ID 也可以是仓库名+Tag名:repository[:tag] options 常用参数 -i 以交互模式运行容器,通常与 -t 同时使用 -t 为容器重新分配一个伪输入终端,通常与 -i 同时使用 -d 后台运行容器,并返回容器ID --name=<name> 为容器指定一个名称 -p 指定端口映射,格式为:主机(宿主)端口:容器端口 --net="bridge" 指定容器的网络连接类型,支持bridge/host/none/container: 四种类型 -e ="v" 设置环境变量 复制代码 示例 [root@lwl]# docker run -it -p 80:80 --name mynginx nginx:latest /bin/bash root@b8573233d675:/# ~~ 如果容器的启动命令是依赖伪终端执行,则需要 -it,如 ubuntu:14.04 镜像 ~~ 没有依赖伪终端执行的启动命令,可去掉-it,因为-d 参数会直接返回容器ID [root@lwl]# docker run -it -d -p 80:80 --name mynginx nginx:latest 315cc38afc2f06abb5a2fbb075ebca16455367b2de685cf0c5ba828ab62dd5a1 [root@lwl]# 复制代码 start、stop、restart 启动与停止 ~~ 启动已被停止的容器 。 container 可以是容器名 也可以是容器ID # docker start container ~~ 停止运行中的容器 # docker stop container ~~ 重启容器 # docker restart container 复制代码 ps 容器查询 // 列出容器 # docker ps [-options] -a :显示所有的容器,包括未运行的。 -f :根据条件过滤显示的内容。 --format :指定返回值的模板文件。 -l :显示最近创建的容器。 -n :列出最近创建的n个容器。 --no-trunc :不截断输出。 -q :静默模式,只显示容器编号。 -s :显示总的文件大小 // 相当于在 container 容器里使用 top命令 # docker top container 复制代码 rm 删除容器 # docker rm container 复制代码 cp 文件传输 本机和 docker 容器之前的文件传输 ~~ 复制本地目录 srcPath 到 容器 container 的目录 destPath # docker cp srcPath container:destPath ~~ 复制容器 container 的目录 srcPath 到本地目录 destPath # docker cp container:destPath srcPath 复制代码 exec 在运行的容器中执行命令 docker exec [options] container command -d :分离模式: 在后台运行 -i :即使没有附加也保持STDIN 打开 -t :分配一个伪终端 ~~ 在容器 mynginx 中以交互模式执行容器内 /root/run.sh 脚本 # docker exec -it mynginx /bin/sh /root/run.sh ~~ 在容器 mynginx 中开启一个交互模式的终端 # docker exec -it mynginx /bin/bash 复制代码 logs 获取容器的日志 docker logs [options] container -f 跟踪日志输出 --since 显示某个开始时间的所有日志 -t 显示时间戳 --tail 仅列出最新N条容器日志 [root@lwl]# docker logs -f mynginx 192.168.239.1 - - [10/Jul/2021:16:53:33 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36" "-" 2021/07/10 16:53:33 [error] 5#5: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 192.168.239.1, server: localhost, request: "GET /favicon.ico HTT .... 复制代码 4 Dockerfile文件编写 # docker build -f /path/to/a/Dockerfile 复制代码 // 1 FROM:指定基础镜像,必须为第一个命令,格式: FROM <image> FROM <image>:<tag> // 2 MAINTAINER: 维护者信息 格式: MAINTAINER <name> // 3 ENV:设置环境变量 格式 // 4 ARG:用于指定传递给构建运行时的变量 格式: ARG <name>[=<default value>] // 5 WORKDIR:工作目录,类似于cd命令 格式: WORKDIR /path/to/workdir ENV <key>=<value // 6 ADD:将本地文件添加到容器中,可以访问网络资源,类似wget ADD <src>... <dest> // 7 COPY:功能类似ADD,但是是不会自动解压文件 // 8 RUN:构建镜像时执行的命令 格式 RUN <command> // 9 CMD:构建容器后调用,也就是在容器启动时才进行调用 CMD command param1 param2 (执行shell内部命令) // 10 ENTRYPOINT:配置容器,使其可执行化 ENTRYPOINT command param1 param2 (shell内部命令) 复制代码 Dockerfile 示例 # 基础镜像 FROM centos # 维护者信息 MAINTAINER tianfeiyu #ENV 设置环境变量 ENV PATH /usr/local/nginx/sbin:$PATH #ADD 文件放在当前目录下,拷过去会自动解压 ADD nginx-1.8.0.tar.gz /usr/local/ ADD epel-release-latest-7.noarch.rpm /usr/local/ # RUN 执行以下命令 RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm RUN yum install -y wget lftp gcc gcc-c++ make openssl-devel pcre-devel pcre && yum clean all RUN useradd -s /sbin/nologin -M www # WORKDIR 相当于cd WORKDIR /usr/local/nginx-1.8.0 RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install RUN echo "daemon off;" >> /etc/nginx.conf # CMD 运行以下命令 CMD ["nginx"] 复制代码 5 docker 镜像的相关命令 镜像查看列举:docker images 镜像搜索:docker search <imageName>;imageName 是 repository[:tag] 的模糊词 拉取:docker pull repository[:tag] 删除单个镜像:docker rmi 镜像ID 或者docker rmi repository[:tag] 给镜像打别名(标签):docker tag oldRepository[/:oleTag] newRepository][/:newTag] # docker tag ubuntu:15.10 lwl/ubuntu:v3 复制代码 构建镜像 ~~ 根据 Dockerfile 构建镜像 # docker build -t repository[:tag] dir # docker build -t lwl/ubuntu:v1 . options 参数 --build-arg=[] 设置镜像创建时的变量 -t 镜像的名字及标签,通常 name:tag 或者 name 格式 -f 指定要使用的Dockerfile路径 复制代码 ~~ 根据正在运行的 container 容器 构建镜像 # docker commit [options] container repository[:tag] options 参数 -a :提交的镜像作者; -c :使用Dockerfile指令来创建镜像; -m :提交时的说明文字; -p :在commit时,将容器暂停 复制代码 推送:docker push repository[:tag] 欢迎指正文中错误 创作不易,如果对大家有所帮助,希望大家点赞支持,有什么问题也可以在评论区里讨论😄~ 如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点star: http://github.crmeb.net/u/defu 不胜感激 ! 来自 “开源独尊 ” ,链接: https://ym.baisou.ltd/post/798.html

优秀的个人博客,低调大师

python实用技能之下载视频

python下载夏目友人帐 一般情况下我们使用爬虫更多的应该是爬数据或者图片吧,今天在这里和大家分享一下关于使用爬虫技术来进行视频下载的方法,不仅可以方便的下载一些体积小的视频,针对大容量的视频下载同样试用。 先上个 requests模块的iter_content方法 这里我们使用的是python的requests模块作为例子,需要获取文本的时候我们会使用response.text获取文本信息,使用response.content获取字节流,比如下载图片保存到一个文件,而对于大个的文件我们就要采取分块读取的方式了, requests.get方法的stream 第一步,我们需要设置requests.get的stream参数为True。默认情况下是stream的值为false,它会立即开始下载文件并存放到内存当中,倘若文件过大就会导致内存不足的情

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册