Blazor开发时禁用捆绑后CSS的热重载方法
Blazor默认使用了CSS隔离与捆绑,导致CSS修改后不能实时更新(需要重启程序再次捆绑才生效)。即使手工管理CSS放至wwwroot/css目录下,也需要刷新页面才能更新CSS。
解决方法:
- 定时扫描各razor页面对应的CSS变化,有变化时复制集中到一个CSS中。这样可以保留隔离的结构,release模式下可以继续隔离
- 在index.html中增加定时检测功能,检测后台CSS有无变化,有则立即更新CSS,这样可以避免整页刷新
禁用捆绑
@@@code1 2 3 4 5 6 7 8 9 10 11 12 | < PropertyGroup > < OutputType >Exe</ OutputType > < TargetFrameworks >net8.0</ TargetFrameworks > < ImplicitUsings >enable</ ImplicitUsings > < Nullable >enable</ Nullable > < DisableScopedCssBundling Condition="'$(Configuration)'=='Debug'">true</ DisableScopedCssBundling > <!--<ScopedCssEmbeddedResourceNamespace>$(RootNamespace).wwwroot</ScopedCssEmbeddedResourceNamespace>--> <!--<PublishAot>true</PublishAot>--> <!--<InvariantGlobalization>true</InvariantGlobalization>--> </ PropertyGroup > |
扫描服务类
@@@code@csharp@1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | public interface ICssService { Task< bool > AdjustReloadCss(); } public class CssService : ICssService { private readonly HttpClient _httpClient; public CssService(HttpClient httpClient) { _httpClient = httpClient; //启动扫描 startScan(); } [Conditional( "DEBUG" )] void startScan() { string _workPath = AppDomain.CurrentDomain.BaseDirectory; int interval = 3; scan(_workPath); Console.WriteLine( "ok,scan..." ); //文件监视没有工作,那就定时扫描 System.Timers.Timer timer = new System.Timers.Timer(); timer.Interval = interval * 1000; timer.Elapsed += (s, e) => scan(_workPath); timer.Enabled = true ; } /// <summary> /// 扫描,只有有一处更新,则重新生成整个文件 /// </summary> /// <param name = "_workPath"></param> void scan( string _workPath) { int idx = _workPath.IndexOf( "bin\\Debug" ); //只在DEBUG模式下处理 if (idx == -1) return ; string root = _workPath.Substring(0, idx - 1); string wwwroot = Path.Combine(root, "wwwroot" ); string cssFile = Path.Combine(wwwroot, "css" , $ "{nameof(UsbClient)}.css" ); DateTime lastChangeTime = DateTime.MinValue; if (File.Exists(cssFile)) { lastChangeTime = new FileInfo(cssFile).LastWriteTime; } bool hasChange = false ; //todo:尝试使用文件监视 List< string > files = Directory.GetFiles(root, "*.razor.css" , SearchOption.AllDirectories).Where(s => { if (s.Substring(root.Length + 1).StartsWith( "bin" ) || s.Substring(root.Length + 1).StartsWith( "wwwroot" )) return false ; return true ; }).ToList(); foreach ( var s in files) { if ( new FileInfo(s).LastWriteTime > lastChangeTime) { hasChange = true ; break ; } } if (hasChange) { //所有文件合并 File.WriteAllLines(cssFile, files.Select(r => File.ReadAllText(r)).ToArray()); File.WriteAllText(Path.Combine(wwwroot, "data" , "cssChangeTime.json" ), DateTime.Now.Ticks.ToString()); } } long cssLastChangeTime = 0; public async Task< bool > AdjustReloadCss() { #if DEBUG var newTime = long .Parse(await _httpClient.GetStringAsync( @"data/cssChangeTime.json" )); if (newTime > cssLastChangeTime) { cssLastChangeTime = newTime; return true ; } #endif return false ; } } |
在index.html中增加函数
@@@code1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!-- 调试时 --> < link href="./css/UsbClient.css" rel="stylesheet"> < script > window.refreshStyle = (s) => { var prefix = window.location.origin + '/css/UsbClient.css'; var links = document.querySelectorAll('link[rel="stylesheet"]'); links.forEach(function (link) { if (link.href.startsWith(prefix)) { var newLink = document.createElement('link'); newLink.rel = 'stylesheet'; newLink.type = 'text/css'; newLink.href = link.href; link.parentNode.replaceChild(newLink, link); } }); } </ script > |
在相应的Layout中增加判断
@@@code@csharp@
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // DEBUG模式下自动刷新CSS(强制重刷) @inject IJSRuntime JSRuntime @inject ICssService cssService #if DEBUG async void adjustReloadCss( object state) { var needReload = await cssService.AdjustReloadCss(); if (needReload) { await JSRuntime.InvokeVoidAsync( "refreshStyle" , "" ); } } private Timer _timer; protected override async Task OnAfterRenderAsync( bool firstRender) { await base .OnAfterRenderAsync(firstRender); _timer = new Timer(adjustReloadCss, null , TimeSpan.Zero, TimeSpan.FromSeconds(2)); } #endif |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!