Как я могу экспортировать функцию из компонента Svelte, которая изменяет значение в компоненте?

У меня есть компонент WastedTime.svelte со значением wastedTime. Также есть функция для изменения значения на 50 (в моем реальном коде это анимация, но это сокращенный тестовый пример для переполнения стека).

Чтобы разрешить вызов дочерней функции из родительской, я использовал <script context="module"> в документации Svelte:

<script context="module">
    var wastedTime = 0;
    export function changeNumber(){
        console.log('changing number')
        wastedTime = 50
    }
</script>

<script>
    // Putting 'var wastedTime = 0' here doesn't work either
</script>


<h1>Wasted time: {wastedTime}</h1>

Родитель вызывает функцию в дочернем элементе из onMount:

<script>

    import { onMount } from 'svelte';
    import WastedTime, {changeNumber } from './WastedTime.svelte';

    onMount(() => {
        changeNumber()
    });
</script>

<WastedTime />

Проблема в том, что, поскольку wastedTime упоминается в <script context="module">, похоже, что он не может изменить wastedTime. Экспортированная функция выполняется, но wastedTime остается равным 0.

Копия этого на Svelte REPL

Я пробовал: - Ввести var wastedTime = 0 в <script context="module"> - Ввести var wastedTime = 0 в <script>

Ни то, ни другое не работает.

Как я могу экспортировать функцию из компонента Svelte, которая изменяет значение в компоненте?


person mikemaccana    schedule 08.10.2019    source источник


Ответы (3)


<script context="module"> не является реактивным - изменения переменных внутри этого блока не повлияют на отдельные экземпляры (если вы не меняли значение хранилища, и каждый экземпляр был подписан на это хранилище).

Вместо этого экспортируйте функцию changeNumber непосредственно из экземпляра и получите ссылку на нее с помощью bind:this:

WastedTime.svelte

<script>
    var someNumber = 0;
    export function changeNumber(){
        console.log('changing number')
        someNumber = 56
    }
</script>

<h1>Wasted time: {someNumber}</h1>

App.svelte

<script>
    import { onMount } from 'svelte';
    import WastedTime from './WastedTime.svelte';

    let wastedTimeComponent;

    onMount(() => {
        wastedTimeComponent.changeNumber()
    });
</script>

<WastedTime bind:this={wastedTimeComponent} />

Демо здесь: https://svelte.dev/repl/f5304fef5c6e43edb8bf0d25d634f965

person Rich Harris    schedule 08.10.2019
comment
Спасибо. Клянусь, я прочитал пример в документации, в котором говорилось, что <script context="module"> был необходим для экспорта функции из компонента, но я, похоже, больше не могу его найти. - person mikemaccana; 09.10.2019
comment
вы, вероятно, думаете о svelte.dev/tutorial/module-exports - person Rich Harris; 09.10.2019
comment
Было бы здорово объяснить разницу между 1) Все, что экспортируется из блока сценария context = module, становится экспортом из самого модуля. и 2) изменения переменных внутри этого блока не повлияют на отдельные экземпляры. Должен ли я экспортировать переменные из script с context="module" или нет? - person mikemaccana; 25.10.2019
comment
Этот вопрос и ответ должны быть включены в документацию Svelte! Без объяснения этого паттерна это три кольца-цирк для связи с компонентом. - person bob; 25.03.2020
comment
Другой способ - разделить функции в WastedTimeService.js с помощью exported функций или как отдельный js-класс. Это постоянное выполнение упрощает модульное тестирование функций. - person nologin; 11.07.2020
comment
@Rich На самом деле пример REPL не работает, при нажатии выдается следующая ошибка: Error: message: "Cannot read property 'call' of undefined" stack: TypeError: Cannot read property 'call' of undefined at HTMLButtonElement.eval (eval at handle_message (about:srcdoc:13:8), <anonymous>:59:23) - person Tomasz Plonka; 10.09.2020
comment
похоже, есть проблема с версией Svelte, на которую ссылается эта ссылка. не уверен, что это было, но обновленная ссылка (с использованием текущей версии) отлично работает для меня - person Rich Harris; 10.09.2020

Что ж, это чертовски хороший вопрос!

Моя первая попытка:

Я пробовал ответить @Rich Harris (экспортировать функцию из компонента, привязать ее при использовании компонента с onMount прослушивателем событий). Концепция действительно прочная, но моя проблема была немного сложнее - я попытался передать событие с параметром, который будет вызываться извне. И да - этот параметр подделан с использованием {#await} .. так что ... не повезло с таким подходом


Моя вторая попытка:

Узнав больше о том, как svelte обрабатывает события (я новичок в стройности и учусь по мере продвижения), я нашел эту отличную статью. Вкратце: давайте сделаем наш компонент действительно ориентированным на события! В вашем вопросе - вы на самом деле пытаетесь реализовать onload событие ... так ... почему бы нам не сделать именно это?

component.svelte

<script>
  import { createEventDispatcher, onMount } from 'svelte';
  const dispatch = createEventDispatcher();

  function doSomething() { console.log('wowza!') }
  
  // wait for onMount to trigger, then dispatch event named "load"
  onMount(() => dispatch('load', 
  { 
    data: 'yay',
    doSomething
  }));
</script>

app.svelte

<script>
  import { Component } from './component.svelte';

  function component_load(event)
  {
    console.log(event.detail.data);
    event.detail.doSomething();
  }
</script>
<Component on:load={component_load} />

Так что - да, я кое-что узнал сегодня! Также:

  • Это более элегантный способ кодирования (управляемый событиями)
  • Компонент раскрывается при правильном использовании жизненного цикла событий Svelte
  • dispatch может запускаться в ответ на другие события, что позволяет вам построить полный жизненный цикл вашего компонента.

УДИВИТЕЛЬНО!

person ymz    schedule 26.11.2020

Упрощенная версия ответа Рича:

App.svelte

<script>
import Time from './Time.svelte';

let timeComponent;
let time;
</script>

<Time bind:this={timeComponent} bind:time />

<h1>Spent time: {time} ms</h1>

{#if timeComponent}
  <button on:click={() => timeComponent.spendTime() }>Click me</button>
{:else}
  Loading...
{/if}

Time.svelte

<script>
export var time = 0;
export function spendTime() {
  time += 50;
}
</script>

Ключ здесь export function.

person Ben Bucksch    schedule 05.05.2021