单页应用程序是指存在于单个HTML文档中的应用程序。这意味着,如果您想根据应用程序的状态向用户显示一些不同的内容,就需要进行DOM操作(删除并替换当前文档中某些元素以显示不同的HTML),以更新用户看到的“视图”。如果这对您来说很明显,请不要介意,我认为我应该从这里开始。跟着我,我将解释您的路由情况将如何发挥作用(或多或少)。
URL由几个不同的部分组成,每个部分都会向浏览器提供特定的信息,以下载用户试图访问的资源所需的信息。通常,您要寻找的资源在某个服务器上,浏览器知道这一点是因为URL中包括“协议”(“http:”)和“主机”(“www.mydomain.com”)等部分,因此它会前往该服务器以查找您正在请求的内容。 URL中还有“查询”参数,这些参数为服务器提供了有关特定操作的其他信息,例如搜索查询的搜索词。在查询参数之后,是“哈希”。哈希是单页应用程序的神奇之处……呃,或者说,算是吧……
先来讲一下哈希。当您向URL添加“#”时,浏览器会将其后面的信息解释为当前显示的文档中的某个位置(元素)。这意味着,如果您有一个具有“id”为“main”的元素,并且将“#main”添加到URL的末尾,例如:“http://www.example.com#main”,浏览器将“滚动”(通常是“跳转”)到该元素的开头,以便您可以看到它。但请注意,如果您输入'
http://www.example.com/#main' (哈希符号与URL之间用斜杠分隔),那么您将强制进行完整的页面重新加载,并且浏览器将试图在服务器上找到名为“#main”的文件(我敢打赌它找不到)。
这里要记住的是,如果URL中存在哈希,则浏览器不会尝试从当前文档导航离开,特殊情况已经提到了,对于单页应用程序来说,这非常好,因为它们不想从页面导航离开或从服务器请求新文档。(看看单页应用程序的路由方式有什么不同?)
现在,关于哈希的整个问题并不是单页应用程序必须处理的事情,因为您可以创建一个而不处理它。实际上,一堆点击处理程序和DOM操作就足够了。但是,这意味着用户将无法共享应用程序中特定视图的链接。URL永远不会改变,我们永远无法直接导航到任何特定的视图。我们总是从您的应用程序的起始位置开始,这可能很容易成为令人讨厌的情况。如果你的单页面应用程序将有不同的视图,并且希望用户可以通过书签或链接直接导航到特定的视图,则需要在前端实现一种路由方式,除了必须在后端实现的路由(数据API等),这意味着你需要利用hash。我不想讨论不同框架如何在前端实现路由,但基本上是在用户点击链接时更新浏览器地址栏,监视地址栏以确定当前URL并将与该URL相关联的HTML加载到文档树中指定的位置的浏览器地址栏的一件事。
因此,在单页应用程序中,你在服务器上只有一个路由来处理渲染应用程序HTML文档(index.html),而你还有负责处理应用程序数据的路由(在数据库中创建新实例、登录和注销、编辑或破坏数据库中的实例以及获取数据等),这些路由通过AJAX请求调用。
这实际上是相当复杂的情况,因为HTML5允许我们放弃hash(通过服务器上的一些链接重写)并且能够使用“后退”和“前进”按钮,就好像我们已经导航离开了原始文档一样(但我们没有,因为我们只是将浏览器指向完全相同的URL,只是具有修改的hash值,因此没有发生新页面加载)。通过利用浏览器的History API可以实现传统的网站导航和链接,该API从IE版本10开始提供(我认为),其它主要的浏览器供应商早在很长时间前就已经掌握了这个技术,因此利用这项技术的框架将允许您的用户在URL中不使用hash导航到您的应用程序。解释这一点是一个转移和理解单页应用程序中的路由并不必要,但它很有趣,你最终还是需要学习它的。
应该使用AJAX从服务器请求JSON。AJAX请求将总是命中你的服务器,因为你不会在AJAX请求中包含hash符号(这样做是荒谬的,因为hash仅用于文档内浏览),因此服务器端的路由必须负责公开您的数据API(考虑RESTful)。尽管这不是单页应用程序中它们唯一的目的,但也许是它们最重要的目的。
所以,总之,你将有两组路由。一个在客户端(作为像AngularJS或EmberJS这样的客户端框架的一部分,列表还在继续……我更喜欢Angular,但对于那一个存在相当陡峭的学习曲线),另一个在服务器上。当你考虑“服务器路由”时,请考虑数据API。当你考虑“页面路由”时,请记住所有这些都在客户端上处理,通过你最初的服务器响应提供的JavaScript(这是与渲染HTML到浏览器有关的唯一必要的服务器端路由,加载你的“index.html”和所有必要的脚本和样式表等)。你将使用express.static中间件来提供静态文件,因此不必担心为那些东西分配路由。
编辑:快速提到AJAX实现。在服务器端,您将拥有与Alex提供的示例类似的路由,并使用您选择的框架或库公开的任何XMLHttpRequest(XHR)对象从客户端调用这些URL。现在认为将这些请求实现为Promises是标准且最佳实践http://wiki.commonjs.org/wiki/Promises/A。您应该自己阅读一些相关内容,但我可以简要概括一下,它是一种异步操作,类比于同步操作中的“try、catch、throw”。您将实例化一个Promise对象,并通过它尝试从服务器加载数据,例如,通过GET请求。确保您已经分配了函数来处理发送到您所请求的URL(服务器端路由)的请求!您通过此对象进行服务器请求时,该对象承诺会在请求结果从服务器返回后向您返回结果(无论请求成功或失败)。如果成功,它将调用您编写的一个函数并提供来自服务器的数据。如果失败,它将调用另一个由您编写的函数,并向其提供错误对象(或故障原因),以便您可以适当地处理错误。
希望这有助于回答您的问题。