Svelte:如何将数据从 javascript 文件传回 Svelte 组件

Svelte: How to pass data back to svelte component from javascript file

我是 svelte 的新手,我正在尝试使用 websocket 获取温度,下面是用于使用 websocket 获取温度的代码。

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>

网页显示零,这是初始值,不会进一步改变,但是在网络浏览器的控制台中,由于控制台日志语句,我能够获取温度

请指出正确的方向来解决这个问题。

谢谢

不错的 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 中,您可以使用商店。 Stores 本质上是某种简化的流(或在 ES 中称为 Observable),即它们表示随时间变化的值。与我们的回调示例相同,除了它们提供了一些其他非常有用的工具(例如计算来自其他商店的派生值),以及 Svelte 组件中的巧妙语法。当您需要从普通 JS 源导入 "reactivity" 时,商店是惯用的 Svelte 方式。请参阅 docs 以获取完整参考。

以下是我们如何使用商店重写回调示例:

// 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 组件中,您可以使用具有特殊 $ 前缀语法的商店来访问商店的 value(因为 temperature 变量是对商店本身的引用,它只是我们的目的,我们需要的结果是 value):

<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 store 还有一个非常方便的功能来处理资源的处置。也就是说,您打开一个 WS:您需要在某一时刻关闭它。 Svelte 商店可以提供帮助。

实际上,我们在上面看到的 $ 语法实际上会设置一个 订阅 到商店。并且当组件被销毁时订阅将被取消。如果 store 被多个组件订阅,只有当最后一个组件取消订阅时,store 才会被释放(如果有新的订阅,它会被重新初始化)。这对于管理代码中一次性物品的生命周期非常方便。

为了使用它,我们需要使用更高级的 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 逻辑可以封装在您的 JS 模块中。这样可以很好地分离关注点。您的 WS 生命周期也由 Svelte 自动管理! (我们已经涵盖了 75% 的商店主题......它们高效,直截了当......而且简单!)

注意我没有深入检查我从你的例子中粘贴的代码,似乎有一些小错误,但我相信你会的了解总体思路,您可以自己解决这些问题。