You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ASP.NET生命周期内为Razor视图一次性附加编译时数据?

针对你的Razor视图静态关联Webpack资源的解决方案

你的需求很明确——要在应用生命周期内一次性给每个Razor视图绑定Webpack生成的带哈希的脚本/样式,既不想硬编码,也不想每次请求都去读取数据源。下面几个方案完全符合你的要求,都是在编译或启动阶段完成绑定,运行时直接取用静态数据:

方案一:利用Razor编译时代码生成器(ASP.NET Core)

这个方案是从根源上解决问题,直接在Razor视图编译的过程中,把Webpack资源路径注入到视图类的静态属性里,相当于编译时就"写死"到视图代码中,但又不需要你手动硬编码:

  1. 准备Webpack Manifest
    首先让Webpack生成manifest.json文件,把每个视图对应的资源路径(带哈希)记录下来,比如:

    {
      "Views/Home/Index": "/js/home-index.abc123.js",
      "Views/Account/Login": "/js/login.def456.js"
    }
    

    这里的键要和视图的完整路径对应,方便后续匹配。

  2. 自定义Razor代码生成器
    创建一个继承自DefaultRazorCSharpCodeGenerator的类,重写GenerateView方法,在生成视图类代码时,从manifest里读取当前视图对应的资源路径,生成静态属性:

    public class AssetInjectingRazorGenerator : DefaultRazorCSharpCodeGenerator
    {
        private readonly IWebHostEnvironment _env;
        private readonly Dictionary<string, string> _assetManifest;
    
        public AssetInjectingRazorGenerator(
            RazorCodeGeneratorContext context,
            IWebHostEnvironment env) : base(context)
        {
            _env = env;
            // 应用启动时一次性读取manifest
            var manifestPath = Path.Combine(_env.ContentRootPath, "wwwroot", "manifest.json");
            _assetManifest = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(manifestPath));
        }
    
        public override void GenerateView()
        {
            base.GenerateView();
            // 获取当前视图的路径(比如"Views/Home/Index")
            var viewPath = Context.FilePath.Substring(Context.FilePath.IndexOf("Views/")).Replace(".cshtml", "");
            if (_assetManifest.TryGetValue(viewPath, out var scriptPath))
            {
                // 给视图类添加静态属性
                Context.CodeWriter.WriteLine($"public static string ScriptPath = \"{scriptPath}\";");
            }
        }
    }
    
  3. 注册自定义生成器
    在Program.cs里替换默认的Razor代码生成器:

    builder.Services.AddRazorPages()
        .AddRazorRuntimeCompilation(options =>
        {
            options.CodeGenerators.Add((context) => 
                new AssetInjectingRazorGenerator(context, builder.Environment));
        });
    
  4. 在视图中使用
    编译完成后,视图类会自动拥有ScriptPath静态属性,直接在视图里用:

    <script src="@this.ScriptPath"></script>
    

    这个属性是编译时确定的,运行时完全没有额外开销,和硬编码效果一样,但能自动同步Webpack的哈希变更。

方案二:启动时加载Manifest + 静态类映射

如果觉得编译生成器有点复杂,可以用更简单的静态类方案,在应用启动时一次性读取所有资源映射,然后通过静态类提供给视图:

  1. 创建静态资源类
    定义一个静态类,每个视图对应一个属性:

    public static class ViewAssets
    {
        public static string HomeIndexScript { get; set; }
        public static string AccountLoginScript { get; set; }
        // 其他视图的属性...
    }
    
  2. 启动时加载并赋值
    在Program.cs的应用启动阶段(比如builder.Build()之后),读取Webpack的manifest并给静态属性赋值:

    var app = builder.Build();
    var env = app.Services.GetRequiredService<IWebHostEnvironment>();
    var manifestPath = Path.Combine(env.ContentRootPath, "wwwroot", "manifest.json");
    var assetManifest = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(manifestPath));
    
    ViewAssets.HomeIndexScript = assetManifest["Views/Home/Index"];
    ViewAssets.AccountLoginScript = assetManifest["Views/Account/Login"];
    // 其他视图的赋值...
    
  3. 在视图中使用
    先在_ViewImports.cshtml里导入静态类的命名空间:

    @using YourAppNamespace.ViewAssets
    

    然后在对应视图里直接用:

    <script src="@ViewAssets.HomeIndexScript"></script>
    

    这个方案也是一次性加载,运行时直接取用静态属性,完全没有重复操作的开销。

额外说明

  • 不管用哪个方案,当Webpack更新资源哈希后,只需要重新编译视图(方案一)或者重启应用(方案二)就能自动更新资源路径,和硬编码的更新流程一致。
  • 如果用ASP.NET Core的Razor运行时编译,方案一在修改manifest后,视图会自动重新编译,不需要重启应用,非常方便开发阶段使用。

内容的提问来源于stack exchange,提问作者lockedscope

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:46:17