在 single-page Svelte 应用程序中设置页面标题的最佳 place/lifecycle 方法

Best place/lifecycle method to set page titles in a single-page Svelte app

我开始使用 Svelte 并构建一个 single-page 应用程序(使用 page.js 作为路由器)。我想我会有一个单独的组件来生成 <svelte:head> 块,当每个组件安装时它会将页面标题写入商店,然后由头组件读取。它部分有效——当我点击不同的页面时,页面标题会更新。但是,如果我返回浏览器的历史记录,标题不会随页面一起变回。如果我重新加载页面,它确实会改变。所以也许 onMount() 不是正确的生命周期方法。我可以采用哪种方法来处理历史状态导航?

这是我的应用程序归结为一个最小的例子。

// index.js

import page from 'page'

import App from './views/App.svelte'

const app = new App({
    target: document.body,
    props: {
        route: null,
    },
})

function one() {
    app.$set({ route: 'one' })
}

function two() {
    app.$set({ route: 'two' })
}

page('/one', one)
page('/two', two)
page()

// App.svelte

<script>
    import One  from './One.svelte'
    import Two  from './Two.svelte'
    import Head from '../parts/Head.svelte'
    import Home from './Home.svelte'

    export let route
</script>

<Head />

{#if route === 'one'}
    <One />
{:else if route === 'two'}
    <Two />
{:else}
    <Home />
{/if}

// Head.svelte

<script>
    import { pageName } from '../stores.js'

    let displayPageName

    pageName.subscribe(value => {
        displayPageName = value
    })
</script>

<svelte:head>
    {#if displayPageName}
        <title>Test App &mdash; {displayPageName}</title>
    {:else}
        <title>Test App</title>
    {/if}
</svelte:head>

// stores.js

import { writable } from 'svelte/store'

export const pageName = writable(null)

// Home.svelte

<a href="/one">One</a> <a href="/two">Two</a>

// One.svelte

<script>
    import { onMount } from 'svelte'
    import { pageName } from '../stores.js'

    onMount(async () => {
        pageName.update(() => 'Component One')
    })
</script>

<a href="/two">Two</a>

// Two.svelte

<script>
    import { onMount } from 'svelte'
    import { pageName } from '../stores.js'

    onMount(async () => {
        pageName.update(() => 'Component Two')
    })
</script>

<a href="/one">One</a>

出于某些原因,Svelte 似乎不喜欢 {#if} 块中的 <title>...我建议改为计算 reactive statement 中的值。

$: title = $pageName ? `Test App \u2014 ${$pageName}` : 'Test App'

$pageName 是一种特殊的语法,用于访问存储的值而无需自己处理 subscribe/unsubscribe (docs)。

即便如此,我发现我的浏览器(在 Chrome 中测试)显然可以在返回时忽略 DOM 中的 <title>。我们可以强制 document.title 的值来解决这个问题。另一个反应块可以解决这个问题:

$: {
  document.title = title
}

所以你的整个 Head.svelte 组件现在看起来像这样:

<script>
  import { pageName } from '../stores.js'

  $: title = $pageName ? `Test App \u2014 ${$pageName}` : 'Test App'

  $: {
    document.title = title
  }
</script>

<svelte:head>
  <title>{title}</title>
</svelte:head>

最后,在您的示例中,当您导航到 / 时您没有更新商店的值,因此在这种情况下您的标题会过时。我想你需要像这样添加一条路线:

page('/', () => app.$set({ route: null }))