六、保持会话状态

在前面的章节中,我们已经使用了 Meteor 的 session 对象来实现延迟加载技术。 在本章中,我们将更深入地了解它,并学习如何使用它来创建特定于模板的响应函数。

在本章中,我们将涵盖以下主题:

  • 会话是什么
  • 热代码推送如何影响会话
  • 使用会话重新运行模板帮助程序
  • 运行函数
  • Creating template-specific reactive functions

    注释

    如果你已经直接读了这一章,并想要遵循这些例子, 下载前一章的代码示例从这本书的 web 页面在 https://www.packtpub.com/books/content/support/17713从 GitHub 库 https://github.com/frozeman/book-building-single-page-web-apps-with-meteor/tree/chapter5

    这些代码示例还将包含所有样式文件,因此我们不必担心在过程中添加 CSS 代码。

Meteor 的会话对象

Meteor 提供的Session对象是一个反应式数据源,主要用于在热代码重新加载时保存全局状态,尽管当页面被手动重新加载时,它不会保存其数据,这与 PHP 会话不同。

注释

当我们上传新代码,服务器将这些更新推送到所有客户端时,就会发生热代码重载。

Session对象是响应式数据源。 这意味着在响应式函数中使用此会话变量时,当其值发生变化时,它将重新运行该函数。

session 变量的一个用途是维护应用的全局状态,例如,检查用户的侧边栏是否可见。

session 对象对于模板和应用的其他部分之间的简单数据通信没有用处,因为维护它将很快成为一场噩梦,并且可能发生命名冲突。

简单反应的更好方法

如果我们想使用一些应用内部通信,最好使用 Meteorreactive-var包,它附带一个Session类似ReactiveVar对象。

要使用它,我们可以简单地使用$ meteor add reactive-var添加它。

然后这个对象需要被实例化,并带有一个响应式的get()set()函数,就像session对象:

Var myReactiveVar = new ReactiveVar('my initial value');

// now we can get it in any reactive function
myReactiveVar.get();

// and set it, to rerun depending functions
myReactiveVar.set('my new value');

对于更多的自定义反应,我们可以使用 Meteor 的Tracker包构建我们自己的自定义反应对象。 要了解更多,请参考第 9 章高级反应

提示

对于绑定到特定模板实例的反应式变量,请在https://atmospherejs.com/frozeman/template-var查看我的frozeman:template-var包。

在模板助手中使用会话

因为所有的模板助手函数都是响应函数,所以使用会话对象的好地方就在这样的助手中。

响应式意味着当我们在函数中使用响应式对象时,当响应式对象发生变化时,该函数将重新运行,另外还将重新呈现模板的这一部分。

注释

模板帮助器不是唯一的响应函数; 我们也可以使用Tracker.autorun(function(){…})创建我们自己的,就像我们在前面的章节中看到的。

要演示在模板帮助程序中使用会话,请执行以下步骤:

  1. Let's open our my-meteor-blog/client/templates/home.js file and add the following helper code anywhere in the file:

    Template.home.helpers({ //... sessionExample: function(){ return Session.get('mySessionExample'); } });

    这将创建sessionExamplehelper,它返回mySessionExample会话变量的值。

  2. 接下来,我们需要将这个助手添加到我们的home模板本身,通过打开my-metepr-blog/client/templates/home.html文件,并将助手添加到我们的{{#each postsList}}块助手之上:

    <h2>This comes from our Session: <strong>{{sessionExample}}</strong></h2>

  3. Now, let's open up our browser at http://localhost:3000. We will see the static text we add appearing in our blog's home page. Yet, to see Meteor's reactive session at work, we need to open up the browser's console and type the following line of code:

    Session.set('mySessionExample', 'I just set this.');

    如下截图所示:

    Using sessions in template helpers

当我们按下回车后,我们看到文本被添加到模板中。 这是因为当我们调用Session.set('mySessionExample', ...)时,Meteor 会重新运行我们之前调用Session.get('mySessionExample')的所有反应函数。 对于模板帮助程序,这将只重新运行这个特定的模板帮助程序,只重新呈现模板的这一部分。

我们可以通过设置mySessionExample会话变量的不同值来尝试,这样我们就可以看到文本在任何时候是如何变化的。

会话和热代码推送

hot code push 是当我们更改文件时,Meteor 服务器将这些更改推送到客户端。 Meteor 足够聪明,可以重新加载页面,而不会丢失 HTML 表单或会话的值。 因此,会话可用于在热代码推送中保持用户状态一致。

为了看到这一点,我们将mySessionExample的值设置为任何我们想要的值,然后看到网站更新到这个值。

当我们现在转到我们的home.html文件并做一个小的改变,例如,删除{{sessionExample}}助手周围的<strong>并保存文件,我们看到我们的会话状态被保留了,即使页面使用新的更改模板重新加载。 下面的截图演示了这一点:

Session and hot code pushes

注释

如果我们使用浏览器的刷新按钮手动重新加载页面,会话将无法持久更改,文本将消失。

为了克服这一限制,Meteor 的包库中有许多包,它们在浏览器的本地存储中响应地存储数据,以便在页面重新加载时持续保存。 其中一种叫做persistent-session,可在http://atmospherejs.com/package/persistent-session上找到。

响应式运行函数

为了根据会话变化重新运行函数,Meteor 提供了Tracker.autorun()函数,我们以前用它来更改延迟加载订阅。

Tracker.autorun()函数将使我们传递给它的每个函数都具有反应式。 为了查看一个简单的示例,我们将创建一个函数,该函数将在每次函数重新运行时发出文本警告。

注释

Tracker包是会话在幕后使用的,以使反应式工作。 在第 9 章高级反应中,我们将更深入地了解这个包。

执行以下步骤以响应性地重新运行函数:

  1. Let's create a new file called main.js, but this time in the root of the my-meteor-blog folder, with the following content:

    ``` if(Meteor.isClient) {

    Tracker.autorun(function(){
        var example = Session.get('mySessionExample'); 
        alert(example);
    });
    

    } ```

    注释

    我们将在后面的章节中需要main.js文件。 因此,我们在根文件夹中创建了它,使它可以在客户机和服务器上访问。

    然而,由于 Meteor 的会话对象只在客户端可用,我们将使用if(Meteor.isClient)条件,以便只在客户端执行代码。

    当我们现在检查我们的浏览器,我们将看到一个警报显示undefined。 这是因为传递给Tracker.autorun()的函数也将在代码执行时运行,此时我们还没有设置会话。

  2. 要设置会话变量的默认值,我们可以使用Session.setDefault('mySessionExample', 'My Text')。 当会话的值未定义时,将在不运行任何响应函数的情况下设置会话。 如果会话变量的值已经设置,那么setDefault根本不会更改这些变量。

  3. 在我们的示例中,我们可能不希望在页面加载时出现警告窗口。 为了防止第一次运行,我们可以使用Tracker.Computation对象,它作为第一个参数传递给函数,并为我们提供了一个名为firstRun的属性。 该属性将在函数第一次运行时设置为true。 当我们使用这个时,我们可以防止在开始时显示警报:

    ``` Tracker.autorun(function(c){ var example = Session.get('mySessionExample');

    if(!c.firstRun) {
        alert(example);
    }
    

    }); ```

  4. 现在让我们转到浏览器的控制台,并将会话设置为任意值,以看到警报出现:

    Session.set('mySessionExample','Hi there!');

该代码的输出如下截图所示:

Rerunning functions reactively

注释

当我们再次运行相同的命令时,我们将不会看到警报窗口显示,因为 Meteor 足够聪明,当会话的值不改变时,可以防止重新运行。 如果我们将它设置为另一个值,警报将再次出现。

停止反应式功能

Tracker.Computation对象,传递作为第一个参数,也为我们提供了一种方法来阻止函数发生反应。 为了尝试这一点,我们将改变函数,以便当我们将stop字符串传递给会话时,它停止其反应式:

Tracker.autorun(function(c){
    var example = Session.get('mySessionExample'); 

    if(!c.firstRun) {
        if(Session.equals('mySessionExample', 'stop')) {
            alert('We stopped our reactive Function');
            c.stop();
        } else {
            alert(example);
        }
    }
});

现在,当我们进入浏览器控制台并运行Session.set('mySessionExample', 'stop')时,响应函数将停止响应。 为了测试这一点,我们可以尝试运行Session.set('mySessionExample', 'Another text'),我们将看到警报窗口不会出现。

注释

如果我们更改代码并重新加载热代码,响应式函数将再次变为响应式,因为代码将再次执行。

前面的示例还使用了一个名为Session.equals()的函数。 与使用Session.get('mySessionExample) === 'stop'相比,该函数可以比较两个标量值,同时防止不必要的重新计算。 使用Session.equals()只会在会话变量从更改该值时重新运行该函数。

注释

然而,在我们的示例中,这个函数没有什么区别,因为我们之前也调用了Session.get()

在模板中使用自动运行

虽然在某些情况下,在我们的应用中使用可能是有用的Tracker.autorun(),但随着我们的应用增长,它会很快变得难以维护这些全局反应功能。

因此,将响应式函数绑定到它们执行操作的模板是一种良好的实践。

幸运的是,Meteor 提供了一个特殊的Tracker.autorun()版本,绑定到一个模板实例,当模板被销毁时自动停止。

为了利用这一点,我们可以在created()或 render 回调中启动响应函数。 首先,让我们从main.js文件中注释掉前面的示例,这样我们就不会得到两个警告窗口。

打开我们的home.js文件并添加以下代码行:

Template.home.created = function(){

    this.autorun(function(){
        alert(Session.get('mySessionExample'));
    });
};

这将在创建主模板时创建反应函数。 当我们进入浏览器的控制台并将mySessionExample会话设置为一个新值时,我们会看到警报窗口出现,如下面的截图所示:

Using autorun in a template

现在,当我们开关的模板通过单击菜单中的链接,我们设置了mySessionExample使用浏览器控制台会话变量又到另一个值,我们不会看到警告窗口出现的反应this.autorun()停止模板时被毁。

注释

注意,所有的Tracker.autorun()函数都返回一个Tracker.Computation对象,该对象可用于在任何时候使用Tracker.Computation.stop()停止自动运行的反应式:

Var myReactiveFunction = Tracker.autorun(function(){...});
// Do something which needs to stop the autorun
myReactiveFunction.stop();

响应式会话对象

我们已经看到会话对象可以在其值改变时重新运行函数。 这与集合的find()findOne()函数的行为相同,当集合中的底层数据发生变化时,它们将重新运行函数。

我们可以使用会话来保持热代码推送的用户状态,例如下拉菜单或弹出窗口的状态。 但是,请记住,如果没有明确的命名约定,这些会话变量很快就会变得难以维护。

对于更具体的反应行为,使用 Meteor 的Tracker核心包构建自定义反应对象是很好的,我们将在9 章高级反应中介绍。

小结

在本章中,我们学习了如何处理 Meteor 的反应会话对象。 我们使用它来重新运行模板帮助程序和我们自己的自定义函数,并且我们使用created()destroyed()回调函数制作了一个响应式函数模板。

为了更深入地挖掘,可以查看 Meteor 的关于会话和反应的文档,参考以下资源:

你可以在https://www.packtpub.com/books/content/support/17713或在 GitHub 上https://github.com/frozeman/book-building-single-page-web-apps-with-meteor/tree/chapter6找到本章的代码示例。

在下一章中,我们将为我们的博客创建管理用户和后台,为创建和编辑文章奠定基础。