Blazor默认使用了CSS隔离与捆绑,导致CSS修改后不能实时更新(需要重启程序再次捆绑才生效)。即使手工管理CSS放至wwwroot/css目录下,也需要刷新页面才能更新CSS。
解决方法:
- 定时扫描各razor页面对应的CSS变化,有变化时复制集中到一个CSS中。这样可以保留隔离的结构,release模式下可以继续隔离
- 在index.html中增加定时检测功能,检测后台CSS有无变化,有则立即更新CSS,这样可以避免整页刷新
禁用捆绑
@@@code<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@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中增加函数
@@@code
<!-- 调试时 -->
<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@
// 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
浙公网安备 33010602011771号