Blazor开发时禁用捆绑后CSS的热重载方法

Blazor默认使用了CSS隔离与捆绑,导致CSS修改后不能实时更新(需要重启程序再次捆绑才生效)。即使手工管理CSS放至wwwroot/css目录下,也需要刷新页面才能更新CSS。

解决方法:

  1. 定时扫描各razor页面对应的CSS变化,有变化时复制集中到一个CSS中。这样可以保留隔离的结构,release模式下可以继续隔离
  2. 在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
@@#

   

posted @ 2024-05-08 17:59  秦秋随  阅读(74)  评论(0编辑  收藏  举报