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

时间:2020-03-31 10:20:35

标签: svelte

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

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 个答案:

答案 0 :(得分:5)

漂亮的小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组件中提供了一种巧妙的语法。当您需要从常规JS来源导入“反应性”时,商店就是惯用的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商店还具有非常方便的功能来处理资源处置。即,您打开一个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逻辑封装在JS模块中。这样可以很好地分离关注点。 Svelte也可以自动管理您的WS生命周期! (而且我们已经涵盖了75%的商店主题。。。它们很高效,而且很简单!)

注意我还没有深入检查过从您的示例粘贴的代码,这似乎有一些小错误,但是我敢肯定,您会了解总体思路,您可以自己修复这些问题。