1
在研究Reactjs服务器端呈现时,在节点Express Web服务器和Webpack中间件上,我发现很难理解为什么根路由“/”匹配组件没有被传递到html中,而嵌套的“/ foobar”工作正常(你也看到父母)。当使用webpack中间件时,根路径“/”与服务器端呈现中reactjs声明的路径不匹配
如果移除了webpack中间件,路径“/”将返回匹配reactjs路由。
请在下面找到源代码,请记住,有很多测试和看到,它不是质量代码。
的发展的WebPack配置文件:
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
devtool: 'inline-source-map',
context: path.resolve(__dirname, 'src'),
entry: [
'react-hot-loader/patch',
'webpack/hot/dev-server',
'webpack-hot-middleware/client',
'babel-polyfill',
'./js/index.js'
],
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
'babel-loader'
]
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
use: [
'file-loader'
]
},
{
test: /\.(jpg|png|gif|svg)$/i,
use: [
'file-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
inject: true,
template: path.join(__dirname, '/src/index.html'),
filename: 'index.html'
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development')
}
}),
// enable HMR globally
new webpack.HotModuleReplacementPlugin(),
// prints more readable module names in the browser console on HMR updates
new webpack.NamedModulesPlugin(),
new webpack.NoEmitOnErrorsPlugin()
]
}
的Reactjs路由器相关的组件。
的Rootjs:
import React from 'react'
import { Router } from 'react-router'
import { Provider } from 'react-redux'
import routes from './routes'
const Root = ({store, history}) => {
return (
<Provider store={store}>
<Router history={history}>
{ routes }
</Router>
</Provider>
)
}
export default Root
路线:
import React from 'react'
import { Route } from 'react-router'
import App from './containers/app'
import Foobar from './containers/foobar'
export default (
<Route path='/' component={App}>
<Route path='foobar' component={Foobar} />
</Route>
)
的Server.js:
import express from 'express'
import path from 'path'
import superagent from 'superagent'
import chalk from 'chalk'
import React from 'react'
import { renderToString } from 'react-dom/server'
import { Provider } from 'react-redux'
import { match, RouterContext } from 'react-router'
import routes from './src/js/routes'
import configureStore from './src/js/store'
import App from './src/js/containers/app'
const app = express()
const router = express.Router()
const port = process.env.PORT ? process.env.PORT : 3000
var serverInstance = null
var dist = path.join(__dirname, ('dist' + (process.env.NODE_ENV ? '/' + process.env.NODE_ENV : 'staging')))
var config = null
var fs = require('fs')
var htmlTemplateString = ''
/**
* Environment settings
*/
if (['staging', 'production'].indexOf(process.env.NODE_ENV) > -1) {
console.log('break 1')
dist = path.resolve(__dirname, process.env.NODE_ENV)
config = require('../config')
htmlTemplateString = fs.readFileSync(dist + '/index.html', 'utf-8')
} else {
console.log('break 2')
config = require('./config')
htmlTemplateString = fs.readFileSync('./dist/production/index.html', 'utf-8')
}
/**
* Process error handling
*/
process.on('uncaughtException', (err) => {
throw err
})
process.on('SIGINT',() => {
serverInstance.close()
process.exit(0)
})
/**
* The Cross origin resource sharing rules
*/
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE')
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type')
res.setHeader('Access-Control-Allow-Credentials', true)
next()
})
/**
* Health check
*/
app.use('/healthcheck', (req, res) => {
res.json({
'env': {
'NODE_ENV': process.env.NODE_ENV
}
})
res.end()
})
router.use('/api/test', (req, res) => {
superagent
.get('https://jsonip.com/')
.end((err, response) => {
if (err) {
console.log('api test err', err)
}
res.send(response.body)
})
})
// HMR only in development
if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'staging') {
console.log('Development environment: Starting webPack middleware...')
const webpack = require('webpack')
const webpackHotMiddleware = require('webpack-hot-middleware')
const webpackDevConfig = require('./webpack.dev.config')
const compiler = webpack(webpackDevConfig)
var webpackDevMiddleware = require('webpack-dev-middleware')
var devMiddleware = webpackDevMiddleware(compiler, {
noInfo: true,
publicPath: webpackDevConfig.output.publicPath,
stats: {
colors: true
}
})
router.use(devMiddleware)
router.use(webpackHotMiddleware(compiler, {
log: console.log
}))
// Production needs physical files! (built via separate process)
router.use('/assets', express.static(dist))
// any other is mapped here
router.get('*', (req, res, next) => {
console.log('req.url: ', req.url)
match({ routes, location: req.url }, (err, redirect, props) => {
if (props) {
const preloadedState = {'foobar': 1}
// Create a new Redux store instance
const store = configureStore(preloadedState)
// Render the component to a string
const myAppHtml = renderToString(<RouterContext {...props} />)
// Grab the initial state from our Redux store
const finalState = store.getState()
// Send the rendered page back to the client
let html = htmlTemplateString.replace('<div id="app">', '<div id="app">' + myAppHtml)
// Paste the state into the html
const preloadedStateScript = `<script>window.__PRELOADED_STATE__ = ${JSON.stringify(finalState).replace(/</g, '\\x3c')}</script>`
html = html.replace('</head>', preloadedStateScript)
res.send(html)
} else {
res.status(404).send('Not found')
}
})
})
}
app.disable('x-powered-by')
app.use('/', router)
serverInstance = app.listen(port, (error) => {
if (error) {
console.log(error) // eslint-disable-line no-console
}
console.log(chalk.green('[' + config.build_name + '] listening on port ' + port + '!'))
})