如何透明地将原生 JavaScript 变量转换为反应式 Meteor 变量? [英] How to transparently turn a native JavaScript variable into a reactive Meteor variable?

查看:23
本文介绍了如何透明地将原生 JavaScript 变量转换为反应式 Meteor 变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当本机变量更改时不会重新运行跟踪器计算:

Tracker computations are not re-run when a native variable change:

var foo = 'foo';
Tracker.autorun(function logFoo() { console.log('foo is:', foo); });

这段代码只会执行一次:

This code will be executed only one time:

foo 是:
'富'

foo is:
'foo'

计算没有依赖性,没有_onInvalidateCallback.它几乎死了.

The computation has no dependency, no _onInvalidateCallback. It's pretty much dead.

然而,在很多情况下,我确实需要一个原生 JavaScript 变量或一个对象字段以某种方式在 Tracker 计算中反应性地运行(原生 API 未完全移植到 Meteor,...)

There is however a lot of cases where I do need a native JavaScript variable or an object field to somehow run reactively inside Tracker computations (native API not fully ported to Meteor, ...)

我当然不能简单地写:

foo = new ReactiveVar(foo);

由于我会破坏当前帧的引用,其他函数可能会使用 foo 的另一个引用,从而导致不同步和痛苦和头痛.
以类似的方式...

Since I will break the reference for the current frame and other functions may use another reference for foo, thus desynchronisation and pain and headaches.
In a similar way...

obj.foo = new ReactiveVar(obj.foo);

这也会中断,因为 obj.foo 现在完全不同,依赖于 obj.foo 的代码是一个简单的、非反应性的值将立即中断.
它也对模块模式(对 obj.foo 的孤立引用)毫无用处,并且会导致更多的不同步和更多的痛苦,甚至更多的头痛.

This will also break, since obj.foo is now completely different and code depending on obj.foo being a simple, non-reactive value will immediately break.
It is also of no use against a module pattern (an isolated reference to obj.foo) and will cause more desynchronisation and more pain and even more headaches.

如何在不破坏遗留代码的情况下将原生 Javascript 变量或对象字段正确更改为 Reactive-Var?

How can I properly change a native Javascript variable or object field to a Reactive-Var without breaking legacy code?

推荐答案

native 变量和对象字段这两种情况需要分开处理,需要不同的处理方式.第一个将使用一个简单但肮脏的技巧,第二个将使用更高级的技巧.
让我们从原生变量案例开始.

The two cases, native variable and object field, need to be taken separately, they will require different approaches. The first one will use a simple yet dirty trick, the second a more advanced trick.
Let's start with the native variable case.

如果变量是可写对象字段,那么我们可以将引用改为创建一个自定义的 get/set 对链接到闭包中的反应变量.
就这么简单:

If the variable is a writable object field, then we can change the reference to make a custom get/set couple linked to a reactive variable in a closure.
It's as simple as that:

function reactivise(obj, field) {
  var rvar = new ReactiveVar(obj[field]);

  Object.defineProperty(obj, field, {
    get : function() {
      return rvar.get();
    },
    set : function(value) {
      rvar.set(value);
      return value;
    }
  })
}

它只是有效.使用 obj.foo 的本机代码不会注意到变化(除非他们检查属性描述符,但这是一件很奇怪的事情).然而,响应式计算将因更改此字段而失效.
但是,它对模块模式很弱(引用隔离以防止损坏).这是此类模块的示例:

And it just works. Native code using obj.foo won't notice a change (unless they check for the property descriptors but that's a weird thing to do). Reactive computations however will be invalidated by changes to this field.
However, it is weak against the module pattern (reference isolation to prevent corruption). Here's an example of such module :

(function logFoo(foo) {
  console.log(foo);
}(obj.foo);

这段代码不会在意你更改了 getter 还是 setter,它已经保存了引用.

This code won't care that you changed the getter or the setter, it already holds the reference.

可能有办法解决这个问题……但在撰写本文时,这几乎是一种炼金术.一个可以提供帮助的 ES7 功能:Object.观察.今天太年轻了,我就不举例了.

There might be a way around this... But as of this writing it's pretty much alchemy. An ES7 feature that could help: Object.observe. It is so young today that I won't draw an example out of it.

如果你想观察的不是非模块对象字段(上面的例子),那么我知道的唯一解决方案就是轮询.
基本上,定期检查值是否发生变化,并为此设置一个反应变量(我们失去了透明度).

If what you want to observe is not a non-moduled object field (example above), then the only solution I know of is polling.
Basically, regularly check if the value changed, and set a new reactive variable for that (we lose the transparency).

此类轮询的示例:

function reactivePoll(getter) {
  var rPoll = new ReactiveVar(getter());

  Meteor.setInterval(function pollVariable() {
    var newValue = getter();
    if(!_.isEqual(rPoll.curValue, newValue)) {
      rPoll.set(newValue);
    }
  }, 100);

  return rPoll;
}

我们需要让它工作的不是变量引用本身(foo),而是这个变量的getter.这是因为如果稍后在代码中更改 foo 引用,我们的函数将不会意识到它(更加痛苦的不同步头痛).
另外,我们每次都必须检查深度相等,以确保在我们导致失效之前该值确实发生了变化,因为如果 Tracker 看到一个非原始值,它会自动失效.

What we need for it to work is not the variable reference in itself (foo), but a getter to this variable. This is because if the foo reference is later changed in the code our function won't be aware of it (still more painful desynchronized headaches).
Plus, we have to check for deep equality each time to make sure that the value did indeed change before we cause invalidations since Tracker will automatically invalidate if it sees a non-primitive value.

使用示例:

var reactiveFoo = reactivePoll(function getFoo() { return foo; });

它当然也适用于对象字段.
请注意,示例代码没有任何类型的停止机制.它将永远运行,可能会引起内存泄漏、崩溃、减慢您的应用程序并导致剧烈的头痛.不要在生产应用程序中这样使用它,调整它以更好地控制间隔.

It of course also works with object fields.
Note that the example code does not feature any kind of stop mechanism. It will run forever, may provoke memory leaks, crash, slow down your apps, and cause violent head pain. Do not use it as such in production applications, adapt it to add better control over the intervals.

最安全的选择是基本的脏轮询,即使这意味着更多的负载和防止内存泄漏所需的完全控制.

The safest bet is then the basic dirty polling, even if it means a bit more load and a complete control needed to prevent memory leaks.

这篇关于如何透明地将原生 JavaScript 变量转换为反应式 Meteor 变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆