13、全栈开发:Angular Universal与Brisket框架解析

全栈开发:Angular Universal与Brisket框架解析

1. Angular Universal概述

Angular Universal是一个令人瞩目的项目。它源于开发者对AngularU演讲中相关特性的热烈反响,Brad和Tobias决定将其打造为Angular旗下的官方项目。在正式迁移到Angular仓库之前,许多工作都在Angular 2核心库之外进行。在随后的三个月里,部分功能逐渐被整合到核心中。预计到Angular 2发布时,Angular Universal库将变得极为精简,主要由针对特定后端Node.js框架(如Express或Hapi.js)的集成库组成。

这个库的一大亮点在于它并非特定于Angular 2。开发者既可以在Angular 1中使用,也能将其与React、Ember或其他框架搭配使用。在大多数情况下,使用该库能让页面瞬间具备功能,这正是开发者所追求的用户体验。

2. 全栈JavaScript开发的意义

“使用合适的工具完成工作”这一建议听起来很明智,但在实际应用中却存在诸多问题:
- 委员会审批 :在许多大型组织中,引入新技术需要经过多个委员会的批准,决策过程可能更多地受到政治因素而非技术优劣的影响。
- 团队内争议 :在某些工作环境中,选择技术会在团队成员之间引发紧张关系,因为大家可能有不同的偏好。
- 上下文切换 :在多样化的技术环境中工作,开发者要么需要频繁切换技术栈,导致思维损耗;要么按技术边界划分团队,增加沟通成本,降低工作效率。
- 代码重复 :在多样化的技术环境中,几乎无法避免代码重复。例如,组织内的安全标准和数据模型等通用内容,需要在不同语言中分别实现。

为了解决这些问题,一个简单的解决方案是选择一种主要技术并将其用于几乎所有场景。而JavaScript是唯一能在各处运行的语言,因此成为全栈开发的理想选择。尽管过去JavaScript在浏览器中的表现不佳,但随着时间的推移,它已经有了显著的改进。结合ES6和ES7的一些特性以及Angular 2,开发者可以轻松构建出色的全栈应用。

3. Angular 2助力全栈开发的特性
  • 通用依赖注入 :Angular 1的依赖注入(DI)虽然有效,但存在一些缺陷。而Angular 2的DI非常出色,不仅能在客户端和服务器端工作,还能在Angular之外使用。通过DI,开发者可以轻松替换与特定容器紧密耦合的代码。
  • 通用服务器渲染 :这一特性前面已经讨论过,它能显著提升页面的加载速度和SEO效果。
  • 通用事件发射 :Angular 2利用RxJS可观察对象处理事件,在客户端和服务器端的工作方式相同。而在Angular 1中,事件发射与客户端的$scope绑定,在服务器端无法使用。
  • 通用路由 :与事件发射类似,Angular 2的路由在客户端和服务器端都能很好地工作。
  • ES6模块 :统一的模块格式,让开发者可以用一种格式编写JavaScript代码,并在任何地方使用。
  • ES7装饰器(TypeScript) :在大型全栈开发中,使用TypeScript的自定义装饰器可以更轻松地实现安全和缓存等横切关注点。
  • 工具支持(Webpack、JSPM、Browserify) :新的模块打包工具能够从一个入口点开始遍历依赖树,生成一个打包好的JavaScript文件。这对于全栈开发非常有帮助,因为开发者无需分别创建/server和/client文件夹,所有客户端文件都可以通过依赖树提取出来。
4. GetHuman.com的实践案例

在GetHuman.com项目中,使用Angular 1实现的服务器渲染解决方案已经在生产环境中运行了六个多月。实践证明,服务器渲染和全栈开发的承诺得到了切实的兑现:
- 性能 :在很多情况下,用户在短短一秒多的时间内就能看到初始页面。
- 可扩展性 :仅使用三台单核Web服务器,就能轻松支持1000多个并发用户。
- 生产力 :直到最近,只有两名全职开发者负责十几个不同的Web应用。

目前,团队在持续改进Angular 1基础设施的同时,也在对Angular 2的应用进行原型开发。为了探索使用Angular 2构建全栈应用的最佳实践,还创建了一个开源的电子商务应用。

5. Brisket框架的诞生背景

Brisket是基于Backbone.js构建的同构JavaScript框架,由Bloomberg LP的消费者Web团队开发,并于2014年10月2日在GitHub上以Apache 2开源许可证发布。该团队在开发Brisket时遵循了三个指导原则:
- 代码自由 :让开发者在编写代码时拥有尽可能多的自由。
- 跨环境一致的API :确保API在不同环境中保持一致,方便开发和维护。
- 不妨碍技术进步 :框架的设计不会阻碍新技术的引入和应用。

6. 传统网站与单页应用的局限性

在2013年底,团队接到了将Bloomberg.com的意见板块重新打造成新数字媒体产品BloombergView.com的任务。该产品有诸多雄心勃勃的目标,如无限滚动、弹出式灯箱文章、响应式设计、快速加载和良好的SEO等。

此前,团队主要构建传统网站,即服务器端渲染页面并结合客户端代码处理交互。传统网站的优点是能快速在服务器端渲染页面,并且具有良好的SEO效果,但在处理大量客户端功能时存在不足:
- 模板和业务逻辑无法共享 :新网站的一些功能需要客户端渲染,而服务器端模板使用Ruby编写,无法在客户端复用,需要用JavaScript重新创建,同时还需要维护两套数据模型。
- 功能封装不佳 :传统网站中,服务器渲染功能的标记,客户端代码再添加额外功能,导致功能的完整生命周期难以理解。
- 页面导航感知速度慢 :在传统网站中点击新页面时,即使实际速度不慢,用户也会感觉页面切换缓慢。

单页应用(SPA)似乎更适合新网站,它能将所有应用代码集中在一起,提供更快的页面切换感知速度和丰富的用户界面。然而,SPA也存在缺点:
- 初始页面加载缓慢 :SPA在导航时感觉很快,但初始加载通常较慢,因为需要下载所有应用资产并启动应用,在应用启动之前用户无法读取任何内容。
- 缺乏SEO :由于SPA通常不在服务器端渲染标记,搜索引擎无法抓取其内容。虽然当时搜索引擎已经开始尝试抓取SPA,但这项技术还不够成熟,风险较大。

7. 探索解决方案的过程

为了结合传统网站和SPA的优点,团队考虑了“同构JavaScript”的概念,即可以在客户端和服务器之间共享的JavaScript代码。Rendr是一个先驱性的框架,它使用相同的代码库通过Node.js服务器渲染页面并在浏览器中处理客户端功能。但团队在尝试使用Rendr构建BloombergView的原型时,发现了一些问题:
- 模板方面 :Rendr默认使用Handlebars模板,而团队更喜欢Mustache模板,替换模板并非易事。
- 文件组织 :Rendr在某些情况下更倾向于遵循约定而非配置,要求应用文件遵循特定的文件夹结构,而团队希望使用基于领域驱动的文件夹结构。
- 代码风格 :Rendr对页面描述的编码风格限制较大,灵活性不足。例如,当数据是简单对象而非模型/集合时,不清楚如何构建路由。而且控制器无法直接访问视图,导致视图需要承担一些本应由控制器管理的职责。

以下是2013年Rendr控制器的示例代码:

var _ = require('underscore');
module.export = {
   index: function(params, callback) {
      var spec = {
         collection: {collection: 'Users', params: params}
      };
      this.app.fetch(spec, function(err, result){
         callback(err, result);
      });
   },
   show: function(params, callback) {
      var spec = {
         model: {model: 'User', params: param},
         repos: {collection: 'Repos', params: {user: params.login}}
      };
      this.app.fetch(spec, function(err, result){
         callback(err, result);
      });
   }
}

在探索了其他解决方案后,团队决定从头开始构建网站。由于时间紧迫,他们选择在Backbone的基础上进行开发,因为Backbone在服务器端更容易运行。

8. Brisket框架的关键工具

经过一段时间的开发,团队将BloombergView中的工具提取出来,形成了真正的Brisket框架。以下是一些关键工具:
| 工具名称 | 功能描述 |
| — | — |
| Brisket.createServer | 返回一个Express引擎,用于在应用中运行Brisket应用 |
| Brisket.RouterBrewery | 生成能够在服务器和客户端进行路由的路由器 |
| Brisket.Model, Brisket.Collection | 与环境无关的标准Backbone模型和集合的实现 |
| Brisket.View | 自定义的Backbone.View,支持重新附加视图、子视图管理和内存管理等核心功能 |
| Brisket.Templating.TemplateAdapter | 用于告知Brisket如何渲染模板 |
| Brisket请求/响应对象 | 标准化的请求/响应对象,提供cookie、应用级引用、设置响应状态等工具 |

这些工具的提取和重构都遵循了Brisket的核心原则,旨在为开发者提供更大的代码自由和跨环境的一致性。

9. Brisket的代码自由特性

从Rendr的经验中,团队认识到使用包含建模、路由、数据获取和渲染的完整同构框架时,如果不限制开发者的代码编写范围,会非常困难。虽然Brisket无法提供与普通Backbone应用相同程度的自由,但它努力接近这一目标。

在Brisket应用中,唯一的要求是路由处理程序必须返回一个视图或视图的Promise。其他方面,开发者可以像编写标准的Backbone应用一样自由。Brisket负责将视图放置在页面中,而不是让每个路由处理程序自行处理。

以下是Brisket路由处理程序的工作流程:

graph LR
    A[用户输入URL] --> B[Express处理请求]
    B --> C[请求转发到应用]
    C --> D[路由处理程序接收请求]
    D --> E[返回视图]
    E --> F[ServerRenderer接收视图]
    F --> G[序列化视图和布局]
    F --> H[发送数据获取结果]
    F --> I[渲染元标签]
    F --> J[渲染页面标题]
    F --> K[设置HTML基础标签]
    G --> L[HTML负载发送到浏览器]
    L --> M[应用在浏览器中继续处理]
    M --> N[ClientRenderer处理渲染]

在初始请求时,用户在浏览器中输入URL,Express处理请求并将其转发到应用。路由处理程序接收请求并返回一个视图,ServerRenderer负责序列化视图、发送数据获取结果、渲染元标签和页面标题,并设置HTML基础标签。当序列化的视图到达浏览器后,应用会从服务器中断的地方继续执行,ClientRenderer会处理渲染。

在后续的页面请求中,当用户点击应用链接时,请求由浏览器中的应用处理,路由处理程序的工作方式与服务器端相同,返回的视图由ClientRenderer更新布局内容。

综上所述,Angular Universal和Brisket框架都为全栈开发提供了强大的支持,通过解决传统开发中的各种问题,帮助开发者更高效地构建出优秀的应用。

全栈开发:Angular Universal与Brisket框架解析

10. Brisket路由处理程序示例

为了更好地理解Brisket路由处理程序的工作方式,下面给出一个示例:

// 示例Brisket路由处理程序
const MyView = require('./MyView');

module.exports = {
  index: function (params, callback) {
    // 这里可以进行数据获取等操作
    const view = new MyView();
    callback(null, view);
  },
  show: function (params, callback) {
    // 假设这里根据params获取特定数据
    const view = new MyView({ data: params });
    callback(null, view);
  }
};

在这个示例中,路由处理程序根据不同的路由( index show )创建并返回相应的视图。开发者可以在路由处理程序中进行数据获取、处理等操作,然后将视图传递给回调函数。

11. Brisket的优势总结

Brisket框架在全栈开发中具有诸多优势,以下通过表格进行总结:
| 优势 | 描述 |
| — | — |
| 代码自由 | 开发者在路由处理程序中拥有较大的代码编写自由,除了返回视图或视图的Promise外,可像编写标准Backbone应用一样操作。 |
| 跨环境一致性 | 路由处理程序在服务器端和客户端接收相同的输入并产生相同的输出,无需考虑运行环境。 |
| 良好的用户体验 | 通过设置HTML基础标签,即使JavaScript禁用或未加载完成,用户也能正常访问内容,保证了页面的可用性。 |
| 功能集成 | 集成了多种关键工具,如路由生成、模型和集合实现、视图管理等,方便开发者进行全栈开发。 |

12. Angular Universal与Brisket的对比

Angular Universal和Brisket虽然都用于全栈开发,但它们在多个方面存在差异,以下是详细对比:
| 对比项 | Angular Universal | Brisket |
| — | — | — |
| 基础框架 | 基于Angular | 基于Backbone.js |
| 适用场景 | 适用于使用Angular构建的项目,可与多种框架搭配 | 适合希望在Backbone基础上进行全栈开发的项目 |
| 核心特性 | 具有通用依赖注入、通用事件发射、通用路由等特性 | 强调代码自由、跨环境一致的API和不妨碍技术进步 |
| 学习曲线 | 对于熟悉Angular的开发者较友好,但对于初学者有一定难度 | 对于熟悉Backbone的开发者容易上手 |

13. 全栈开发的未来展望

随着技术的不断发展,全栈开发将变得越来越重要。Angular Universal和Brisket等框架为开发者提供了强大的工具,使得构建高效、可扩展的全栈应用成为可能。未来,全栈开发可能会朝着以下几个方向发展:
- 技术融合 :更多的框架和技术将相互融合,提供更全面的开发解决方案。例如,Angular Universal可能会与更多的后端框架进行深度集成,Brisket也可能会引入新的特性和工具。
- 性能优化 :开发者将更加关注应用的性能优化,包括页面加载速度、响应时间等。框架也会不断改进,以提供更好的性能。
- 开发效率提升 :随着工具和技术的不断完善,开发效率将得到进一步提升。例如,自动化工具将减少开发者的重复工作,提高开发速度。

14. 总结

全栈开发是当今软件开发的重要趋势,Angular Universal和Brisket框架分别从不同的角度为开发者提供了支持。Angular Universal借助Angular的强大功能,提供了一系列通用特性,适用于Angular项目的全栈开发。Brisket则基于Backbone.js,强调代码自由和跨环境一致性,为希望在Backbone基础上进行全栈开发的开发者提供了良好的选择。

通过对这两个框架的深入了解,开发者可以根据项目的需求和自身的技术栈选择合适的框架,从而构建出高效、可扩展的全栈应用。同时,随着技术的不断发展,全栈开发的未来充满了无限可能,开发者需要不断学习和探索,以跟上技术的步伐。

graph LR
    A[全栈开发需求] --> B[选择框架]
    B --> C{项目基于Angular?}
    C -- 是 --> D[使用Angular Universal]
    C -- 否 --> E{熟悉Backbone?}
    E -- 是 --> F[使用Brisket]
    E -- 否 --> G[探索其他框架]
    D --> H[构建全栈应用]
    F --> H

以上流程图展示了在全栈开发中选择框架的决策过程,开发者可以根据项目的基础和自身的技术熟悉程度来选择合适的框架进行全栈应用的构建。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值