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

unity一键注释日志和反注释日志

开发背景:游戏中日志也是很大的开销,虽然有些日志不打印但是毕竟有字符串的开销,甚至有字符串拼接的开销,有些还有装箱和拆箱的开销,比如Debug.Log(1) 这种

因此需要注释掉,当然还需要提供反注释的功能,需要的时候能立马找回来。

也就是说我们只需要打包的时候处理即可。

1.先检测代码中是否存在不规范代码,类似if(a > 1) Debug.Log("11") 这种代码如果注释掉日志会引起业务逻辑问题需要先找出来,类似的不限于if else for foreach while这些 所以尽可能的要做这个检测,有这类问题直接报错。然后才可以进行后续流程

2.注释Debug.Log或者UnityEngine.Debug.Log,但是有一个问题就是注释之后再注释的问题,所以在调用这个接口前需要调用反注释然后调用注释,这样就可以保证注释是没有问题的。

3.需要提供反注释的代码,主要是配合2的功能

注意事项:大家写代码的过程中想打日志就打日志这个没有限制,打包的时候会自动处理日志。

如果是特别的日志可以打错误日志或者警告,或者自定义日志那种日志不去处理即可。

未来规划是用代码分析给日志加标签,平常开发还是按unity的习惯来我绝对不改变大家的unity标准,在做扩展的时候尽量做到神不知鬼不觉,不让大家有额外操作不让大家有压力。

接着上面扩展 :如下图所示这种else日志不被允许

那么如何进行csharp的语法分析呢 ,我们建一个c#的控制台程序,使用nuget安装3个包

 Microsoft.CodeAnalysis.CSharp 和 Microsoft.CodeAnalysis.CSharp.Workspaces  和Microsoft.CodeAnalysis

分析c#代码检测是否存在if else for while foreach 后直接接Debug.Log或者UnityEngine.Debug.Log完整控制台程序如下

using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace cs_test
{
    internal class Program
    {
        public static long getCurrentTimeMillis()
        {
            long milliseconds = System.DateTime.Now.Ticks / System.TimeSpan.TicksPerMillisecond;
            return milliseconds;
        }
        static long firstTime = 0;
        const float LogWritetimeOutTime = 2f;
        static List<string> strList = new List<string>();
        static void SetTimer(float time = LogWritetimeOutTime)
        {
            System.Timers.Timer timer = new System.Timers.Timer();
            timer.Interval = time;
            timer.AutoReset = false;
            timer.Enabled = true;
            
            timer.Elapsed += (a, b) =>
            {
                using (var fs = new FileStream(logPath, FileMode.Append,FileAccess.Write,FileShare.Write))
                {
                    using (var sw = new StreamWriter(fs, Encoding.UTF8))
                    {
                        lock (strList)
                        {
                            foreach (var item in strList)
                            {
                                sw.WriteLine(item);
                            }
                            strList.Clear();
                        }
                    }
                }
            };
        }
        static void Log(string str,bool useFileSystem = true)
        {
            if(useFileSystem && isLogToFile)
            {
                lock (strList)
                {
                    strList.Add(str);
                }
                if (firstTime == 0)
                {
                    firstTime = getCurrentTimeMillis();
                    SetTimer(LogWritetimeOutTime);
                    return;
                }
                if (getCurrentTimeMillis() - firstTime > LogWritetimeOutTime)
                {
                    firstTime = 0;
                    SetTimer(LogWritetimeOutTime);
                }
            }
            else
            {
                Console.WriteLine(str);
            }
        }
        public class LogStatementWalker : CSharpSyntaxWalker
        {
            public List<Issue> Issues { get; } = new List<Issue>();
            public override void VisitIfStatement(IfStatementSyntax node)
            {
                CheckBlockStatement(node.Statement, node.GetLocation());
                if (node.Else != null)
                {
                    CheckElseStatement(node.Else);
                }
                base.VisitIfStatement(node);
            }
            public override void VisitForStatement(ForStatementSyntax node)
            {
                CheckBlockStatement(node.Statement, node.GetLocation());
                base.VisitForStatement(node);
            }
            public override void VisitForEachStatement(ForEachStatementSyntax node)
            {
                CheckBlockStatement(node.Statement, node.GetLocation());
                base.VisitForEachStatement(node);
            }

            public override void VisitWhileStatement(WhileStatementSyntax node)
            {
                CheckBlockStatement(node.Statement, node.GetLocation());
                base.VisitWhileStatement(node);
            }

            private void CheckElseStatement(ElseClauseSyntax elseClause)
            {
                if (elseClause.Statement is IfStatementSyntax)
                {
                    // Skip checking if it's an "else if" statement
                    return;
                }
                CheckBlockStatement(elseClause.Statement, elseClause.GetLocation());
            }
            private void CheckBlockStatement(StatementSyntax statement, Location location)
            {
                if (statement is BlockSyntax)
                {
                    return;
                }
                var logStatements = statement.DescendantNodes()
                                             .OfType<InvocationExpressionSyntax>()
                                             .Where(inv => inv.Expression is MemberAccessExpressionSyntax memberAccess &&
                                                           (memberAccess.Name.ToString() == "Log" || memberAccess.Name.ToString() == "Debug.Log"))
                                             .ToList();
                foreach (var logStatement in logStatements)
                {
                    var lineSpan = logStatement.GetLocation().GetLineSpan();
                    Issues.Add(new Issue
                    {
                        LineNumber = lineSpan.StartLinePosition.Line + 1,
                        Message = $"Debug.Log or UnityEngine.Debug.Log found in a single-line {statement.Kind()} statement without braces."
                    });
                }
            }
        }
        public class Issue
        {
            public int LineNumber { get; set; }
            public string Message { get; set; }
        }
        static string unityRoot;
        static string logPath = "D:/tempLog.txt";
        static bool isLogToFile = true;
        static void Main(string[] args)
        {
            List<string> pathList = new List<string>();
            if (args == null || args.Length == 0)
            {
                var path = System.Environment.CurrentDirectory;
                path = path.Substring(0, path.IndexOf("tools"));
                path = path.Replace("\\", "/");
                args = new string[]
                {
                     path + "Assets/HotUpdate"
                };
            }
            unityRoot = args[0].Substring(0, args[0].IndexOf("Assets"));
            logPath = unityRoot + "tools/tempLog.txt";
            if(File.Exists(logPath))
            {
                File.Delete(logPath);
            }
            pathList.AddRange(args);
            isLogToFile = bool.Parse(pathList[pathList.Count - 1]);
            pathList.RemoveAt(pathList.Count - 1);
            List<string> allCsharpFiles = new List<string>();
            foreach(var path_item in pathList)
            {
                allCsharpFiles.AddRange(Directory.GetFiles(path_item, "*.cs", SearchOption.AllDirectories));
            }
            bool hasInsue = false;
            strList.Clear();
            foreach(var item in allCsharpFiles)
            {
                string sourceCode = File.ReadAllText(item);
                SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceCode);
                CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
                var diagnostics = tree.GetDiagnostics();
                if (diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error))
                {
                    Log("Syntax errors found in the source code.");
                    return;
                }
                var walker = new LogStatementWalker();
                walker.Visit(root);
                foreach (var issue in walker.Issues)
                {
                    hasInsue = true;
                    var itemName = item.Substring(item.IndexOf("Assets"));
                    Log($"请使用括号封装你的日志! at line {issue.LineNumber}: {itemName}");
                }
            }
            if(!hasInsue)
            {
                Log("未发现不规范代码");
            }
        }
        /// <summary>
        /// 检测判断if语句后面是否带花括号,由于在for循环中经常会遇到if不带花括号的情况,故这个检测暂时放弃 不然
        /// 会有非常多的代码提示 
        /// 这里的代码暂时封存 
        /// </summary>
        /// <param name="args"></param>
        static void Main2(string[] args)
        {

            if(File.Exists(logPath))
            {
                File.Delete(logPath);
            }
            File.Create(logPath).Dispose();
            string directoryPath = "D:\\qiangsheng_wx\\Assets\\HotUpdate"; // 替换为你的目录路径
            // 获取目录中的所有 .cs 文件
            string[] files = Directory.GetFiles(directoryPath, "*.cs", SearchOption.AllDirectories);
            foreach (string filePath in files)
            {
                Log($"Processing file: {filePath}");
                string code = File.ReadAllText(filePath);
                SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
                var root = tree.GetRoot();
                var walker = new IfElseSyntaxWalker();
                walker.Visit(root);
            }
            Console.ReadLine();
        }
        class IfElseSyntaxWalker : CSharpSyntaxWalker
        {
            public override void VisitIfStatement(IfStatementSyntax node)
            {
                base.VisitIfStatement(node);

                // 检查 if 语句是否有花括号
                if (node.Statement is BlockSyntax) { }
                else
                {
                    var str = $"警告: if 语句 at line {node.GetLocation().GetLineSpan().StartLinePosition.Line + 1} does not use 花括号";
                    Log(str);
                }
                // 检查 else 语句是否有花括号
                if (node.Else != null)
                {
                    if (node.Else.Statement is BlockSyntax) { }
                    else
                    {
                        var str = $"警告: else 语句 at line {node.Else.GetLocation().GetLineSpan().StartLinePosition.Line + 1} does not use 花括号";
                        Log(str);
                    }
                }
            }
        }
    }
}
控制台程序做完之后还需要对接到unity项目中,我是直接将微信api的代码分析回调日志直接写入了某个文件,让unity调用这个exe文件,你可以封装成bat将参数传给exe执行,unity那边读取文件将文本内容Debug.LogError输出即可。

上诉工作做完了之后就可以提供注释和反注释的代码接口了,将下述代码封装到你的编辑器工具类中或者某个编辑器类下的对象中 

 private const string LogPattern = @"(?s)(Debug\.Log\s*\(.*?\);|UnityEngine\.Debug\.Log\s*\(.*?\);)";
    private const string unLogPattern = @"(?s)/\*(\s*Debug\.Log\s*\(.*?\);\s*|UnityEngine\.Debug\.Log\s*\(.*?\);\s*)\*/";

    public static void ForbidLog(string filePath)
    {
        
        string fileContent = File.ReadAllText(filePath,Encoding.UTF8);

     // 正则表达式匹配 Debug.Log 调用及其相关代码
     string pattern = LogPattern;
        string replacement = @"/*$1*/";

     if(!Regex.IsMatch(fileContent, pattern))
     {
         return;
     }
        string newContent = Regex.Replace(fileContent, pattern, replacement);

     using (StreamWriter writer = new StreamWriter(filePath, false, new UTF8Encoding(false)))
     {
         writer.Write(newContent);
     }
 }

    public static void UnForbidLog(string filePath)
    {
        
        string fileContent = File.ReadAllText(filePath, Encoding.UTF8);

     // 正则表达式匹配被注释的 Debug.Log 调用及其相关代码
     string pattern = unLogPattern;
        string replacement = @"$1";

     if (!Regex.IsMatch(fileContent, pattern))
     {
         return;
     }

     string newContent = Regex.Replace(fileContent, pattern, replacement);

     using (StreamWriter writer = new StreamWriter(filePath, false, new UTF8Encoding(false)))
     {
         writer.Write(newContent);
     }
     
    }
    [MenuItem("Tools/日志/注释所有日志", priority = -1999)]
 public static void ReMoveLog()
 {
     
     var listFolder = new List<string>();
     listFolder.Add(System.Environment.CurrentDirectory + "/Assets/HotUpdate");
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/SDKTool");
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/DevWork");
     var allCsharpFiles = new List<string>();    
     foreach(string folder in listFolder)
     {
         allCsharpFiles.AddRange(Directory.GetFiles(folder, "*.cs",SearchOption.AllDirectories));
     }
     string[] filters = new string[]
     {
         "InitCtrl.cs",
         "Global.cs",
         "LoginPanel.cs",
         "LoginCode.cs",
         "ProductModel.cs",
     };
     foreach(string csFile in allCsharpFiles)
     {
         var filename = Path.GetFileName(csFile);
         if(filters.Contains(filename))
         {
             Debug.Log("跳过过滤文件" + csFile);
             continue;
         }
ForbidLog(csFile);
        }
        allCsharpFiles.Clear();
        listFolder.Clear();
        listFolder = null;
        allCsharpFiles = null;
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }
    [MenuItem("Tools/日志/反注释所有日志", priority = -1999)]
    public static void UnReMoveLog()
    {
        var listFolder = new List<string>();
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/HotUpdate");
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/SDKTool");
        listFolder.Add(System.Environment.CurrentDirectory + "/Assets/DevWork");
        var allCsharpFiles = new List<string>();
        foreach (string folder in listFolder)
        {
allCsharpFiles.AddRange(Directory.GetFiles(folder, "*.cs", SearchOption.AllDirectories));
        }
        foreach (string csFile in allCsharpFiles)
        {
UnForbidLog(csFile);
        }
     allCsharpFiles.Clear();
     listFolder.Clear();
     listFolder = null;
     allCsharpFiles = null;
     AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    }

运行截图:

相关文章:

unity一键注释日志和反注释日志

开发背景&#xff1a;游戏中日志也是很大的开销&#xff0c;虽然有些日志不打印但是毕竟有字符串的开销&#xff0c;甚至有字符串拼接的开销&#xff0c;有些还有装箱和拆箱的开销&#xff0c;比如Debug.Log(1) 这种 因此需要注释掉&#xff0c;当然还需要提供反注释的功能&am…...

VBA数据库解决方案第十五讲:Recordset集合中单个数据的精确处理

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…...

甄选范文“论软件需求管理”,软考高级论文,系统架构设计师论文

论文真题 软件需求管理是一个对系统需求变更了解和控制的过程。需求管理过程与需求开发过程相互关联,初始需求导出的同时就要形成需求管理规划,一旦启动了软件开发过程,需求管理活动就紧密相伴。 需求管理过程中主要包含变更控制、版本控制、需求跟踪和需求状态跟踪等4项活…...

Android Studio Dolphin 中Gradle下载慢的解决方法

我用的版本Android Studio Dolphin | 2021.3.1 Patch 1 1.Gradle自身的版本下载慢 解决办法&#xff1a;修改gradle\wrapper\gradle-wrapper.properties中的distributionUrl 将https\://services.gradle.org/distributions为https\://mirrors.cloud.tencent.com/gradle dis…...

Excel实现省-市-区/县级联

数据准备 准备省份-城市映射数据&#xff0c;如下&#xff1a; 新建sheet页&#xff0c;命名为&#xff1a;省-市数据源&#xff0c;然后准备数据&#xff0c;如下所示&#xff1a; 准备城市-区|县映射数据&#xff0c;如下&#xff1a; 新建sheet页&#xff0c;命名为&#x…...

【优化代码结构】函数的参数归一化

某些封装的函数&#xff0c;其参数具有多样性&#xff0c;会导致函数中会增加非常多的分支&#xff0c;比如下面这个 format 函数有如下几种参数方式&#xff0c;其中 formatter 会有很多种情况 date&#xff1a;日期对象formatter&#xff1a; ‘date’&#xff1a;格式化日期…...

CSS中height设置100vh和100%的区别

文章目录 CSS中height设置100vh和100%的区别一、引言二、高度设置的区别1、100%1.1、父元素高度固定1.2、父元素高度未定义 2、100vh2.1、视口高度2.2、不受父元素限制 三、总结 CSS中height设置100vh和100%的区别 一、引言 在前端开发中&#xff0c;我们经常需要设置元素的高…...

红米k60至尊版工程固件 MTK芯片 资源预览 刷写说明 与nv损坏修复去除电阻图示

红米k60至尊版机型代码为:corot。 搭载了联发科天玑9200+处理器。此固件mtk引导为MT6985。博文将简单说明此固件的一些特点与刷写注意事项。对于NV损坏的机型。展示修改校验电阻的图示。方便改写参数等 通过博文了解 1💝💝💝-----此机型工程固件的资源刷写注意事项 2…...

QEMU使用Qemu-Guest-Agent传输文件、执行指令等

简介 之前介绍过qemu传输文件&#xff0c;使用的挂载 / samba方式 &#xff1a;Qemu和宿主机不使用外网进行文件传输。 这是一种方式&#xff0c;这里还有另一种方式&#xff1a;使用Qemu-Guest-Agent&#xff0c;后面简称qga。 官网介绍&#xff1a;https://www.qemu.org/d…...

【漏洞复现】金和OA C6 GeneralXmlhttpPage.aspx Sql注入漏洞

免责声明: 本文旨在提供有关特定漏洞的信息,以帮助用户了解潜在风险。发布此信息旨在促进网络安全意识和技术进步,并非出于恶意。读者应理解,利用本文提到的漏洞或进行相关测试可能违反法律或服务协议。未经授权访问系统、网络或应用程序可能导致法律责任或严重后果…...

复数表示的电场

Exm加是复振幅&#xff0c;这是用复数表示电场&#xff0c;并提取只与空间有关的项复振幅就是复数表示电场&#xff0c;且把与空间xyz有关的量提取出来 经过验证实数E0cos&#xff08;wtδx&#xff09;对t求导&#xff0c;等于E0e^j(wtδx)对t求导再取实部 实数表示电磁波cos…...

常用快捷键整理

用加粗标注的是我个人使用时常用的&#xff0c;其实这个全凭个人喜好&#xff0c;大家可以熟悉一下自己喜欢的&#xff0c;都多试试&#xff0c;把觉得有用的记一下&#xff0c;多使用&#xff0c;后续写代码效率就会提高一些) 常用 VS 运行调试程序快捷键 编译 . 编译程序&a…...

【Transformer】长距离依赖

在自然语言处理&#xff08;NLP&#xff09;中&#xff0c;长距离依赖&#xff08;Long-Range Dependencies&#xff09;指的是在文本中相隔较远的两个或多个元素之间的依赖关系。这些依赖关系可以是语法上的&#xff0c;也可以是语义上的。例如&#xff0c;在句子中&#xff0…...

Git傻傻分不清楚(下)

进入Idea编译器 File -> New -> Project from Version Control -> URL (这个路径是要拉取项目的Github路径哦~) 设置成maven项目...

golang学习笔记27-反射【重要】

本节也是GO核心部分&#xff0c;很重要。包括基本类型的反射&#xff0c;结构体类型的反射&#xff0c;类别方法Kind()&#xff0c;修改变量的值。 目录 一、概念&#xff0c;基本类型的反射二、结构体类型的反射三、类别方法Kind()四、修改变量的值 一、概念&#xff0c;基本…...

利用Puppeteer-Har记录与分析网页抓取中的性能数据

引言 在现代网页抓取中&#xff0c;性能数据的记录与分析是优化抓取效率和质量的重要环节。本文将介绍如何利用Puppeteer-Har工具记录与分析网页抓取中的性能数据&#xff0c;并通过实例展示如何实现这一过程。 Puppeteer-Har简介 Puppeteer是一个Node.js库&#xff0c;提供…...

YOLOv5改进系列(1)——添加CBAM注意力机制

一、如何理解注意力机制 假设你正在阅读一本书&#xff0c;同时有人在你旁边说话。当你听到某些关键字时&#xff0c;比如“你的名字”或者“你感兴趣的话题”&#xff0c;你会自动把注意力从书上转移到他们的谈话上&#xff0c;尽管你并没有完全忽略书本的内容。这就是注意力机…...

无头单向非循环java版的模拟实现

【本节目标】 1.ArrayList的缺陷 2.链表 1. ArrayList的缺陷 上节课已经熟悉了 ArrayList 的使用&#xff0c;并且进行了简单模拟实现。通过源码知道&#xff0c; ArrayList 底层使用数组来存储元素&#xff1a; public class ArrayList<E> extends AbstractList<…...

Bert Score-文本相似性评估

Bert Score Bert Score 是基于BERT模型的一种方法。它通过计算两个句子在BERT模型中的嵌入编码之间的余弦相似度来评估它们的相似度。BERTScore考虑了上下文信息和语义信息&#xff0c;因此能够更准确地衡量句子之间的相似度。 安装 pip install bert-score 使用例子 一个…...

Pyenv管理Python版本,conda之外的另一套python版本管理解决方案

简介 Pyenv 是一个 python 解释器管理工具&#xff0c;可以对计算机中的多个 python 版本进行管理和切换。为什么要用 pyenv 管理python呢&#xff0c;用过的 python 人都知道&#xff0c;python 虽然是易用而强大的编程语言&#xff0c;但是 python 解释器却有多个版本&#…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...