Visual Studio React Typescript webpack-dev-server 热重载不适用于组件更改 [英] Visual Studio React Typescript webpack-dev-server hot reload not working for component changes

查看:25
本文介绍了Visual Studio React Typescript webpack-dev-server 热重载不适用于组件更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

如果我浏览 https://localhost:5010/webpack-dev-server,我可以在那里看到 home.bundle.js.此外,如果更改msg"的值HomeModule.tsx 中 HomeComponent 的 param/prop,我可以看到为 home.bundle.js 生成的新包带有新的哈希码,更改也反映到浏览器 https://localhost:5010/home/BUT如果我确实更改为 HomeComponent.tsx 例如:如果我更改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 运行应用程序时,加载网站需要几秒钟,直到浏览器显示无法访问此站点"

项目结构

解决方案解决方案- AspNetCore.Web- 节点模块- wwwroot- js- 控制器- 意见- 脚本(反应)- 成分- HomeComponent.tsx- 模块- HomeModule.tsx- 包.json- tsconfig.json- webpack.config.js- webpack.dev.js- 其他 .Net Core 项目

Package.json

<代码>{版本":1.0.0",名称":aspnetcore-react",私人":真的,脚本":{"build": "webpack serve --config webpack.dev.config.ts",测试":回声\"错误:未指定测试\"&&出口 1"},依赖关系":{axios":^0.21.1",反应":^16.13.1",react-dom":^16.13.1",反应路由器":^ 5.2.0";},开发依赖":{@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",自动前缀":^9.7.6",babel-core":^6.26.3",babel-loader":^8.2.2",引导程序":^4.4.1",clean-webpack-plugin":^4.0.0-alpha.0",css-loader":^3.5.1",文件加载器":^6.0.0",jquery":^3.6.0",节点-sass":^4.14.1",postcss-loader":^3.0.0",resolve-url-loader":^3.1.3",sass-loader":^8.0.2",样式加载器":^1.1.3",ts-loader":^7.0.2",ts-node":^10.0.0",打字稿":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

<代码>{编译器选项":{库":[dom",dom.iterable",esnext"],allowJs":真,allowSyntheticDefaultImports":真,skipLibCheck":真,esModuleInterop":真,严格":真实,forceConsistentCasingInFileNames":真,模块分辨率":节点",resolveJsonModule":真,isolatedModules":真,noEmit":真,jsx":反应",},排除":[wwwroot/js",节点模块"],包括":[./Scripts/**/*"]}

webpack.dev.config.ts

从路径"导入路径;从webpack"导入 webpack;从 'fs' 导入 * 作为 fs;const modulePath = './Scripts/modules/'常量条目 = {家:modulePath +HomeModule.tsx",编辑器:modulePath +EditorModule.tsx";};const 配置:webpack.Configuration = {模式:发展",输出: {文件名:[名称].bundle.js",公共路径:/js",},条目:条目,模块: {规则: [{测试:/\.(ts|js)x?$/i,排除:/node_modules/,用: {装载机:babel-loader",选项: {预设:[@babel/preset-env",@babel/preset-react",@babel/preset-typescript",],},},},{测试:/\.(scss)$/,用: [{//通过注入 `<style>` 标签将 CSS 添加到 DOM装载机:'风格装载机'},{//像 `import/require()` 一样解释 `@import` 和 `url()` 并解析它们装载机:'css-装载机'},{加载器:解析网址加载器";},{//webpack 的加载器以使用 PostCSS 处理 CSSloader: 'postcss-loader',选项: {插件:函数(){返回 [要求('自动前缀')];}}},{//加载一个 SASS/SCSS 文件并将其编译为 CSSloader: 'sass-loader'}]},{测试:/\.(png|jpe?g|gif|webp)(\?.*)?$/,用: [{loader: 'url-loader',选项: {限制:4096,倒退: {加载器:'文件加载器',选项: {name: 'assets/[name].[hash:8].[ext]'}}}}]},{测试:/\.(svg)(\?.*)?$/,用: [{加载器:'文件加载器',选项: {name: 'assets/[name].[hash:8].[ext]'}}]},{测试:/\.(woff|woff2|eot|ttf|otf)$/,用: [{加载器:'文件加载器',选项: {name: 'assets/[name].[hash:8].[ext]'}}],},{测试:/\.html$/,用: [{加载器:html-加载器",选项: {最小化:真}}]}],},解决: {扩展名:[.tsx"、.jsx"、.ts"、.js"]、},插件: [],开发工具:内联源映射",开发服务器:{contentBase: path.join(__dirname, build"),historyApiFallback: 真,端口:5010,打开:假,热:真的,https:{密钥:fs.readFileSync('./Scripts/generated/webpack_cert.key'),证书:fs.readFileSync('./Scripts/generated/webpack_cert.crt'),}}};导出默认配置;

HomeModule.tsx

import * as React from 'react';import * as ReactDom from 'react-dom';从 '../components/HomeComponent' 导入 HomeComponent;const homeRoot = document.getElementById('home');ReactDom.render(<HomeComponent msg={'Hello World!!!'}/>, homeRoot);

HomeComponent.tsx

import * as React from 'react';导出接口 IHomeComponentProps {消息:字符串}导出接口 IHomeComponentState {}导出默认类 HomeComponent 扩展 React.Component{构造函数(道具:IHomeComponentProps){超级(道具);this.state = {}}公共渲染(){返回 (<div><h2>主页组件</h2><p><b>{this.props.msg}</b></p>

);}}

Views/Home/Index.cshtml

@{ViewData[标题"] =主页";ViewBag.NoContainer = true;}<div class="home-header"><h2 class="home-header-subtitle">欢迎来到</h2><h1 class="home-header-title">AspnetCoreReact</h1><div class="home-header-actions"><a class="btn btn-primary home-header-button";asp区域="asp-controller=编辑器"asp-action=Index">尝试演示</a>

<div id="home">

<script src="~/js/home.bundle.js";类型=模块"></script>

Startup.cs

命名空间 AspnetCoreReact.Web{公开课启动{...public void ConfigureServices(IServiceCollection 服务){服务.AddApplication();services.AddInfrastructure(Configuration, Environment);services.AddScoped();services.AddHttpContextAccessor();services.AddHealthChecks().AddDbContextCheck();配置Cookie设置(服务);//添加内存缓存服务services.AddMemoryCache();//要做:稍后改成分布式缓存services.Configure(options => options.LowercaseUrls = true);服务.AddMvc();services.AddControllersWithViews();services.AddRazorPages();services.AddHttpContextAccessor();}公共无效配置(IApplicationBuilder 应用程序,IWebHostEnvironment 环境){如果 (env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseDatabaseErrorPage();}别的{app.UseExceptionHandler("/Home/Error");//默认的 HSTS 值为 30 天.您可能希望针对生产场景更改此设置,请参阅 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(名称:区域",模式:{area:exists}/{controller=Home}/{action=Index}/{id?}");endpoints.MapControllerRoute(名称:默认",模式:{controller=Home}/{action=Index}/{id?}");端点.MapRazorPages();});app.UseSpa(spa =>{spa.Options.SourcePath = 脚本";如果 (env.IsDevelopment()){spa.UseReactDevelopmentServer(npmScript: "build:hotdev");}});}}}

launchSettings.json

<代码>{iis设置":{windowsAuthentication":假,匿名身份验证":真,iisExpress":{applicationUrl":http://localhost:53158",sslPort":44329}},个人资料":{IIS Express":{命令名称":IISExpress",launchBrowser":真,环境变量":{ASPNETCORE_ENVIRONMENT":开发"}},AspnetCore-反应":{命令名称":项目",launchBrowser":真,环境变量":{ASPNETCORE_ENVIRONMENT":开发",ASPNETCORE_HOSTINGSTARTUPASSEMBLIES":Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"},applicationUrl":https://localhost:5001;http://localhost:5000"}}}

解决方案

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

新代码块(工作中)

app.UseSpa(spa =>{spa.Options.SourcePath = 脚本";如果 (env.IsDevelopment()){//确保你启动了 webpack server - npm run buildstring webpackDevServer = "https://localhost:5010/";spa.UseProxyToSpaDevelopmentServer(webpackDevServer);}});

旧代码块(不工作)

app.UseSpa(spa =>{spa.Options.SourcePath = 脚本";如果 (env.IsDevelopment()){spa.UseReactDevelopmentServer(npmScript: "build:hotdev");}});

I am using .Net Core 3.1 with React, Typescript and webpack for an SPA. I have multiple modules in react which I load on different page/view of Asp.Net core application.

I am facing issue with auto refresh or hot reload. When I change a parameter which I am passing from module to a component hot reload works as expected BUT if I do any change in component then those are not getting reflected in browser.

If I browse https://localhost:5010/webpack-dev-server I can see home.bundle.js there. Also if change value of "msg" param/prop of HomeComponent in HomeModule.tsx, I can see new bundle generated for home.bundle.js with fresh hashcode and change also reflects to browser on https://localhost:5010/home/ BUT If I do change to HomeComponent.tsx ex: if I change "Home component" to "Home component 123" this change not getting reflected neither on https://localhost:5010/home nor the new bundle NOT generating on https://localhost:5010/webpack-dev-server.

Here is my project structure and files including configuration files. Any help will be highly appreciated.

Update: I added HotModuleReplacementPlugin to webpack.dev.config.js and now when I modify something in component HomeComponent.tsx I see a new entry in https://localhost:5010/webpack-dev-server something like "5f80de77a97d48407bd9.hot-update.json". If I open this file it's content is like {"h":"0dce1f9e44bff4d47fb4","c":{}}

Apart from this another issue is when I run application with F5 from Visual Studio it takes couple of seconds to load website until than browser shows "This site can't be reached"

Project stucture

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"
    }
  }
}

解决方案

I finally able to resolve the issue by replacing UseReactDevelopmentServer with UseProxyToSpaDevelopmentServer.

New code block (Working)

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);
                }
            });

Old code block (Not working)

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

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

这篇关于Visual Studio React Typescript webpack-dev-server 热重载不适用于组件更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆