clientRouting
环境:Browser
默认情况下,vite-plugin-ssr
执行 服务端路由
我们可以通过修改 /renderer/_default.client.js
来选择 客户端路由
- 设置
export const clientRouting = true
- 更新并适配 客户端
render()
hook.
React example:
Vue example:
用例和选项
// /renderer/_default.page.client.js
// Environment: Browser
export { render }
// 开启客户端路由
export const clientRouting = true
// 请参阅下面的 `Link prefetching` 部分。 默认值:`{ when: 'HOVER' }`。
export const prefetchStaticAssets = { when: 'VIEWPORT' }
// 你的 UI 框架是否允许 hydration 被中止
// (允许 vite-plugin-ssr 在用户点击链接完成之前中止 hydration)
// 只有 React 用户应该将 `hydrationCanBeAborted` 设置为 `true`
// (其他框架,如 Vue,会在 hydration 中止时抛出错误)
export const hydrationCanBeAborted = true
// 自定义页面过渡动画
export { onPageTransitionStart }
export { onPageTransitionEnd }
import { renderToDom, hydrateDom } from 'some-ui-framework'
async function render(pageContext) {
// `pageContext.isHydration` 由 `vite-plugin-ssr` 设置,
// 当页面已经渲染为 HTML 时为 `true`
if (pageContext.isHydration) {
// 我们给第一个页面 hydrate(由于我们做首屏 SSR,
// 第一个页面已经渲染为 HTML,我们只需要对其 hydrate)
await hydrate(pageContext.Page)
} else {
// 我们渲染一个新页面。 (当用户导航到新页面时。)
await renderToDom(pageContext.Page)
}
}
}
function onPageTransitionStart(pageContext) {
console.log('Page transition start')
// `pageContext.isBackwardNavigation` 也在 `render(pageContext)`
// 和 `onPageTransitionEnd(pageContext)` 中设置。
console.log('Is backwards navigation?', pageContext.isBackwardNavigation)
// 例如:
document.body.classList.add('page-transition')
}
function onPageTransitionEnd(pageContext) {
console.log('Page transition end')
// 例如:
document.body.classList.remove('page-transition')
}
请注意,pageContext
在页面导航时被完全丢弃并重新创建。
这就是它被称为 pageContext(而不是 appContext)的原因
我们可以继续使用 <a href="/some-url">
链接:客户端路由器自动拦截对 <a>
元素的点击。
我们可以通过添加 rel="external"
属性来跳过客户端路由器,例如 <a rel="external" href="/some/url">客户端路由器不会拦截我</a>
。
我们可以使用 navigate('/some/url')
以编程方式将用户导航到新页面。
默认情况下,客户端路由器在页面更改时滚动到页面顶部;
如果我们想保留滚动位置,可以使用<a keep-scroll-position />
/ navigate('/some/url', { keepScrollPosition: true })
(对于 嵌套布局 很有用)
状态初始化
通常,在使用 Apollo GraphQL、Redux 或 Vuex 等工具时,我们会在渲染 HTML 时确定服务端 UI 的初始状态,然后使用该初始状态初始化客户端
根据工具,我们执行以下操作之一:
初始化一次:
// /renderer/_default.page.server.js
// Environment: Node.js
export { render }
export { passToClient }
import { escapeInject, dangerouslySkipEscape } from 'vite-plugin-ssr'
import { renderToHtml } from 'some-ui-framework'
import { getInitialState } from './getInitialState'
const passToClient = ['initialState']
// `render()` hook 只在第一个页面被调用。
// (而在页面导航时也会调用 `onBeforeRender()`。)
async function render(pageContext) {
const initialState = await getInitialState()
// 我们使用 `initialState` 来渲染 HTML,
// 因此 HTML 包含 `initialState` 的内容。
const pageHtml = await renderToHtml(pageContext.Page, initialState)
const documentHtml = escapeInject`<!DOCTYPE html>
<html>
<body>
<div>${dangerouslySkipEscape(pageHtml)}</div>
</body>
</html>`
return {
documentHtml,
pageContext: {
initialState,
},
}
}
// /renderer/_default.page.client.js
// Environment: Browser
export { render }
import { initClientSide } from './initClientSide'
async function render(pageContext) {
// 第一个页面渲染为 HTML,并且 `pageContext.isHydration === true`
if (pageContext.isHydration) {
// `pageContext.initialState` is available here
initClientSide(pageContext.initialState)
} else {
// 请注意,`pageContext.initialState` 在这里不可用,
// 因为我们的 `render()` hook 只在第一个页面被调用
}
// ...
}
在每个页面导航上初始化:
// /renderer/_default.page.server.js
// Environment: Node.js
export { onBeforeRender }
export { passToClient }
import { getInitialState } from './getInitialState'
const passToClient = ['initialState']
// `onBeforeRender()` hook 在第一个页面和页面导航时被调用
// (而 `render()` 只在第一个页面被调用。)
async function onBeforeRender() {
const initialState = await getInitialState()
return {
pageContext: {
initialState,
},
}
}
// /renderer/_default.page.client.js
// Environment: Browser
export { render }
import { initClientSide } from './initClientSide'
async function render(pageContext) {
// 我们为每个页面渲染初始化状态。
// 因此,不仅是第一个页面,还有任何后续页面导航。
initClientSide(pageContext.initialState)
// ...
}
Link prefetching
默认情况下,只要用户将鼠标悬停在链接 <a href="/some-url">
上,就会加载 /some-url
的静态资源。
这意味着静态资源通常在用户点击链接之前就已加载。
我们可以通过使用视图窗口预获取来更快地获取资源:链接一旦出现在用户浏览器的视图窗口中就会被预获取。
// /renderer/_default.page.client.js
// Environment: Browser
// 一进入视图窗口就预获取链接
export const prefetchStaticAssets = { when: 'VIEWPORT' }
// 当用户的鼠标悬停在链接上时预获取链接
export const prefetchStaticAssets = { when: 'HOVER' }
// 禁用预获取
export const prefetchStaticAssets = false
视图窗口预获取在开发中被禁用。(因为它会使 Vite 转换所有预加载的页面,从而明显减慢开发速度)
我们可以通过设置 <a data-prefetch="true" href="/some-url" />
来覆盖单个链接的链接预获取行为
仅预获取静态资源;页面的 pageContext
不会被预获取,请参阅 #246
We can also have viewport prefetching for mobile users while having hover prefetching for desktop users:
我们还可以为移动用户开启视图窗口预获取,而为桌面用户开启 hover 预获取:
// 对于小屏幕,例如移动设备,视图窗口预获取可能是一个明智的策略
export const prefetchStaticAssets = window.matchMedia('(max-width: 600px)').matches
? { when: 'VIEWPORT' }
: { when: 'HOVER' }
// 或者我们为任何没有鼠标的设备启用视图窗口预获取:移动设备和平板电脑
// (但不包括具有触摸屏的笔记本电脑)。
export const prefetchStaticAssets = window.matchMedia('(any-hover: none)').matches
? { when: 'VIEWPORT' }
: { when: 'HOVER' }
另请参考: