admin管理员组

文章数量:1279017

I'm getting started with Svelte and building a single-page application (using page.js as the router). I thought I'd have a separate ponent to produce the <svelte:head> block, and when each ponent mounts it would write the page title to a store, which would then be read by the head ponent. It partially works - the page title is updated when I click through to different pages. However, if I go back in my browser's history, the title doesn't change back with the page. It does change if I then reload the page. So perhaps onMount() isn't the right lifecycle method. What approach can I take that will work with history state navigation?

Here's my app boiled down to a minimal example.

// 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>

I'm getting started with Svelte and building a single-page application (using page.js as the router). I thought I'd have a separate ponent to produce the <svelte:head> block, and when each ponent mounts it would write the page title to a store, which would then be read by the head ponent. It partially works - the page title is updated when I click through to different pages. However, if I go back in my browser's history, the title doesn't change back with the page. It does change if I then reload the page. So perhaps onMount() isn't the right lifecycle method. What approach can I take that will work with history state navigation?

Here's my app boiled down to a minimal example.

// 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>
Share Improve this question edited Sep 5, 2020 at 16:50 Hex asked Nov 23, 2019 at 23:41 HexHex 1,3662 gold badges17 silver badges28 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 13

For some reasons, Svelte doesn't seem to like the <title> in an {#if} block... I remend puting the value in a reactive statement instead.

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

$pageName is a special syntax to access the store's value without having to handle subscribe/unsubscribe yourself (docs).

Even with that, I found that my browser (tested in Chrome) was apparently OK with ignoring the <title> in the DOM when navigating back. We can force the value of document.title to workaround that. Another reactive block can take care of that:

$: {
  document.title = title
}

So your whole Head.svelte ponent would now looks like this:

<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>

And finally, in your example, you're not updating the store's value when you navigate to /, so you'd have a stale title in this case. I think you need to add a route like so:

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

本文标签: javascriptBest placelifecycle method to set page titles in a singlepage Svelte appStack Overflow