无论路由如何,如何提供相同的静态文件?

How to serve the same static files regardless of the route?

我有一个 React 应用程序。我使用 webpack 构建生产环境。现在我尝试设置一个小 koa 服务器来服务 webpack 生成的用于生产的静态文件。

所以我这样做了

import Koa from 'koa'
import serve from 'koa-static'

const app = new Koa()

app.use(serve('dist/'))

app.listen(3001)

其中 dist/ 是文件(index.html、捆绑包等)所在的目录。 这很有效,但仅适用于路由 '/' (localhost:3001/) 在我的应用程序中,我使用了 React 路由器,因此我需要通过示例转到 /login (localhost:3001/login)。但是当我尝试时,我得到 "Not Found"。对于 devServer(通过 webpack),这条路线运行良好。我只需要始终服务 /dist,无论路线如何。如何使用 koa 做这个?

一种选择是拦截 Koa 中的 react-router 客户端路由,并将它们重写为 '/' 以加载 index.html 和客户端资产。

const REACT_ROUTER_PATHS = [
  '/login',
  '/logout',
  '/other-client-path'
];

app
  .use(async (ctx, next) => {
    if (REACT_ROUTER_PATHS.includes(ctx.request.path)) {
      ctx.request.path = '/';
    }
    await next();
  })
  .use(serve('dist/'));

如果 koa 像 express-static,他 returns 'Not Found' 是正常的,因为唯一存在的文件是 'index.html'。 我的解决方案

import fs from 'fs';


app.use(serve('dist/'));
// If the file is not found by koa
// intercept all request and return index.html
// this way you can manage the request in React 
app.use((ctx, next) => {
    ctx.type = 'html';
    ctx.body = fs.readFileSync('./dist/index.html');
});

好吧我终于赢了

import Koa from 'koa'
import serve from 'koa-static'
import fs from 'fs'
import path from 'path'

const app = new Koa()
const dist = path.join(__dirname, 'dist')
let validRoutes

fs.readdir(dist, (err, files) => {
  if (err) return console.log('Unable to scan directory: ' + err)
  validRoutes = files
})

function isRouteValid (url) {
  if (!validRoutes || !url) return false
  return validRoutes.find(route => url.slice(1) === route)
}

app.use(async (ctx, next) => {
  if (!isRouteValid(ctx.url)) ctx.path = 'index.html'
  await next()
})

app.use(serve(dist))

app.listen(3010)

根据构建的生成方式,仅查看生成的 dist 目录中的文件可能还不够。例如,如果您使用 CRA,您可能需要更像他们使用 in this post 的东西来查找子目录中的所有有效路由。所以你可能有一些看起来像

import Koa from 'koa';
import serve from 'koa-static';
import fs from 'fs';
import path from 'path';

const app = new Koa();
const dist = path.join(__dirname, 'dist');
let validRoutes;

var walk = function(dir, done) {
    var results = [];
    fs.readdir(dir, function(err, list) {
        if (err) return done(err);
        var i = 0;
        (function next() {
            var file = list[i++];
            if (!file) return done(null, results);
            file = path.resolve(dir, file);
            fs.stat(file, function(err, stat) {
                if (stat && stat.isDirectory()) {
                    walk(file, function(err, res) {
                        results = results.concat(res);
                        next();
                    });
                } else {
                    results.push(file);
                    next();
                }
           });
        })();
    });
};

walk(dist, function(err, results) {
    if (err) throw err;
    validRoutes = results;
});

function isRouteValid (r: string) {
    if (!validRoutes || !r) return false;
    return validRoutes.find(route => r === route);
}

app.use(async (ctx, next) => {
    if (!isRouteValid(path.resolve(dist, ctx.url.slice(1)))) ctx.path = 'index.html';
    await next();
});

app.use(serve(dist));

app.listen(3010);