Visual Studio React Typescript webpack-dev-server 热重载不适用于组件更改

时间:2021-05-13 09:07:27

标签: reactjs webpack webpack-dev-server asp.net-core-3.1

我将 .Net Core 3.1 与 React、Typescript 和 webpack 一起用于 SPA。我有多个反应模块,我将它们加载到 Asp.Net 核心应用程序的不同页面/视图上。

我遇到了自动刷新或热重载的问题。当我更改从模块传递给组件的参数时,热重载按预期工作,但是如果我对组件进行任何更改,那么这些将没有反映在浏览器中。

如果我浏览 https://localhost:5010/webpack-dev-server,我可以在那里看到 home.bundle.js。此外,如果在 HomeModule.tsx 中更改 HomeComponent 的“msg”参数/prop 的值,我可以看到为 home.bundle.js 生成的具有新哈希码的新包,并且更改也会反映到 https://localhost:5010/home/ 上的浏览​​器但是如果我确实更改为 HomeComponent.tsx 例如:如果我将“Home component”更改为“Home component 123”,则此更改未反映既不在 https://localhost 上:5010/home 和新包也没有在 https://localhost:5010/webpack-dev-server 上生成。

这是我的项目结构和文件,包括配置文件。任何帮助将不胜感激。

更新:我将 HotModuleReplacementPlugin 添加到 webpack.dev.config.js,现在当我修改组件 HomeComponent.tsx 中的某些内容时,我在 https://localhost:5010/webpack-dev-server 中看到一个新条目,类似于“5f80de77a97d48407bd9 .hot-update.json”。如果我打开这个文件,它的内容就像 {"h":"0dce1f9e44bff4d47fb4","c":{}}

除此之外的另一个问题是,当我从 Visual Studio 使用 F5 运行应用程序时,加载网站需要几秒钟,直到浏览器显示“无法访问此站点”

项目结构

Solution
    Solution
    - AspNetCore.Web
        - node_modules
        - wwwroot
            - js
        - Controllers
        - Views
        - Scripts (React)
            - components
                - HomeComponent.tsx
            - modules
                - HomeModule.tsx
        - package.json
        - tsconfig.json
        - webpack.config.js
        - webpack.dev.js
    - Other .Net Core projects

Package.json

{
  "version": "1.0.0",
  "name": "aspnetcore-react",
  "private": true,
  "scripts": {
    "build": "webpack serve --config webpack.dev.config.ts",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-router": "^5.2.0"
  },
  "devDependencies": {
    "@babel/core": "^7.14.3",
    "@babel/plugin-transform-runtime": "^7.14.3",
    "@babel/preset-env": "^7.14.2",
    "@babel/preset-react": "^7.13.13",
    "@babel/preset-typescript": "^7.13.0",
    "@babel/runtime": "^7.14.0",
    "@popperjs/core": "^2.2.1",
    "@types/react": "^16.9.35",
    "@types/react-dom": "^16.9.8",
    "@types/webpack": "^4.41.25",
    "@types/webpack-dev-server": "^3.11.1",
    "autoprefixer": "^9.7.6",
    "babel-core": "^6.26.3",
    "babel-loader": "^8.2.2",
    "bootstrap": "^4.4.1",
    "clean-webpack-plugin": "^4.0.0-alpha.0",
    "css-loader": "^3.5.1",
    "file-loader": "^6.0.0",
    "jquery": "^3.6.0",
    "node-sass": "^4.14.1",
    "postcss-loader": "^3.0.0",
    "resolve-url-loader": "^3.1.3",
    "sass-loader": "^8.0.2",
    "style-loader": "^1.1.3",
    "ts-loader": "^7.0.2",
    "ts-node": "^10.0.0",
    "typescript": "3.8.3",
    "url-loader": "^4.1.0",
    "vue-loader": "^15.9.2",
    "webpack": "^5.11.1",
    "webpack-cli": "^4.3.1",
    "webpack-dev-server": "^3.11.1",
    "webpack-merge": "^5.7.3"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react",      
  },
  "exclude": [
    "wwwroot/js",
    "node_modules"
  ],
  "include": [
    "./Scripts/**/*"
  ]
}

webpack.dev.config.ts

import path from "path";
import webpack from "webpack";
import * as fs from 'fs';

const modulePath = './Scripts/modules/'
const entries = {
    home: modulePath + "HomeModule.tsx",
    editor: modulePath + "EditorModule.tsx"
};

const config: webpack.Configuration = {
    mode: "development",
    output: {
        filename: "[name].bundle.js",
        publicPath: "/js",
    },
    entry: entries,
    module: {
        rules: [
            {
                test: /\.(ts|js)x?$/i,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "@babel/preset-env",
                            "@babel/preset-react",
                            "@babel/preset-typescript",
                        ],
                    },
                },
            },
            {
                test: /\.(scss)$/,
                use: [
                    {
                        // Adds CSS to the DOM by injecting a `<style>` tag
                        loader: 'style-loader'
                    },
                    {
                        // Interprets `@import` and `url()` like `import/require()` and will resolve them
                        loader: 'css-loader'
                    },
                    {
                        loader: "resolve-url-loader"
                    },
                    {
                        // Loader for webpack to process CSS with PostCSS
                        loader: 'postcss-loader',
                        options: {
                            plugins: function () {
                                return [
                                    require('autoprefixer')
                                ];
                            }
                        }
                    },
                    {
                        // Loads a SASS/SCSS file and compiles it to CSS
                        loader: 'sass-loader'
                    }
                ]
            },
            {
                test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 4096,
                            fallback: {
                                loader: 'file-loader',
                                options: {
                                    name: 'assets/[name].[hash:8].[ext]'
                                }
                            }
                        }
                    }
                ]
            },
            {
                test: /\.(svg)(\?.*)?$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'assets/[name].[hash:8].[ext]'
                        }
                    }
                ]
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'assets/[name].[hash:8].[ext]'
                        }
                    }
                ],
            },
            {
                test: /\.html$/,
                use: [{
                    loader: "html-loader",
                    options: {
                        minimize: true
                    }
                }]
            }
        ],
    },
    resolve: {
        extensions: [".tsx", ".jsx", ".ts", ".js"],
    },
    plugins: [        
    ],
    devtool: "inline-source-map",
    devServer: {
        contentBase: path.join(__dirname, "build"),
        historyApiFallback: true,
        port: 5010,
        open: false,
        hot: true,
        https: {
            key: fs.readFileSync('./Scripts/generated/webpack_cert.key'),
            cert: fs.readFileSync('./Scripts/generated/webpack_cert.crt'),
        }
    }
};

export default config;

HomeModule.tsx

import * as React from 'react';
import * as ReactDom from 'react-dom';
import HomeComponent from '../components/HomeComponent';

const homeRoot = document.getElementById('home');

ReactDom.render(<HomeComponent msg={'Hello World!!!'} />, homeRoot);

HomeComponent.tsx

import * as React from 'react';

export interface IHomeComponentProps {
    msg: string
}

export interface IHomeComponentState {
}

export default class HomeComponent extends React.Component<IHomeComponentProps, IHomeComponentState> {
    constructor(props: IHomeComponentProps) {
        super(props);

        this.state = {
        }
    }

    public render() {
        return (
            <div>
                <h2>Home component</h2>                
                <p><b>{this.props.msg}</b></p>
            </div>
        );
    }
}

Views/Home/Index.cshtml

@{
    ViewData["Title"] = "Home Page";
    ViewBag.NoContainer = true;
}


<div class="home-header">
    <h2 class="home-header-subtitle">Welcome to the</h2>
    <h1 class="home-header-title">AspnetCoreReact</h1>
    <div class="home-header-actions">
        <a class="btn btn-primary home-header-button" asp-area="" asp-controller="Editor" asp-action="Index">Try the demo</a>
    </div>
    <div id="home">
    </div>
</div>

<script src="~/js/home.bundle.js" type="module"></script>

Startup.cs

namespace AspnetCoreReact.Web
{
    public class Startup
    {
        ...

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddApplication();
            services.AddInfrastructure(Configuration, Environment);

            services.AddScoped<ICurrentUserService, CurrentUserService>();

            services.AddHttpContextAccessor();

            services.AddHealthChecks()
                .AddDbContextCheck<AspNetReactDbContext>();

            ConfigureCookieSettings(services);

            // Add memory cache services
            services.AddMemoryCache(); // To do: Change to distributed cache later

            services.Configure<RouteOptions>(options => options.LowercaseUrls = true);

            services.AddMvc();

            services.AddControllersWithViews();
            services.AddRazorPages();

            services.AddHttpContextAccessor();            
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();                
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHealthChecks("/health");
            //app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseSpaStaticFiles();
            app.UseRouting();

            app.UseCookiePolicy();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "areas",
                    pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                
                endpoints.MapRazorPages();
            });

            app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "Scripts";

                if (env.IsDevelopment())
                {
                    spa.UseReactDevelopmentServer(npmScript: "build:hotdev");
                }
            });
        }
    }
}

launchSettings.json

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:53158",
      "sslPort": 44329
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },    
    "AspnetCore-React": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
      },
      "applicationUrl": "https://localhost:5001;http://localhost:5000"
    }
  }
}

1 个答案:

答案 0 :(得分:0)

我终于能够通过将 UseReactDevelopmentServer 替换为 UseProxyToSpaDevelopmentServer 来解决该问题。

新代码块(工作中)

app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "Scripts";

                if (env.IsDevelopment())
                {
                    // Ensure that you start webpack server - npm run build
                    string webpackDevServer = "https://localhost:5010/";
                    spa.UseProxyToSpaDevelopmentServer(webpackDevServer);
                }
            });

旧代码块(不工作)

app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "Scripts";

                if (env.IsDevelopment())
                {
                    spa.UseReactDevelopmentServer(npmScript: "build:hotdev");
                }
            });
相关问题