2

私は Elmish アプリケーションで作業しています。アプリケーションの開始時に動的に生成される初期状態が必要なため、インデックス ページにのみ SSR (サーバー側レンダリング) を配置します。ホットモジュール交換が機能せず、次の警告がスローされているため:

[HMR] Update failed: SyntaxError: Unexpected token < in JSON at position 0
at JSON.parse (<anonymous>)
at XMLHttpRequest.request.onreadystatechange

onreadystatechangejson の新しいアプリ ファイル (?) ではなく、html でインデックス ページを取得しているため、webpack-devserver の構成が間違っていることはわかっています。/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?
    ]
4

1 に答える 1

1

さて、質問を書いてなんか思ったのですが、昨夜は疲れていたのですね。そこで、ドキュメントをもう一度読んで、機能するbypassオプションを見つけました。

プロキシ構成を次のように変更します。

devServerProxy: {
    '/': {
            target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
            changeOrigin: true,
            bypass: function(req, res, proxyOptions) {
                if (req.path.indexOf('.hot-update.js') !== -1) {
                    var lastSlashIndex = req.path.lastIndexOf('/');
                    return req.path.substr(lastSlashIndex);
                }
                return null;
            }
        }
// rest as before ...
}
于 2020-11-03T08:57:59.720 に答える