首页 > 解决方案 > Cloudflare Workers - 带有 Vuejs 的 SPA

问题描述

您好,我已使用以下命令将我的 Vue.js 应用程序部署到 Cloudflare 工作人员:

wrangler generate --site
wrangler publish --env dev

这是我的 wrangler.toml:

account_id = "xxx"
name = "name"
type = "webpack"
workers_dev = true

[site]
bucket = "./dist"
entry-point = "workers-site"

[env.dev]
name = "name"
route = "xxx.com/*"
zone_id = "XXX"
account_id = "XXX"

该网站很好,并且在“xxx.com”上运行,但是当我在任何其他路线上刷新页面时,我收到以下错误消息:

在您的内容命名空间中找不到 es-es/index.html

或者例如:

在您的内容命名空间中找不到 category/65/index.html

在 nginx 上,我必须创建一个 .htaccess,但我不知道如何让它在这里工作。

这是我的 index.js 以防万一:

import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler'

/**
 * The DEBUG flag will do two things that help during development:
 * 1. we will skip caching on the edge, which makes it easier to
 *    debug.
 * 2. we will return an error message on exception in your Response rather
 *    than the default 404.html page.
 */
const DEBUG = false

addEventListener('fetch', event => {
  try {
    event.respondWith(handleEvent(event))
  } catch (e) {
    if (DEBUG) {
      return event.respondWith(
        new Response(e.message || e.toString(), {
          status: 500,
        }),
      )
    }
    event.respondWith(new Response('Internal Error', { status: 500 }))
  }
})

async function handleEvent(event) {
  const url = new URL(event.request.url)
  let options = {}

  /**
   * You can add custom logic to how we fetch your assets
   * by configuring the function `mapRequestToAsset`
   */
  // options.mapRequestToAsset = handlePrefix(/^\/docs/)

  try {
    if (DEBUG) {
      // customize caching
      options.cacheControl = {
        bypassCache: true,
      }
    }
    return await getAssetFromKV(event, options)
  } catch (e) {
    // if an error is thrown try to serve the asset at 404.html
    if (!DEBUG) {
      try {
        let notFoundResponse = await getAssetFromKV(event, {
          mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/404.html`, req),
        })

        return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
      } catch (e) {}
    }

    return new Response(e.message || e.toString(), { status: 500 })
  }
}

/**
 * Here's one example of how to modify a request to
 * remove a specific prefix, in this case `/docs` from
 * the url. This can be useful if you are deploying to a
 * route on a zone, or if you only want your static content
 * to exist at a specific path.
 */
function handlePrefix(prefix) {
  return request => {
    // compute the default (e.g. / -> index.html)
    let defaultAssetKey = mapRequestToAsset(request)
    let url = new URL(defaultAssetKey.url)

    // strip the prefix from the path for lookup
    url.pathname = url.pathname.replace(prefix, '/')

    // inherit all other props from the default request
    return new Request(url.toString(), defaultAssetKey)
  }
}

标签: vue.jscloudflarecloudflare-workers

解决方案


如您所知,Vue.js(与许多其他 SPA 框架一样)期望对于任何未映射到特定文件的路径,服务器会回退到为根/index.html文件提供服务。然后 Vue 将在浏览器端的 JavaScript 中进行路由。您提到您知道如何.htaccess使用 .

好消息:在 Workers 中,我们可以编写代码来做任何我们想做的事情!

事实上,worker 代码已经有一个特定的代码块来处理“404 not found”错误。解决该问题的一种方法是更改​​此代码块,以便它不会返回 404 错误,而是返回/index.html.

我们要更改的代码是这部分:

  } catch (e) {
    // if an error is thrown try to serve the asset at 404.html
    if (!DEBUG) {
      try {
        let notFoundResponse = await getAssetFromKV(event, {
          mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/404.html`, req),
        })

        return new Response(notFoundResponse.body, { ...notFoundResponse, status: 404 })
      } catch (e) {}
    }

    return new Response(e.message || e.toString(), { status: 500 })
  }

我们想将其更改为:

  } catch (e) {
    // Fall back to serving `/index.html` on errors.
    return getAssetFromKV(event, {
      mapRequestToAsset: req => new Request(`${new URL(req.url).origin}/index.html`, req),
    })
  }

这应该够了吧。


但是,上面的解决方案有一个小问题:对于任何 HTML 页面(除了根页面),它都会进行两次查找,首先是针对特定路径,然后才会查找/index.html作为后备。这些查找非常快,但也许我们可以通过更智能一点并根据 URL 预先检测 HTML 页面来加快速度。

为此,我们要自定义mapRequestToAsset功能。您可以在代码中的注释中看到有关此的提示:

  /**
   * You can add custom logic to how we fetch your assets
   * by configuring the function `mapRequestToAsset`
   */
  // options.mapRequestToAsset = handlePrefix(/^\/docs/)

让我们继续使用它。将上面的注释替换为:

  options.mapRequestToAsset = req => {
    // First let's apply the default handler, which we imported from
    // '@cloudflare/kv-asset-handler' at the top of the file. We do
    // this because the default handler already has logic to detect
    // paths that should map to HTML files, for which it appends
    // `/index.html` to the path.
    req = mapRequestToAsset(req)

    // Now we can detect if the default handler decided to map to
    // index.html in some specific directory.
    if (req.url.endsWith('/index.html')) {
      // Indeed. Let's change it to instead map to the root `/index.html`.
      // This avoids the need to do a redundant lookup that we know will
      // fail.
      return new Request(`${new URL(req.url).origin}/index.html`, req)
    } else {
      // The default handler decided this is not an HTML page. It's probably
      // an image, CSS, or JS file. Leave it as-is.
      return req
    }
  }

现在,代码会专门检测 HTML 请求并将其替换为 root /index.html,因此无需浪费时间查找不存在的文件,只是为了捕获结果错误。对于其他类型的文件(图像、JS、CSS 等),代码不会修改文件名。


推荐阅读