codecamp

Meteor 会话

会话

Meteor 是一个响应式框架。这意味着随着数据的变化, App 的改变并不需要你显式地做任何事情。

事实上,我们已经看到过我们的模板是如何根据数据和路由规则的变化去进行改变的。

我们将在后面的章节去深入了解这里面是如何工作的,但我们现在想介绍一些基本的响应性功能,它对于普通的 App 是非常有用的。

Meteor 的会话(Session)

现在在 Microscope 下,用户在 App 中的当前状态是完全包含在 URL 里面,并且需要从 URL (或者数据库)里面寻找。

但是在许多情况下,你需要存储一些只对应于当前用户的应用程序版本的短暂状态(例如,一个元素是否显示或隐藏)。利用 Session 可以很方便地去做到这一点。

Session 是一个全局的响应式数据存储。它全局性的意思是全局的单例对象:这个 Session 对象在全局都是可被访问到。全局变量通常被认为不是一件什么好事,不过在刚才的例子上,Session 可以作为中央通信总线用于项目的不同地方。

修改会话(Session)

会话 Session 是全局可访问的。设置一个 Session 的值,你可以调用:

❯ Session.set('pageTitle', 'A different title');

浏览器控制台(Browser console)

通过 Session.get('mySessionProperty'); 你可以重新读取数据的内容,这是一个响应式的数据源,这意味着如果你把它放在一个 Helper 里面,你会看到 Helper 根据 Session 变量的改变而响应式地改变它的输出。

让我们试一试,将下面的代码添加到布局模板:

<header class="navbar navbar-default" role="navigation"> 
  <div class="navbar-header">
    <a class="navbar-brand" href="{{pathFor 'postsList'}}">{{pageTitle}}</a>
  </div>
</header>

client/templates/application/layout.html

Template.layout.helpers({
  pageTitle: function() { return Session.get('pageTitle'); }
});

client/templates/application/layout.js

关于附录(Sidebar)的代码

请注意在附录章节中的代码并不是本书主流程的一部分。所以要么创建一个新分支(如果你使用 Git),要么确保在本章结束后恢复你所做的改动。

Meteor 的自动重载(即使用后面讲到的“动态代码重载技术”(HCR)的页面自动刷新)会保存 Session 的变量,所以我们现在应该看到“不同的标题”显示在导航栏中。如果不是,再次输入之前的 Session.set() 命令。

另外,如果我们去更改它的值(再次在浏览器控制台中),我们应该看到另一个标题显示:

❯ Session.set('pageTitle', 'A brand new title');

Browser console

由于 Session 的全局可访问性,所以这些变化可以作用到应用程序的任何地方。这给了我们很大的权力,但如果使用太多也可以是一个陷阱。

另外,重点指出的是 Session 对象不在用户之间共享,甚至在浏览器标签之间。这就是为什么如果现在你在新浏览器标签打开你的应用,你会看到一个空网站标题。

相同的变化

如果你通过 Session.set() 去修改一个 Session 变量,并将其修改为相同的值,Meteor 会非常聪明的绕过繁琐的操作,避免不必要的方法调用。

自动运行(Autorun)

我们看到响应式数据源的一个例子,并且看到了它在一个模板 Helper 里面的运作。尽管某些情况下 Meteor(如模板 Helper)是响应式的,但是大部分的 Meteor App 仍然是基于普通的非响应式的 JavaScript 代码。

让我们假设有以下的代码片段在我们的 App:

helloWorld = function() {
  alert(Session.get('message'));
}

尽管我们调用一个响应式会话(Session)变量,但它并不是在响应式的上下文中调用,所以当改变这个 Session 变量的时候,我们也不会自动运行 alert 函数。

这个时候,就要引入自动运行(Autorun)机制了。顾名思义,每一次 autorun 上下文中的响应式数据源发生变化的时候,autorun 函数就会自动运行。

尝试到浏览器控制台输入:

❯ Tracker.autorun( function() { console.log('Value is: ' + Session.get('pageTitle')); } );
Value is: A brand new title

浏览器控制台(Browser console)

如你所料,放在 autorun 函数里面的代码将会运行一次,把数据输出到控制台。现在,让我们尝试去改变标题:

❯ Session.set('pageTitle', 'Yet another value');
Value is: Yet another value

浏览器控制台(Browser console)

神奇吧!随着 Session 变量的改变, autorun 知道它必须重新运行自己的代码,并把新的值重新输出到控制台。

所以我们回到之前的例子,如果希望每次 Session 变量发生变化的时候引发新的警报(alert),我们需要做的就是将我们的代码封装在 autorun 函数里面:

Tracker.autorun(function() {
  alert(Session.get('message'));
});

正如我们前面看到的,autorun 会自动跟踪响应式数据源,在它们变化的时候作出响应的反应。

动态代码重载技术

在 Microscope 的开发过程中,我们已经用过 Meteor 的动态代码重载技术(HCR)去节省时间:当我们修改并保存一个源代码的文件后,Meteor 会立刻检测到变化,直接重启正在运行的 Meteor 服务器,并通知每个客户端重新加载该页面。

这类似于页面的自动刷新,但有一个很重要的差异。

为了找出那是什么,先重置一下之前改过的 Session 变量:

❯ Session.set('pageTitle', 'A brand new title');
❯ Session.get('pageTitle');
'A brand new title'

浏览器控制台(Browser console)

如果我们手动去重载浏览器窗口,自然就会丢失我们的 Session 变量(因为这将会创建一个新的会话)。另一方面,如果我们是引发动态代码重载(即,通过修改并保存我们的源文件)去重新加载页面,Session 变量却仍然存在。现在去试一试!

❯ Session.get('pageTitle');
'A brand new title'

浏览器控制台(Browser console)

因此,如果使用 Session 变量来保存用户状态,用户几乎不会察觉到动态代码重载的发生。因为它将保留所有 Session 变量的值。这可以使我们在部署新版本的时候,用户发生中断的机会降到最低。

再想一想,这意味着,只要我们做到用 URL 和 Session 把所有状态保存下来,那么当更新版本的时候,客户端正在运行的应用程序就可以动态重载,不丢失任何数据。

现在去检验一下当我们去手动刷新页面的时候发生了什么:

❯ Session.get('pageTitle');
null

浏览器控制台(Browser console)

当我们重载页面时,我们丢失了 Session 。在 HCR 中,Meteor 将 Session 保存到浏览器的本地存储并且在重载的之后再一次加载它。然而,在重载页面时发生的丢失行为是有一定道理的:如果用户重新加载页面,就好像他们已经再次浏览相同的 URL ,而且其他用户都会看到他们访问的 URL,所以他们应该重置为初始状态。

从中我们应该要学会:

  1. 应该在 Session 或者 URL 中存储用户状态。从而在动态代码重载的时候,让用户发生中断的机会降到最低。
  2. 尽可能使用 URL 去存储你想要共享在用户之间的状态。

以上总结了我们对会话(Session)——— Meteor 最有用的功能之一的探索。不要忘了在进行下一章之前,恢复你对代码的改动。

Meteor 路由
Meteor 添加用户
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }