lanjingling0510 / blog

Rainie's blog 😉 📝
178 stars 8 forks source link

react服务器渲染下,hot reload解决方案 #2

Open lanjingling0510 opened 8 years ago

lanjingling0510 commented 8 years ago

前言

react客户端渲染的前端界面配合webpack-dev-server, react-hot-loader很容易实现前端开发过程中的局部刷新。然而配合node服务器的react-isomorphic实现局部刷新,同时更新client, server端的代码并非易事。 如下介绍一种可行的实施方案:

适用于 koa2, react-hot-loader3, react-router可有可无。

Demo代码地址:https://github.com/lanjingling0510/blog/tree/master/react-isomorphic-hot-example

hot reload分析

image

react静态资源热加载分析

react静态资源的热加载配置并不复杂。webpack-dev-server负责重新编译代码,react-hot-loader负责热加载。

Note:webpack-dev-server也可以用开一个express服务器配合webpack-dev-middlewarewebpack-hot-middleware中间件实现

  1. 配置webpack.client-dev.js:

plugins: [
    new webpack.HotModuleReplacementPlugin()
]

// ...

entry: [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://127.0.0.1:8080',
    'webpack/hot/only-dev-server',
    './src/client/home', // 入口路径
]
  1. 修改babel配置文件
"plugins": [
    "react-hot-loader/babel"
]
  1. 修改入口文件
import React from 'react';
import ReactDOM from 'react-dom';

// 共享的组件页面
import Home from '../shared/page/Home';
// 热加载组件
import ReactHotLoader from '../shared/component/ReactHotLoader';
const container = document.getElementById('react-container');

function renderApp(TheApp) {
    ReactDOM.render(
      <ReactHotLoader>
          <TheApp />
      </ReactHotLoader>,
      container
    );
}

renderApp(Home);

// 下面的代码用来支持我们热加载应用
if (__DEV__ && module.hot) {
    // 接受这个文件的修改用来热加载
    module.hot.accept('./home.js');
    // 应用任何的改变将造成热加载,重新渲染。
    module.hot.accept(
      '../shared/page/Home',
      () => renderApp(require('../shared/page/Home').default)
    );
}

react服务器配置分析

开发模式下,server端的配置比较复杂,需要考虑的事情如下:

  1. 监听server代码

 // 监听server文件的变化,如果被修改则调用compileHotServer
 const watcher = chokidar.watch([
     path.resolve(__dirname, '../src'),
     path.resolve(__dirname),
 ], {ignored: path.resolve(__dirname, '../src/client')});

 watcher.on('ready', () => {
     watcher
     .on('add', compileHotServer)
     .on('addDir', compileHotServer)
     .on('change', compileHotServer)
     .on('unlink', compileHotServer)
     .on('unlinkDir', compileHotServer);
 });
  1. 关闭所有与客户端的连接,关闭server服务器,重新编译server代码
 // 关闭所有连接,关闭服务器,重新编译
 function compileHotServer() {
     compiling ++;
     // listenerManager实例包含当前web服务器对象和客户端连接的socket集合
     if (listenerManager) {
         listenerManager.dispose(true).then(runCompiler);
     } else {
         runCompiler();
     }
 }

 // webpack重新编译
 function runCompiler() {
    compiler.run(() => undefined);
 }
  1. 重新开启server服务器

// server代码编译完成
 // 开启server服务器
 compiler.plugin('done', stats => {
     compiling --;
     if (compiling !== 0) return;

     if (stats.hasErrors()) {
         console.log(stats.toString());
         return;
     }

     console.log('🚀 😝  Build server bundle done.');
     // 确保新的server bundles 代码不在module cache当中
     Object.keys(require.cache).forEach((modulePath) => {
         if (modulePath.indexOf(compiler.options.output.path) !== -1) {
             delete require.cache[modulePath];
         }
     });

     try {
         const listener = require(compiledOutputPath).default;
         listenerManager = new ListenerManager(listener, 'server');
     } catch (err) {
         console.log(err);
     }
 });

待解决

. react-router包含的页面组件更新后,提示[react-router] You cannot change <Router routes>; it will be ignored,但不影响刷新

总结

通过以上配置,可以实现修改代码后,实现server和client代码的更新以及hot reload。 代码开发过程中,需要开启两个端口,分别用来提供client端静态资源的编译和后台的server。

如果在开发模式下,有更完善的react isomoriphic服务器渲染热加载的解决方案,欢迎大家积极贡献 😁

dxcweb commented 7 years ago

在生产环境下需要如果使用呢?

lanjingling0510 commented 7 years ago

嗨 @dxcweb ,hot reload只是在开发环境下来用,方便开发而已

lcoder commented 7 years ago

待解决的可以看下这个:React Hot Loader 3 beta 升级指南