我正在使用 Elmish 应用程序。我只为索引页面设置了 SSR(服务器端渲染),因为我需要在应用程序启动时动态生成一些初始状态。由于热模块更换不起作用并且它抛出以下警告:
[HMR] Update failed: SyntaxError: Unexpected token < in JSON at position 0
at JSON.parse (<anonymous>)
at XMLHttpRequest.request.onreadystatechange
我知道 webpack-devserver 的配置是错误的,因为它onreadystatechange
正在检索 html 中的索引页面,而不是 json 中的新应用程序文件(?)。但是阅读文档对我没有多大帮助,因为我不确定如何防止请求/en-EN/4b1807ffe818fe814be7.hot-update.json
被代理到后端。
我怎样才能让 HRM 重新开始工作?
我的 webpack.config 看起来像这样:
var path = require('path');
var webpack = require('webpack');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
var CONFIG = {
// The tags to include the generated JS and CSS will be automatically injected in the HTML template
// See https://github.com/jantimon/html-webpack-plugin
fsharpEntry: './src/UI/Client/Client.fsproj',
lessEntry: './src/UI/Client/style.less',
outputDir: './src/UI/Client/deploy',
assetsDir: './src/UI/Client/public',
devServerPort: 8080,
// When using webpack-dev-server, you may need to redirect some calls
// to a external API server. See https://webpack.js.org/configuration/dev-server/#devserver-proxy
devServerProxy: {
'/': {
target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
changeOrigin: true
},
// redirect requests that start with /api/* to the server on port 8085
'/api/*': {
target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
changeOrigin: true
},
// redirect websocket requests that start with /socket/* to the server on the port 8085
'/socket/*': {
target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
ws: true
}
},
// Use babel-preset-env to generate JS compatible with most-used browsers.
// More info at https://babeljs.io/docs/en/next/babel-preset-env.html
babel: {
presets: [
['@babel/preset-env', {
modules: false,
// This adds polyfills when needed. Requires core-js dependency.
// See https://babeljs.io/docs/en/babel-preset-env#usebuiltins
useBuiltIns: 'usage',
corejs: 3
}]
],
}
}
// If we're running the webpack-dev-server, assume we're in development mode
var isProduction = !process.argv.find(v => v.indexOf('webpack-dev-server') !== -1);
console.log('Bundling for ' + (isProduction ? 'production' : 'development') + '...');
module.exports = _ => {
var commonPlugins = [];
return {
// In development, split the JavaScript and CSS files in order to
// have a faster HMR support. In production bundle styles together
// with the code because the MiniCssExtractPlugin will extract the
// CSS in a separate files.
entry: isProduction ? {
app: [resolve(CONFIG.fsharpEntry), resolve(CONFIG.lessEntry)]
} : {
app: [resolve(CONFIG.fsharpEntry)],
style: [resolve(CONFIG.lessEntry)]
},
// Add a hash to the output file name in production
// to prevent browser caching if code changes
output: {
path: resolve(CONFIG.outputDir),
filename: isProduction ? '[name].[hash].js' : '[name].js'
},
mode: isProduction ? 'production' : 'development',
devtool: isProduction ? 'source-map' : 'eval-source-map',
optimization: {
splitChunks: {
chunks: 'all'
},
},
// Besides the HtmlPlugin, we use the following plugins:
// PRODUCTION
// - MiniCssExtractPlugin: Extracts CSS from bundle to a different file
// To minify CSS, see https://github.com/webpack-contrib/mini-css-extract-plugin#minimizing-for-production
// - CopyWebpackPlugin: Copies static assets to output directory
// DEVELOPMENT
// - HotModuleReplacementPlugin: Enables hot reloading when code changes without refreshing
plugins: isProduction ?
commonPlugins.concat([
new MiniCssExtractPlugin({ filename: 'style.[hash].css' }),
new CopyWebpackPlugin({ patterns : [{ from: resolve(CONFIG.assetsDir) }]}),
])
: commonPlugins.concat([
new webpack.HotModuleReplacementPlugin(),
]),
resolve: {
// See https://github.com/fable-compiler/Fable/issues/1490
symlinks: false
},
// Configuration for webpack-dev-server
devServer: {
publicPath: '/',
contentBase: resolve(CONFIG.assetsDir),
host: '0.0.0.0',
port: CONFIG.devServerPort,
proxy: CONFIG.devServerProxy,
hot: true,
inline: true,
historyApiFallback: true
},
// - fable-loader: transforms F# into JS
// - babel-loader: transforms JS to old syntax (compatible with old browsers)
// - sass-loaders: transforms SASS/SCSS into JS
// - file-loader: Moves files referenced in the code (fonts, images) into output folder
module: {
rules: [
{
test: /\.fs(x|proj)?$/,
use: {
loader: 'fable-loader',
options: {
babel: CONFIG.babel
}
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: CONFIG.babel
},
},
{
test: /\.(le|c)ss$/,
use: [
isProduction
? MiniCssExtractPlugin.loader
: 'style-loader',
'css-loader',
{
loader: 'less-loader',
options: { implementation: require('less') }
}
]
},
{
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)(\?.*)?$/,
use: ['file-loader']
}
]
}
};
};
function resolve(filePath) {
console.log("resolve filepath: " + filePath);
var resolved = path.isAbsolute(filePath) ? filePath : path.join(__dirname, filePath);
console.log("resolved: " + resolved);
return resolved;
}
我的服务器相关部分如下所示:
// Fable.Remoting api (as giraffe HttpHandler)
let createApi config httpClient translator=
Remoting.createApi()
|> Remoting.withErrorHandler errorHandler
|> Remoting.withRouteBuilder Route.builder
|> Remoting.fromValue (api config httpClient translator)
|> Remoting.buildHttpHandler
let printRequestPath : HttpHandler =
fun next ctx ->
printfn "request path: %O" ctx.Request.Path
next ctx
let webApp config httpClient translator =
choose [
GET >=> route "/" >=> Index.indexHandler config
createApi config httpClient translator
GET >=> Index.indexHandler config // default every GET route to index so client application handles it. TODO: there is a better way? rewriting on prod? on webpack-devserver historyapifallback?
]