Svelte: как передать данные обратно в компонент svelte из файла javascript - PullRequest
0 голосов
/ 31 марта 2020

Я новичок в svelte, я пытаюсь получить температуру с помощью веб-сокета. Ниже приведен код, используемый для получения температуры с помощью веб-сокетов.

ws. js

const webSock = () => {
  let socket = new WebSocket("ws://localhost:54321/webTest");
  let temperature = 0;
  const getTemperature = () => {
    return temperature;
  }

  socket.onopen = function (e) {
    console.log("[open] Connection established");
    console.log("Sending to server");
  };

  socket.onmessage = function (event) {
    var message = JSON.parse(event.data);
    temperature = data.message;
    console.log(temperature);
  };

  socket.onclose = function (event) {
    console.log(event.reason);
  };

  socket.onerror = function (error) {
    console.log(`[error] ${error.message}`);
  };

  const sendMessage = () => {
    var msg = {
      'data': 'hello'
    };
    console.log(msg);
    socket.send(JSON.stringify(msg));
  }

  return {getTemperature};
};

export default webSock;

Ниже приведен код на App.svelte

<script>
  import WS from "./ws.js";
  const ws = WS();
  $: temperature = ws.getTemperature();
</script>

<main>
  <h1>{temperature}</h1>
</main>

Web страница отображает ноль, который является начальным значением и не изменяется в дальнейшем, однако в консоли в веб-браузере я могу получить температуру из-за операторов журнала консоли

Пожалуйста, укажите мне правильное направление, чтобы это исправить выпуск.

Спасибо

1 Ответ

3 голосов
/ 31 марта 2020

Хороший маленький модуль ws:)

Итак, ваш ws.getTemperature() метод не является реактивным. И поэтому, хотя вы используете в реактивном выражении $: Svelte не будет знать, когда значение изменилось, поэтому он запустит его только один раз.

Вам нужно распространять не только значение, но также изменения .

В старой школе JS мы бы сделали это с обратным вызовом. Вы можете адаптировать свой код, например, так:

  let _callback

  const getTemperature = callback => {
    _callback = callback // <- save a callback for future change
    callback(temperature)
  }

  socket.onmessage = function (event) {
    var data = JSON.parse(event.data);
    temperature = data.message;
    console.log(temperature);

    if (_callback) _callback(temperature)
  };

В вашем компоненте Svelte вы можете подписаться на этот источник данных:

<script>
  import WS from "./ws.js";

  const ws = WS();

  let temperature
  ws.getTemperature(value => {
    temperature = value // <- Svelte will see that, it's reactive!
  })
</script>

<main>
  <h1>{temperature}</h1>
</main>

Здесь изменения будут распространяться, потому что мы присваиваем переменной верхнего уровня компонента. Назначение (=) - это то, что используется в Svelte для уведомления об изменении значения.

Этот код будет работать для того, что вы хотите сделать. Теперь в Svelte вы можете воспользоваться магазином. Хранилища - это, по сути, своего рода упрощенные потоки (или наблюдаемые, как они называются в ES), то есть они представляют значение во времени. То же, что и в нашем примере обратного вызова, за исключением того, что они предлагают несколько других очень полезных инструментов (например, вычисление производных значений из других хранилищ), а также удобный синтаксис в компоненте Svelte. Магазины - идиоматический c Svelte способ, когда вам нужно импортировать «реактивность» из ваших обычных JS источников. См. документы для полной справки.

Вот как мы переписали бы наш пример обратного вызова вместо хранилища:

// writable are the simplest form of stores
import { writable } from 'svelte/store'

const webSock = () => {
  let socket = new WebSocket("ws://localhost:54321/webTest");

  // our temperature is now a store with initial value 0
  const temperature = writable(0);

  // now we don't need to change this function, the change will be propaged
  // by the store itself
  const getTemperature = () => {
    return temperature;
  }

  socket.onmessage = function (event) {
    var data = JSON.parse(event.data);
    // temperature = data.message;

    // we update the value of our (writable) store, 
    // this will propagate the change
    temperature.set(data.message)
  };

  // ... rest of the code

  return { getTemperature };
};

export default webSock;

В вашем компоненте Svelte вы можете затем использовать Ваш магазин со специальным синтаксисом $ префикса, чтобы получить доступ к значению магазина (потому что переменная temperature является ссылкой на само хранилище, это просто средство для наших целей, результат нам нужно это значение ):

<script>
  import WS from "./ws.js";

  const ws = WS();

  const temperature = we.getTemperature()

  console.log(temperature) // log the store, for the fun

  // but what we want is the value, that we access with the special $ syntax
  $: temp = $temperature

  // for debug: this will log the value every time it changes
  $: console.log(temp)
</script>

<main>
  <!-- you can use the $ prefixed value directly in the template -->
  <!-- (so we actually don't need the reactive expression above, in this example) -->
  <h1>{$temperature}</h1>
</main>

Итак, очень хорошо, наш код стал меньше ... Но это еще не все! У магазина Svelte также есть очень удобная функция для утилизации ресурсов. А именно, вы открываете WS: вам нужно будет закрыть его в один момент. Хранилища Svelte могут помочь с этим.

Фактически, синтаксис $, который мы видели выше, фактически настроит подписку для магазина. И подписка будет отменена, когда компонент будет уничтожен. Если хранилище подписано несколькими компонентами, хранилище будет удалено только тогда, когда последний компонент откажется от подписки (оно будет повторно инициализировано, если будет создана новая подписка). Это очень удобно для управления жизненным циклом одноразовых вещей в вашем коде.

Чтобы использовать это, нам нужно использовать более продвинутый readable магазин. Вот ваш пример, обновленный для этого:

import { readable } from 'svelte/store'

// a readable store with initial value 0
//
// we pass it a function; the first argument of the function will let us update
// the value when it changes
//
export const temperature = readable(0, set => {
  // this function is called once, when the first subscriber to the store arrives

  let socket = new WebSocket("ws://localhost:54321/webTest");

  socket.onmessage = function (event) {
    var data = JSON.parse(event.data);

    // we're using the `set` function we've been provided to update the value
    // of the store
    set(data.message)
  };

  // ... the rest of your socket code

  const dispose = () => {
    socket.close()
  }

  // the function we return here will be called when the last subscriber
  // unsubscribes from the store (hence there's 0 subscribers left)
  return dispose
})

Потребительский компонент почти не изменится с нашего последнего примера. Разница лишь в том, как мы получим ссылку на наш магазин (поскольку теперь магазин экспортируется из модуля JS):

<script>
  import { temperature } from './ws.js'

  // log the value
  $: console.log($temperature)

  // draw the rest of the owl
</script>

...

Здесь! Теперь весь ваш WS logi c может быть инкапсулирован в ваш JS модуль. Это делает для хорошего разделения проблем. И ваш жизненный цикл WS также автоматически управляется Svelte! (И мы покрыли 75% магазина topi c ... Они эффективны, к сути ... И просты!)

Примечание У меня нет глубоко проверил код, который я вставил из вашего примера, и кажется, что в нем есть несколько небольших ошибок, но я уверен, что вы поймете общую идею и сможете исправить их самостоятельно.

...