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
@@#

   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章