路由导入的服务器端呈现问题

时间:2016-09-06 22:11:58

标签: javascript node.js express reactjs react-router

我通过React找到了很多关于SSR的信息,并且所有这些信息都采用了完全不同的方法。所以我找到了一个看起来更有用的例子(React / graphQL / Apollo / Express / Webpack上的web-app),但我坚持一个问题。下面是一些例子:

actions

server.js

和我们导入路由器的 ... import {router} from './client/App'; import React from 'react'; import { renderToString } from 'react-dom/server'; import { match, RoutingContext, Route } from 'react-router'; ... function renderApp(props, res) { const markup = renderToString(<RoutingContext {...props}/>); const html = createPage(markup); write(html, 'text/html', res); } app.get('*', (req, res, next) => { const location = hist.createLocation(req.path); match({routes: router, location: location}, (err, redirectLocation, renderProps) => { if (err) { writeError('ERROR!', res); next(err); } else if (redirectLocation) { redirect(redirectLocation, res); } else if (renderProps) { renderApp(renderProps, res); } else { writeNotFound(res); } }); }); ...

App.js

我尝试了这个example中的所有内容,但问题是在... import Login from './components/Login'; import Register from './components/Register'; ... let routes = ( <Route> <Route path="login" component={Login}/> <Route path="register" component={Register}/> ... </Route> ); export let router = [{ path: '/', component: Layout, indexRoute: { component: View }, getChildRoutes(location, cb) { require.ensure([], () => cb(null, routes)); } }]; match({router, location}, () => { render( <ApolloProvider client={client}> <div> <Router routes={router} onUpdate={() => window.scrollTo(0, 0)} history={browserHistory}/> </div> </ApolloProvider>, document.getElementById('root') ); }); 我尝试从server.js服务器导入router并不能运行并给我与React组件相关的错误(样式导入等等我们可以在客户端上执行的所有功能,但不能在服务器上执行)。

所以问题是,我做错了什么?如果没有这个问题我怎么能导入路线? 我已经在这个小任务上花了那么多时间,真的很烦人。

我会感激任何帮助,谢谢!

1 个答案:

答案 0 :(得分:1)

如您所知,服务器端呈现是在节点服务器中呈现您的react组件。但节点服务器不支持导入css / png文件。

如果您不想更改客户端代码,可以尝试用户webpack-isomorphic-tools,它会帮助您生成一个assert.json文件,该文件可以生成require(&#39; *。 css&#39;)调用返回json个对象并生成CSS类名称映射,就像它们在webpack css-loader中一样。

如果您有兴趣,可以查看demo

这是你的webpack-isomorphic-tools.js

var WebpackIsomorphicToolsPlugin = require('webpack-isomorphic-tools/plugin');


module.exports = {


  assets: {
    images: {
      extensions: [
        'jpeg',
        'jpg',
        'png',
        'gif'
      ],
      parser: WebpackIsomorphicToolsPlugin.url_loader_parser
    },
    fonts: {
      extensions: [
        'woff',
        'woff2',
        'ttf',
        'eot'
      ],
      parser: WebpackIsomorphicToolsPlugin.url_loader_parser
    },
    svg: {
      extension: 'svg',
      parser: WebpackIsomorphicToolsPlugin.url_loader_parser
    },

    bootstrap: {
      extension: 'js',
      include: ['./src/theme/bootstrap.config.js'],
      filter: function(module, regex, options, log) {
        function is_bootstrap_style(name) {
          return name.indexOf('./src/theme/bootstrap.config.js') >= 0;
        }
        if (options.development) {
          return is_bootstrap_style(module.name) && WebpackIsomorphicToolsPlugin.style_loader_filter(module, regex, options, log);
        }

      },

      path: WebpackIsomorphicToolsPlugin.style_loader_path_extractor,
      parser: WebpackIsomorphicToolsPlugin.css_loader_parser
    },
    style_modules: {
      extensions: ['less','scss'],
      filter: function(module, regex, options, log) {
        if (options.development) {
                      return WebpackIsomorphicToolsPlugin.style_loader_filter(module, regex, options, log);
        } else {
                      return regex.test(module.name);
        }
      },
      path: function(module, options, log) {
        if (options.development) {
                      return WebpackIsomorphicToolsPlugin.style_loader_path_extractor(module, options, log);
        } else {
                      return module.name;
        }
      },
      parser: function(module, options, log) {
        if (options.development) {
          return WebpackIsomorphicToolsPlugin.css_modules_loader_parser(module, options, log);
        } else {
              return module.source;
        }
      }
    }
  }
}

你的server.js看起来应该是这样的

function renderFullPage (title, css, html, initialState) {
    return `
        <!DOCTYPE html>
            <html>
              <head>
                <title>${title}</title>
                <style type="text/css">${css}</style>
              </head>
              <body>
                <div id="app">${html}</div>

                <script>
                    window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
                </script>
                <script src="/assets/scripts/app.bundle.js"></script>
              </body>
            </html>
    `;
}

const asyncStore = (store, renderProps) => {
    let promise = Promise.all([
        store.dispatch(queryArtistList()),
        store.dispatch(queryAuth())
    ]);
    return promise;
}

const HomeCtrl = {
    index: async (req, res) => {
        // 补全同构应用运行时缺失的全局对象
        global.window = {
            navigator: {
                userAgent: req.get('User-Agent'),
            },
            location: {
                protocol: req.protocol + ':',
                hostname: req.hostname,
            },
        };  
        match({ routes, location: req.url }, async (err, redirectLocation, renderProps) => {
            if (err) {
                res.status(500).end(`Internal Server Error ${err}`);
            } else if (redirectLocation) {
                res.redirect(redirectLocation.pathname + redirectLocation.search + '/');
            } else if (renderProps) {
                let store   = configureStore();
                const state = store.getState();
                await asyncStore(store, renderProps);
                const components = (<Provider store={store}>
                                            <RouterContext {...renderProps} />
                                        </Provider>);
                const html = renderToStaticMarkup(components);

                res.end(renderFullPage('tokyo Artist', '', html, store.getState()));

            } else {
              res.status(404).end('Not found');
            }
        })
    }
}

请确保在生成webpack-asserts.json;

后启动服务器

所以你的app.js应该是这样的:

#!/usr/bin/env node
const path = require('path');
const rootDir = path.resolve(__dirname, '..');
const fs = require('fs');

const babelrc = fs.readFileSync(rootDir + '/.babelrc', 'utf-8');
var config;

try {
    config = JSON.parse(babelrc);
} catch (err) {
    console.error('==>     ERROR: Error parsing your .babelrc.');
    console.error(err);
}

require('babel-register')(config);

/**
 * Define isomorphic constants.
 */
global.__CLIENT__ = false;
global.__SERVER__ = true;
global.__DEVELOPMENT__ = process.env.NODE_ENV !== 'production';
global.__DEVTOOLS__ = __DEVELOPMENT__;


const WebpackIsomorphicTools = require('webpack-isomorphic-tools');
global.webpackIsomorphicTools = new WebpackIsomorphicTools(require('../webpack/webpack.isomorphic-tools'))
    .development(__DEVELOPMENT__)
    .server(__DEVELOPMENT__ ? __dirname : rootDir, function() {
        require('../server/app.js');
    });