Express主页未在服务器上呈现

时间:2017-09-20 19:55:45

标签: node.js reactjs express

我正在开发一个使用服务器端渲染的反应应用程序。 我的路线工作,主页除外。不知道为什么。我假设我没有正确设置我的快速服务器来处理索引路由......?

以下是处理快递的模块。设置等等。我假设我app.get('*)app.use(express.static)不正确。

app.js(服务器)

require('ignore-styles')
const compression = require('compression')
const express = require('express')
const path = require('path')

require('babel-register')({
    ignore: /\/(build|node_modules)\//,
    presets: ['env', 'react-app']
})

const universalLoader = require('./universal')

const app = express()

// Support Gzip
app.use(compression())

// Serve static assets
app.use(express.static(path.resolve(__dirname, '..', 'build')))

// Always return the main index.html, so react-router render the route in the client
app.get('*', universalLoader)

module.exports = app

universalLoader.js(服务器)

import path from 'path'
import fs from 'fs'

import React from 'react'
import { Provider } from 'react-redux'
import { renderToString } from 'react-dom/server'
import { StaticRouter, matchPath } from 'react-router-dom'

import configureStore from '../src/store'
import App from '../src/components/App'

import routes from '../src/shared/routes'

import { getSiteInfo } from '../src/store/actions/siteInfo'
import { REACT_APP_SITE_KEY } from '../src/shared/vars'

import Helmet from 'react-helmet'

module.exports = function universalLoader(req, res, next) {

    // console.log('Loading....')

    const store = configureStore()
    const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl
    let routeFound = false

    // Try to find a matched route
    const promises = routes.reduce((promise, route) => {
        var props = matchPath(req.url, route)

        // If route was matched, component exists, and has an initialAction
        // then call it's initialAction.
        // This action will most-likely load some data asyncronously
        if (props && route.component && route.component.initialAction) {
            promise.push(Promise.resolve(store.dispatch(route.component.initialAction(store, props))))
        }

        return promise
    }, [])

    // Load initial site data
    promises.push(Promise.resolve(store.dispatch(getSiteInfo(REACT_APP_SITE_KEY))))

    // Wait until all async data has been loaded
    Promise.all(promises)
    .then(() => {

        // Load index file path
        const filePath = path.resolve(__dirname, '..', 'build', 'index.html')

        // Read index file
        fs.readFile(filePath, 'utf8', (err, htmlData) => {

            if(err){
                console.error('read err', err)
                return res.status(404).end()
            }

            const preloadedState = store.getState()
            // console.log("PreloadedState:", preloadedState)

            const context = preloadedState
            // console.log(context)

            // Note: Pass in serverRequest prop so the App knows the domain it's on for meta tags
            const markup = renderToString(
                <Provider store={store}>
                    <StaticRouter location={req.url} context={context}>
                        <App serverRequest={req} serverResponse={res} />
                    </StaticRouter>
                </Provider>
            )

            const helmet = Helmet.renderStatic()

            // Somewhere a `<Redirect>` was rendered
            if(context.url){
                console.log('Redirected:', context.url)
                redirect(301, context.url)

            // we're good, send the response
            }else{

                // Page meta data
                const meta = helmet.title.toString() + helmet.meta.toString() + helmet.link.toString()

                // Prep state to be injected into DOM for client
                const pageState = `<script>window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}</script>`

                // Inject state and markup
                const RenderedApp = htmlData
                    .replace('<script></script>', pageState) // Store state to pass to client
                    .replace('<meta name="helmet">', meta)   // Meta data
                    .replace('{{SSR}}', markup)              // Actual markup/component html

                console.log("SSR Rendered: ", req.path)

                res.send(RenderedApp)
            }
        })
    })
    .catch(err => {
        console.log("Error:", err)
    })
}

我是console.log() - 在universalLoader()内处理路线时。所有路线都在控制台中显示正在发生的事情。除了我的主页。它甚至没有显示&#34;正在加载......&#34;消息。

1 个答案:

答案 0 :(得分:1)

express.static将提供build目录中的所有文件。如果它找到所请求的文件,它将提供它并结束请求/响应。如果找到合适的文件,express.static之后注册的中间件将无法运行。

基于这一行:

const filePath = path.resolve(__dirname, '..', 'build', 'index.html')

您的index.html目录中似乎有一个名为build的文件。当您点击express.static的网址时,这将由index.html提供,但如果您点击/网址,它也会被提供,因为express.static默认提供服务index.html。请在此处查看index属性:

https://expressjs.com/en/4x/api.html#express.static

您指向的目录express.static需要包含静态的文件,即无需任何处理的文件。如果任何文件需要处理,他们需要住在其他地方。请注意Express应用程序通常具有views单独文件夹的方式,这在很多方面类似于您尝试做的事情。

我还建议评论express.static,看看它有什么影响。它应该是确认express.static负责停止索引路径的快速方法。

<强>更新

根据您的评论,您似乎在build/static处有一个包含JS和CSS文件的静态目录。您可以直接使用以下方式提供此服务:

app.use(express.static(path.resolve(__dirname, '..', 'build', 'static')))

但是,这会导致您的所有网址也发生变化,因此http://localhost/static/js/example.js现在将为http://localhost/js/example.js。要保留原始网址,您需要通过路径路径重新放入static

app.use('/static', express.static(path.resolve(__dirname, '..', 'build', 'static')))