我不愿意承认这一点,但是我已经花了三个晚上去尝试做-我认为那是一件很简单的事情。我终于到达了厌倦它的阶段,并且坦率地感到沮丧,因为“它根本不起作用”。
这是我想要实现的目标:
我尝试了许多组合,已弃用的教程,不再维护的npm软件包以及下载了无法使用的示例。
这是我当前的设置:
webpack.server.config.js:
const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
module.exports = {
name: 'server',
mode: 'development',
target: 'node',
externals: nodeExternals(),
entry: [ './src/server/index' ],
output: {
path: path.resolve(__dirname, 'dist'),
// path: "/",
filename: '[name].js',
publicPath: '/assets/',
libraryTarget: 'commonjs2'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
test: /.js$/,
loader: 'babel-loader',
include: path.resolve(__dirname, 'src/'),
exclude: /node_modules/,
options: {
presets:
[['@babel/preset-env', { modules: 'false' }], '@babel/preset-react'],
plugins: [
['@babel/plugin-proposal-object-rest-spread', { useBuiltIns: true }],
'@babel/plugin-proposal-class-properties'
]
}
},
{
test: /\.scss$/,
loader: 'ignore-loader'
},
{
test: /\.css$/,
loader: 'ignore-loader'
},
{
test: /\.(jpg|png|svg|gif|pdf)$/,
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
}
}
]
}
};
index.js:
import http from 'http';
import fs from "fs";
import express from "express";
import favicon from 'serve-favicon';
// import renderer from "./renderer";
import renderApp from './welcome';
const app = express();
app.use(favicon('./public/favicon.ico'));
app.use(express.static("public"));
if (process.env.NODE_ENV !== 'production') {
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const serverConfig = require('../../webpack.server.config');
const compiler = webpack(serverConfig);
app.use(webpackDevMiddleware(compiler, {
stats: {colors: true},
headers: { "Access-Control-Allow-Origin": "http://localhost"},
publicPath: serverConfig.output.publicPath
}));
app.use(require("webpack-hot-middleware")(compiler));
}
app.get("*", function(req, res) {
fs.readFile("./src/server/html/index.html", "utf8", function(err, data) {
const context = {};
const html = renderApp();
//const html = renderer(data, req.path, context);
res.set('content-type', 'text/html');
res.send(html);
res.end();
});
});
const PORT = process.env.PORT || 8080;
app.listen(3000);
坦率地说,我对应该如何使用它也很困惑。
是否应该执行以下步骤?
这会神奇地重新加载我的服务器吗?
欢迎所有帮助,或者如果您碰巧正在运行演示程序。
我只是无法完成这项工作。
最后,我还将热重载(重新渲染)我的客户端包,但是我想这将是最简单的部分,因为我已经看到了很多有关此的资源。
答案 0 :(得分:1)
我认为这里的答案太复杂了。似乎Webpack并不容易做到这一点。
我没有亲自做些复杂的事情,而是亲自处理了这个问题,并创建了一个小型devserver,它可以在文件更改时进行webpack的构建和服务器的重新加载。客户端还具有重新加载逻辑,因此两种情况下的页面都将自动重新加载。
hot module reload for express server and webpack client
简介
devserver
const fetch = require('node-fetch')
let process
function spawnserver(){
process = require('child_process').spawn("node", ["server/server.js", "dev"])
process.stdout.on('data', (data) => {
console.error(`stdout: ${data}`)
})
process.stderr.on('data', (data) => {
console.error(`stderr: ${data}`)
})
}
function rebuildsrc(){
process = require('child_process').spawn("npm", ["run", "build"])
process.stdout.on('data', (data) => {
console.error(`stdout: ${data}`)
})
process.stderr.on('data', (data) => {
console.error(`stderr: ${data}`)
})
process.on("close", code => {
console.log("build exited with code", code)
fetch("http://localhost:3000/reloadsrc").then(response=>response.text().then(content=>console.log(content)))
})
}
spawnserver()
const watcher = require("chokidar").watch("./server")
watcher.on("ready", _=>{
watcher.on("all", _=>{
console.log("server reload")
process.kill()
spawnserver()
})
})
const srcWatcher = require("chokidar").watch("./src")
srcWatcher.on("ready", _=>{
srcWatcher.on("all", _=>{
console.log("rebuild src")
rebuildsrc()
})
})
客户端重新加载
let stamp = new Date().getTime()
let lastStamp = null
app.get('/stamp', (req, res) => {
lastStamp = new Date().getTime()
res.send(`${stamp}`)
})
app.get('/reloadsrc', (req, res) => {
stamp = new Date().getTime()
res.send(`updated stamp to ${stamp}`)
})
let welcomeMessage = "Welcome !!"
let reloadScript = IS_PROD ? ``:`
let stamp = null
let reloadStarted = false
setInterval(_=>{
fetch('/stamp').then(response=>response.text().then(content=>{
if(stamp){
if(content != stamp) setTimeout(_=>{
if(!reloadStarted){
console.log("commence reload")
setInterval(_=>{
fetch(document.location.href).then(response=>response.text().then(content=>{
if(content.match(/Welcome/)){
console.log("reloading")
document.location.reload()
}
}))
}, 1000)
reloadStarted = true
}
}, 500)
}else{
if(!reloadStarted){
stamp = content
console.log("stamp set to", stamp)
}
}
}))
}, 200)
`
app.use('/dist', express.static('dist'))
app.get('/', (req, res) => {
res.send(`
<!doctype html>
<head>
<title>Reload Express Sample App</title>
</head>
<body>
<h1>${welcomeMessage}</h1>
<script>
${reloadScript}
</script>
<script src="dist/bundle.js"></script>
</body>
</html>
`)
})
答案 1 :(得分:0)
Node.js,babel和webpack一直是我这个月的祸根。应该包括几个组件。您应该有一个名为“ package.json”的起始文件
内容应如下所示:
{
"name": "react-complete-guide",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server",
"build": "rimraf dist && webpack --config webpack.prod.config.js --progress --profile --color"
},
"author": "",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^7.1.5",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-preset-env": "^1.6.0",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"css-loader": "^0.28.7",
"file-loader": "^1.1.5",
"html-webpack-plugin": "^2.30.1",
"postcss-loader": "^2.0.7",
"style-loader": "^0.19.0",
"url-loader": "^0.6.2",
"webpack": "^3.6.0",
"webpack-dev-server": "^2.9.1"
},
"dependencies": {
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-router-dom": "^4.2.2"
}
}
如果键入“ npm start”,则运行代码“ start”:“ webpack-dev-server”。这将加载代码预览。 要将内容打包到构建中,请键入“ npm run build”,然后将运行代码“ build”:“ rimraf dist && webpack --config webpack.prod.config.js --progress --profile --color”。该代码将运行“ rimraf”,如果存在,则删除“ dist”文件夹,其余的将运行webpack配置文件。
您应该有两个Webpack文件。一种用于热装,另一种用于生产环境的包装。这些文件应名为:
“ webpack.config.js”和“ webpack.prod.config.js”。
“ webpack.config.js”的内容如下:
const path = require('path');
const autoprefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
chunkFilename: '[id].js',
publicPath: ''
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
localIdentName: '[name]__[local]__[hash:base64:5]'
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
autoprefixer({
browsers: [
"> 1%",
"last 2 versions"
]
})
]
}
}
]
},
{
test: /\.(png|jpe?g|gif)$/,
loader: 'url-loader?limit=8000&name=images/[name].[ext]'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: __dirname + '/src/index.html',
filename: 'index.html',
inject: 'body'
})
]
};
“ webpack.prod.config.js”的内容如下:
const path = require('path');
const autoprefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
devtool: 'cheap-module-source-map',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist/new'),
filename: 'bundle.js',
chunkFilename: '[id].js',
publicPath: ''
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
localIdentName: '[name]__[local]__[hash:base64:5]'
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
autoprefixer({
browsers: [
"> 1%",
"last 2 versions"
]
})
]
}
}
]
},
{
test: /\.(png|jpe?g|gif)$/,
loader: 'url-loader?limit=8000&name=images/[name].[ext]'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: __dirname + '/src/index.html',
filename: 'index.html',
inject: 'body'
}),
new webpack.optimize.UglifyJsPlugin()
]
};
您还需要一个文件来告诉babel如何采取行动。如果使用babel,则该文件称为“ .babelrc”。内容看起来像这样
{
"presets": [
["env", {
"targets": {
"browsers": [
"> 1%",
"last 2 versions"
]
}
}],
"stage-2",
"react"
],
"plugins": [
"syntax-dynamic-import"
]
}
这段代码中发生了很多事情。我强烈建议您观看一些教程视频。
答案 2 :(得分:0)
可能需要晚上睡觉。
我使用StartServerPlugin进行了此工作(包括React服务器渲染的组件)。
设置完成后,将重新加载Node Express服务器:
const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const StartServerPlugin = require('start-server-webpack-plugin');
module.exports = {
name: 'server',
mode: 'development',
target: 'node',
externals: nodeExternals({
whitelist: ['webpack/hot/poll?1000']
}),
entry: [ 'webpack/hot/poll?1000', './src/server/index' ],
output: {
path: path.resolve(__dirname, 'dist'),
// path: "/",
filename: 'server.js',
publicPath: '/assets/',
libraryTarget: 'commonjs2'
},
plugins: [
new StartServerPlugin({'name': 'server.js', nodeArgs: ['--inspect']}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
"process.env": {
"BUILD_TARGET": JSON.stringify('server')
}
})
],
module: {
rules: [
{
test: /.js$/,
loader: 'babel-loader',
include: path.resolve(__dirname, 'src/'),
exclude: /node_modules/,
options: {
presets:
[['@babel/preset-env', { modules: 'false' }], '@babel/preset-react'],
plugins: [
['@babel/plugin-proposal-object-rest-spread', { useBuiltIns: true }],
'@babel/plugin-proposal-class-properties'
]
}
},
{
test: /\.scss$/,
loader: 'ignore-loader'
},
{
test: /\.css$/,
loader: 'ignore-loader'
},
{
test: /\.(jpg|png|svg|gif|pdf)$/,
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
}
}
]
}
};
index.js:
import http from 'http'
import app from './server'
const server = http.createServer(app)
let currentApp = app;
const PORT = process.env.PORT || 8080;
server.listen(PORT);
if (module.hot) {
module.hot.accept('./server', () => {
server.removeListener('request', currentApp);
server.on('request', app);
currentApp = app;
})
}
server.js:
import http from 'http';
import fs from "fs";
import express from "express";
import favicon from 'serve-favicon';
import renderer from "./renderer";
import renderApp from './welcome';
const app = express();
app.use(favicon('./public/favicon.ico'));
app.use(express.static("public"));
app.get("*", function(req, res) {
fs.readFile("./src/server/html/index.html", "utf8", function(err, data) {
const context = {};
//const html = renderApp();
console.log('test');
const html = renderer(data, req.path, context);
res.set('content-type', 'text/html');
res.send(html);
res.end();
});
});
export default app;
运行方式:
rm -rf ./dist && webpack --config webpack.server.config.js --watch