umijs / mako

An extremely fast, production-grade web bundler based on Rust.
https://makojs.dev
MIT License
1.73k stars 64 forks source link

react类组件中,热更新无法正常更新页面 #1478

Closed rookit-ljt closed 1 week ago

rookit-ljt commented 1 month ago

What is actually happening? 1 .在函数组件中修改代码后,热更新正常更新视图。 2 .在类组件中修改代码后,热更新无法更新页面视图,要手动刷新页面才能显示修改后的视图。

system:macos 15.0 chrome:127.0.6533.99 umi:3.5.42 umi-plugin-mako: 0.0.4

mako react demo 最小复现

What is expected? 希望类组件中修改代码也能正常更新页面视图

sorrycc commented 1 month ago

类组件不支持热更,换函数组件吧。

jeasonnow commented 1 month ago
// crates/mako/src/visitors/react.rs
fn react_refresh_module_prefix(context: &std::sync::Arc<Context>) -> Box<dyn VisitMut> {
    let mut code = r#"
import * as RefreshRuntime from 'react-refresh';
var prevRefreshReg;
var prevRefreshSig;

prevRefreshReg = self.$RefreshReg$;
prevRefreshSig = self.$RefreshSig$;
self.$RefreshReg$ = (type, id) => {
  RefreshRuntime.register(type, module.id + id);
};
self.$RefreshSig$ = () => " %exports%";
"#
    .to_string();

    // check react hmr ability if react was externalized in development mode
    if context.config.mode == Mode::Development
        && context
            .config
            .externals
            .keys()
            .any(|x| x == "react" || x == "react-dom")
    {
        code = format!(
            r#"
if (!(typeof window !== 'undefined' ? window : globalThis).__REACT_DEVTOOLS_GLOBAL_HOOK__) {{
  console.warn('HMR is not available for React currently! Because React was externalized, please install the React Developer Tools extension to enable React Refresh feature. https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/TROUBLESHOOTING.md#externalising-react');
}}
{}
        "#,
            code
        );
    }

    Box::new(PrefixCode {
        context: context.clone(),
        code,
    })
}

fn react_refresh_module_postfix(context: &Arc<Context>) -> Box<dyn VisitMut> {
    Box::new(PostfixCode {
        context: context.clone(),
        // why add `if (prevRefreshReg)` guard?
        // ref: https://github.com/umijs/mako/issues/971
        code: r#"
if (prevRefreshReg) self.$RefreshReg$ = prevRefreshReg;
if (prevRefreshSig) self.$RefreshSig$ = prevRefreshSig;
function $RefreshIsReactComponentLike$(moduleExports) {
  if (RefreshRuntime.isLikelyComponentType(moduleExports)) {
    RefreshRuntime.register(moduleExports, module.id + ' %exports%');
    return true;
  }
  if (RefreshRuntime.isLikelyComponentType(moduleExports.default)) {
    RefreshRuntime.register(moduleExports.default, module.id + ' %exports%');
    return true;
  }
  for (var key in moduleExports) {
    try{
      if (RefreshRuntime.isLikelyComponentType(moduleExports[key])) {
        RefreshRuntime.register(moduleExports[key], module.id + ' %exports%');
        return true;
      }
    }catch(e){
       // in case the moduleExports[key] is not accessible due depedence loop
    }
  }
  return false;
}
if ($RefreshIsReactComponentLike$(module.exports)) {
    module.meta.hot.accept();
    RefreshRuntime.performReactRefresh();
}
"#
        .to_string(),
    })
}

https://github.com/vitejs/vite-plugin-react/blob/1609186b9f379d4c1cf75c60e878d8b4f7675ab3/packages/plugin-react/src/refreshUtils.js#L14-L26 参考 vite-plugin-react 重写了 hmr 部分的逻辑,实际上可以支持 class Component 的热重载,但是不知道是否有其他问题。