如何摆脱 Meteor 模板闪烁

How to get rid of Meteor template flickers

我有一个具有多个条件的 Meteor 模板,在最初加载时我发现一些条件视图闪烁。

我正在使用 iron router,我知道订阅、wait() 和 ready() 选项,但是其中一个问题是主要条件 isInstalled 取决于 meteor.call 回调来设置 isInstalled 变量,因此等待不依赖于订阅。那么我该如何解释这个用例呢?

<template name="adminLayout">
    {{#if isInstalled}}
        {{#if currentUser}}
            {{> adminHeader}}
            <br /><br />
            <div class="row">  
              <div class="medium-3 columns">
              {{> adminNav}}
              </div>
              <div class="medium-9 columns">
                {{> yield}}
              </div>
            </div>
            <div class="row">
              <div class="medium-12 columns">
              {{> adminFooter}}
              </div>
            </div>
        {{else}}
            {{> login}}
        {{/if}}
    {{else}}
        {{> install}} 
    {{/if}}
</template>

这是我的模板助手,说明了我如何为 isInstalled

提供值
Meteor.call('isInstalled', function (err, result) {
  if (err) {
    console.log(err);
  }
  Session.set('isInstalled', result);
});

Template.adminLayout.helpers({
  isInstalled: function () {
    return Session.get('isInstalled');
  }
});

最后是路线:

Router.route('/admin', function () {
  this.layout('adminLayout');
  this.render('dashboard');
});

onBeforeAction 钩子怎么样?当然,您必须为 /login 和 /install 使用其他路由,但我认为这是更好的方法,因为用户应该能够通过 url 导航,例如:

Router.onBeforeAction(function() {
    if (!Meteor.userId()) {
        Router.go('/login');
        this.next();
    } else {
        if (/*check somehow if installed*/) {
            this.next();
        }else{
          Router.go('/install');
          this.next();
        }
    }
});

事实证明,闪烁问题实际上是一个 Meteor 问题,而不是 Iron Router 问题,尽管 Iron Router 可以使用其 wait() 和 ready() 方法提供一种解决方法来解决该问题。

在我的特殊情况下,我不需要等待订阅,而是等待 Meteor.call 结果。为了实现这一点,我创建了一个匿名函数,该函数 return 一个具有 Iron Router 可以理解的现成方法的对象句柄,我稍后可以在路由逻辑中实现。

Sindis 引导我朝着正确的方向前进,尽管它是一个不完整的解决方案。以下是我是如何完成的:

Router.onBeforeAction(function (params) {
    var self = this;
    if (params.url.match(/admin/)) {
        this.wait(function(){
            Meteor.call('isInstalled', function (err, result) {
                Session.set('installationCheck', true);
                Session.set('isInstalled', result);
            });
            return {
                ready: function () {
                    return Session.get('installationCheck');
                    self.next();
                }
            }   
        });
        if (this.ready()) {
            if (Session.get('isInstalled')) {
                this.next();
            } else if(Session.get('isInstalled') === false) {
                console.log('go to install!');
                this.render('install');
            }
        }
    } else {
        this.next();
    }
});

这是一个更通用的模式,允许您根据异步条件设置路由

Router.onBeforeAction(function (params) {
    var self = this;
    this.wait(function(){
        Meteor.call('someMethod', function (err, result) {
            Session.set('someMethodCalled', true);
            // do whatever with result...
            Session.set('someCondition', true); 
        });
        return {
            ready: function () {
                return Session.get('someMethodCalled');
                self.next();
            }
        }   
    });
    if (this.ready()) {
        if (Session.get('someCondition')) {
            this.next();
        } else if(Session.get('someCondition') === false) { // important to be explicit ===
            this.render('someSpecificRoute');
        }
    }
});