客户端框架介绍

我们将从对框架领域进行概括性介绍开始,回顾 JavaScript 和框架的简要历史,探讨框架存在的原因和它们提供的好处,如何开始考虑选择一个框架来学习,以及客户端框架的替代方案。

预备知识 熟悉核心 HTMLCSSJavaScript 语言。
学习成果
  • 第三方代码是什么,以及客户端 JavaScript 框架是如何出现的。
  • 框架解决了哪些问题,有哪些替代方案,以及如何选择一个框架。
  • 库与框架的区别。
  • 何时应该使用框架,何时不应该使用框架。

库和框架的兴起

JavaScript 于 1996 年首次亮相,为此前由静态文档组成的 Web 增添了偶尔的交互性和趣味性。Web 不再仅仅是一个“**阅读事物**”的地方,而是一个“**做事情**”的地方。JavaScript 的受欢迎程度稳步上升。使用 JavaScript 的开发人员编写工具来解决他们面临的问题,并将它们打包成可重用包,称为**库**,以便与他人分享他们的解决方案。这个共享的库生态系统帮助塑造了 Web 的发展,并最终催生了框架。

**框架**是一个库,它对软件的构建方式提供了意见。这些意见使得应用程序具有可预测性和同质性;可预测性使得软件能够扩展到巨大的规模并仍然易于维护;可预测性和可维护性对于软件的健康和寿命至关重要。现代 JavaScript 框架的出现使得构建高度动态、交互式应用程序变得更加容易。

JavaScript 框架为现代 Web 上许多令人印象深刻的软件提供支持,包括你每天可能使用的许多网站。

有哪些框架?

市面上的框架很多,但目前被认为是“四大”的框架如下。

Ember

Ember 最初于 2011 年 12 月发布,是 SproutCore 项目工作的延续。它是一个较老的框架,用户比 React 和 Vue 等现代替代方案少,但由于其稳定性、社区支持和一些巧妙的编码原则,它仍然享有相当大的欢迎。

Angular

Angular 是一个开源 Web 应用程序框架,由 Google 的 Angular 团队以及个人和公司社区主导。它是构建 AngularJS 的同一团队的完全重写。Angular 于 2016 年 9 月 14 日正式发布。

Angular 是一个基于组件的框架,使用声明式 HTML 模板。在构建时,框架的编译器对开发人员透明地将模板转换为优化的 JavaScript 指令。Angular 使用 TypeScript,它是 JavaScript 的超集,我们将在下一章中更详细地介绍。

Vue

在参与并从原始 AngularJS 项目中学习之后,尤雨溪于 2014 年发布了 Vue。Vue 是四大框架中最年轻的,但最近受欢迎程度有所上升。

Vue,像 AngularJS 一样,用它自己的一些代码扩展了 HTML。除此之外,它主要依赖于现代的、标准的 JavaScript。

React

Facebook 于 2013 年发布了 React。在此之前,它已经在内部使用 React 解决了许多问题。从技术上讲,React 本身**不是**一个框架;它是一个用于渲染 UI 组件的库。React 与**其他**库结合使用来构建应用程序——React 和 React Native 使开发人员能够制作移动应用程序;React 和 ReactDOM 使他们能够制作 Web 应用程序,等等。

因为 React 和 ReactDOM 经常一起使用,所以 React 被口语化地理解为 JavaScript 框架。当你阅读本模块时,我们将以这种口语化的理解来工作。

React 用类似 HTML 的语法扩展了 JavaScript,称为 JSX

为什么会有框架?

我们已经讨论了激发框架创建的环境,但没有真正讨论开发人员为什么感到需要创建它们。探索“为什么”需要首先审视软件开发的挑战。

考虑一种常见的应用程序:一个待办事项列表创建器,我们将在未来的章节中研究使用各种框架来实现它。这个应用程序应该允许用户做一些事情,比如渲染任务列表、添加新任务和删除任务;它必须在可靠地跟踪和更新应用程序底层数据的同时完成这些操作。在软件开发中,这种底层数据被称为状态。

我们的每个目标在理论上都是独立的简单。我们可以迭代数据来渲染它;我们可以向对象添加内容来创建新任务;我们可以使用标识符来查找、编辑或删除任务。当我们记住应用程序必须让用户通过浏览器完成所有这些事情时,一些问题就开始显现出来。**真正的问题是:每次我们更改应用程序的状态时,我们都需要更新 UI 以匹配。**

我们可以通过查看待办事项列表应用程序的**一个**功能来审视这个问题的难度:渲染任务列表。

DOM 更改的冗长性

构建 HTML 元素并在适当的时间在浏览器中渲染它们需要惊人的大量代码。假设我们的状态是一个键值存储,包含 `taskName`(由文本输入控制)和 `tasks` 列表。

js
const state = {
  taskName: "",
  tasks: [
    {
      id: "todo-0",
      name: "Learn some frameworks!",
    },
  ],
};

我们如何向用户展示其中一个任务?我们希望将每个任务表示为一个列表项——一个 HTML `

  • ` 元素,位于一个无序列表元素(一个 `
      `)内部。我们如何制作它?它可能看起来像这样:

      js
      function buildTodoItemEl(id, name) {
        const item = document.createElement("li");
        const span = document.createElement("span");
      
        span.textContent = name;
      
        item.id = id;
        item.appendChild(span);
        item.appendChild(buildDeleteButtonEl(id));
      
        return item;
      }
      

      在这里,我们使用 `document.createElement()` 方法来创建我们的 `

    • `,并使用更多行代码来创建它所需的属性和子元素。

      前面的代码片段引用了另一个构建函数:`buildDeleteButtonEl()`。它遵循与我们用于构建列表项元素类似的模式。

      js
      function buildDeleteButtonEl(id) {
        const button = document.createElement("button");
        button.setAttribute("type", "button");
        button.addEventListener("click", () => {
          state.tasks = state.tasks.filter((t) => t.id !== id);
          renderTodoList();
        });
        button.textContent = "Delete";
      
        return button;
      }
      

      有趣的是,每次我们更新状态时,我们都需要手动调用 `renderTodoList`,以便我们的状态与屏幕同步。在页面上渲染项目的代码可能如下所示:

      js
      function renderTodoList() {
        const frag = document.createDocumentFragment();
        state.tasks.forEach((task) => {
          const item = buildTodoItemEl(task.id, task.name);
          frag.appendChild(item);
        });
      
        while (todoListEl.lastChild) {
          todoListEl.removeChild(todoListEl.lastChild);
        }
        todoListEl.appendChild(frag);
      }
      

      我们现在有将近三十行代码**只**用于 UI——**只**用于在 DOM 中渲染一些东西——而且我们从未添加过以后可以用来样式化列表项的类!

      如果你好奇,下面有一个完整的运行演示。你可以点击“播放”按钮在演练场中查看源代码。

      像这个例子一样直接使用 DOM,需要了解许多关于 DOM 如何工作的事情:如何创建元素;如何改变它们的属性;如何将元素嵌套在一起;如何将它们呈现在页面上。所有这些代码实际上都没有处理用户交互,也没有解决添加或删除任务的问题。如果我们要添加这些功能,我们必须记住在正确的时间以正确的方式更新我们的 UI。

      JavaScript 框架的创建是为了让这种工作变得更容易——它们的存在是为了提供更好的**开发者体验**。它们并没有为 JavaScript 带来全新的功能;它们让你更容易地访问 JavaScript 的功能,这样你就可以为今天的 Web 进行构建。

      阅读有关本节中使用的 JavaScript 功能的更多信息

  • 构建 UI 的另一种方式

    每个 JavaScript 框架都提供了一种更**声明性**地编写用户界面的方式。也就是说,它们允许你编写描述 UI 应该如何显示的代码,然后框架会在后台在 DOM 中实现它。

    香草 JavaScript 重复构建新 DOM 元素的方法很难一眼看懂。相比之下,以下代码块说明了你如何使用 Vue 来描述我们的任务列表:

    html
    <ul>
      <li v-for="task in tasks" v-bind:key="task.id">
        <span>{{task.name}}</span>
        <button type="button">Delete</button>
      </li>
    </ul>
    

    就是这样。这个代码片段将近三十行代码缩减到六行。如果这里的花括号和 `v-` 属性对你来说不熟悉,那没关系;你将在模块后面学习 Vue 特定的语法。这里需要记住的是,这段代码看起来像它所代表的 UI,而香草 JavaScript 代码则不是。

    多亏了 Vue,我们不必自己编写构建 UI 的函数;框架将以优化、高效的方式为我们处理。我们在这里的唯一作用是向 Vue 描述每个项目应该是什么样子。熟悉 Vue 的开发人员在加入我们的项目时可以迅速弄清楚发生了什么。Vue 并非独一无二:使用框架可以提高团队和个人效率。

    在原生 JavaScript 中,可以做**类似**的事情。模板字面量字符串 可以轻松编写 HTML 字符串,这些字符串代表最终元素的外观。对于像我们的待办事项列表应用程序这样简单的事情,这可能是一个有用的想法,但对于管理数千条数据记录并可能在用户界面中渲染同样多独特元素的大型应用程序来说,它是不可维护的。

    框架为我们带来的其他好处

    让我们看看框架提供的其他一些优势。正如我们之前提到的,框架的优势在原生 JavaScript 中也可以实现,但使用框架可以消除你自己解决这些问题所需的所有认知负担。

    工具

    由于本模块中的每个框架都有一个庞大而活跃的社区,每个框架的生态系统都提供了改进开发人员体验的工具。这些工具可以轻松添加测试(以确保你的应用程序按预期运行)或 Linting(以确保你的代码无错误且样式一致)等功能。

    **注意:** 如果你想了解更多关于 Web 工具概念的详细信息,请查看我们的客户端工具概述

    模块化

    大多数主流框架鼓励开发人员将其用户界面的不同部分抽象为**组件**——可维护、可重用的代码块,它们可以相互通信。与给定组件相关的所有代码都可以位于一个文件(或几个特定文件)中,这样作为开发人员,你就可以准确地知道在哪里可以更改该组件。在原生 JavaScript 应用程序中,你必须创建自己的一套约定才能以高效、可扩展的方式实现这一点。许多 JavaScript 开发人员,如果让他们自行其是,最终可能会将与 UI 的一部分相关的所有代码分散在整个文件中,或者完全在另一个文件中。

    路由

    Web 最基本的功能是允许用户从一个页面导航到另一个页面——毕竟,它是一个互联文档的网络。当你点击这个网站上的一个链接时,你的浏览器会与服务器通信并获取新的内容来显示给你。这样做时,地址栏中的 URL 会改变。你可以保存这个新的 URL 并在以后回到该页面,或者与他人分享,以便他们可以轻松找到同一页面。你的浏览器会记住你的导航历史记录,并允许你前后导航。这称为**服务器端路由**。

    现代 Web 应用程序通常不获取和渲染新的 HTML 文件——它们加载一个 HTML shell,并不断更新其中的 DOM(称为**单页应用程序**,或 **SPA**),而无需将用户导航到 Web 上的新地址。每个新的伪网页通常称为**视图**,并且默认情况下不进行路由。

    当 SPA 足够复杂,并渲染足够多的独特视图时,将路由功能引入应用程序非常重要。人们习惯于能够链接到应用程序中的特定页面,在导航历史记录中前进和后退等,当这些标准 Web 功能被破坏时,他们的体验会受到影响。当路由以这种方式由客户端应用程序处理时,它恰当地被称为**客户端路由**。

    使用 JavaScript 和浏览器原生功能构建路由器是**可能**的,但流行且活跃开发的框架都有配套库,使路由成为开发过程中更直观的一部分。

    使用框架时需要考虑的事项

    成为一名高效的 Web 开发人员意味着为工作使用最合适的工具。JavaScript 框架使前端应用程序开发变得容易,但它们并非解决所有问题的万能药。本节讨论了你在使用框架时应该考虑的一些事项。请记住,你可能根本不需要框架——请注意,不要仅仅为了使用框架而使用框架。

    对工具的熟悉程度

    就像原生 JavaScript 一样,框架也需要时间学习并有其独特之处。在决定为项目使用框架之前,请确保你有足够的时间学习其功能,使其对你有所帮助,而不是阻碍你,并确保你的队友也对此感到满意。

    过度设计

    如果你的 Web 开发项目是一个包含少量页面且几乎没有交互功能的个人作品集,那么框架(及其所有 JavaScript)可能根本没有必要。话虽如此,框架并非一成不变,有些框架比其他框架更适合小型项目。在 Smashing Magazine 的一篇文章中,Sarah Drasner 撰文讲述了 Vue 如何取代 jQuery 作为使网页小部分具有交互性的工具。

    更大的代码库和抽象

    框架允许你编写更声明性的代码——有时总体上代码会更少——通过在幕后为你处理 DOM 交互。这种抽象对于你的开发人员体验来说非常棒,但它并非没有代价。为了将你编写的内容转换为 DOM 更改,框架必须运行自己的代码,这反过来会使你的最终软件更大,运行成本更高。

    一些额外的代码是不可避免的,支持摇树优化(在构建过程中删除应用程序中实际未使用的任何代码)的框架将使你的应用程序保持较小,但你仍然需要在考虑应用程序性能时记住这个因素,尤其是在网络/存储受限的设备(如手机)上。

    框架的抽象不仅影响你的 JavaScript,还影响你与 Web 本质的关系。无论你如何构建 Web,最终结果,即用户最终与之交互的层面,都是 HTML。用 JavaScript 编写整个应用程序可能会让你忽视 HTML 及其各种标签的目的,并导致你生成一个非语义化且不可访问的 HTML 文档。事实上,有可能编写一个完全依赖 JavaScript 且没有 JavaScript 就无法运行的脆弱应用程序。

    框架并非我们问题的根源。在错误的优先级下,任何应用程序都可能脆弱、臃肿且无法访问。然而,框架确实会放大我们作为开发人员的优先级。如果你的优先级是创建一个复杂的 Web 应用程序,那么这样做很容易。但是,如果你的优先级没有仔细保护性能和可访问性,框架将放大你的脆弱性、臃肿性和不可访问性。由框架放大的现代开发人员优先级,在许多地方颠覆了 Web 的结构。Web 现在常常将 JavaScript 放在首位,将用户体验放在最后,而不是一个健壮的、内容优先的文档网络。

    框架驱动的 Web 上的可访问性

    让我们在前一节所说的基础上,再多谈一些可访问性。创建用户界面可访问性总是需要一些思考和努力,而框架可能会使这个过程复杂化。你通常需要使用高级框架 API 来访问原生浏览器功能,例如 ARIA 实时区域 或焦点管理。

    在某些情况下,框架应用程序会创建传统网站不存在的辅助功能障碍。其中最大的例子就是前面提到的客户端路由。

    使用传统(服务器端)路由,浏览网页会产生可预测的结果。浏览器知道将焦点设置在页面顶部,辅助技术会宣布页面标题。这些事情在你每次导航到新页面时都会发生。

    使用客户端路由时,你的浏览器不会加载新的网页,因此它不知道应该自动调整焦点或宣布新的页面标题。框架作者投入了大量时间和精力来编写 JavaScript 来重新创建这些功能,即使如此,也没有任何框架做得完美。

    结果是,你应该从**每个**Web 项目一开始就考虑可访问性,但请记住,如果你的代码库使用框架并抽象化,那么如果做不到这一点,很可能会出现重大的可访问性问题。

    如何选择框架

    本模块中讨论的每个框架都采用不同的方法进行 Web 应用程序开发。每个框架都在不断改进或改变,并且各有优缺点。选择合适的框架是一个依赖于团队和项目的过程,你应该自己研究以发现适合你需求的产品。话虽如此,我们已经确定了几个你可以提出的问题,以便更有效地研究你的选择:

    1. 框架支持哪些浏览器?
    2. 框架使用了哪些领域特定语言?
    3. 框架是否有强大的社区和良好的文档(以及其他支持)可用?

    本节中的表格一目了然地总结了每个框架当前提供的**浏览器支持**,以及它可以使用**领域特定语言**。

    广义上说,领域特定语言(DSLs) 是在软件开发特定领域相关的编程语言。在框架的上下文中,DSLs 是 JavaScript 或 HTML 的变体,可以使该框架的开发更容易。至关重要的是,没有一个框架**要求**开发人员使用特定的 DSL,但它们几乎都设计时都考虑到了特定的 DSL。选择不使用框架首选的 DSL 意味着你将错过原本可以改善开发人员体验的功能。

    在为任何新项目做出选择时,您应该认真考虑框架的支持矩阵和 DSL。浏览器支持不匹配可能会成为用户访问的障碍;DSL 支持不匹配可能会成为您和您的队友的障碍。

    框架 浏览器支持 首选 DSL 支持的 DSL 引用
    Angular 现代 TypeScript 基于 HTML;TypeScript 官方文档
    React 现代 JSX JSX;TypeScript 官方文档
    Vue 现代 (Vue 2 中的 IE9+) 基于 HTML 基于 HTML,JSX,Pug 官方文档
    Ember 现代 (Ember 版本 2.18 中的 IE9+) Handlebars Handlebars,TypeScript 官方文档

    **注意:** 我们描述为“基于 HTML”的 DSL 没有官方名称。它们并非真正的 DSL,但它们是非标准 HTML,因此我们认为它们值得强调。

    框架是否有强大的社区?

    这或许是最难衡量的一个指标,因为社区规模与易于获取的数字并不直接相关。你可以查看一个项目的 GitHub 星星数量或每周 npm 下载量来了解它的受欢迎程度,但有时最好的方法是搜索一些论坛或与其他开发者交流。这不仅仅是关于社区的规模,还在于它是否热情和包容,以及可用文档的质量如何。

    网络上的观点

    不要只听我们的一面之词——网络上有很多讨论。维基媒体基金会最近选择在其前端使用 Vue,并发布了一份关于框架采纳的征求意见(RFC)。RFC 的作者 Eric Gardner 花时间概述了维基媒体项目的需求以及为什么某些框架是团队的良好选择。这份 RFC 是你在计划使用前端框架时应该自己进行研究的一个很好的例子。

    JavaScript 状态调查是 JavaScript 开发人员反馈的有用集合。它涵盖了许多与 JavaScript 相关的主题,包括框架使用情况和开发人员对它们的看法。目前,有几年的数据可用,可以让你了解框架的受欢迎程度。

    Vue 团队详尽地将 Vue 与其他流行框架进行了比较。这种比较可能存在一些偏见(他们也提到了),但它仍然是一个宝贵的资源。

    客户端框架的替代方案

    如果你正在寻找加速 Web 开发过程的工具,并且你知道你的项目不需要密集的客户端 JavaScript,你可以选择其他一些解决方案来构建 Web:

    • 内容管理系统
    • 服务器端渲染
    • 静态网站生成器

    内容管理系统

    **内容管理系统** (**CMS**) 是任何允许用户直接创建 Web 内容而无需自己编写代码的工具。它们是大型项目的好解决方案,特别是那些需要编码能力有限的内容作者输入,或者希望节省时间的程序员的项目。然而,它们需要大量的时间来设置,并且使用 CMS 意味着你至少在一定程度上放弃了对网站最终输出的控制。例如:如果你选择的 CMS 默认不创作可访问的内容,那么通常很难改进这一点。

    一些流行的 CMS 系统包括 WordPressJoomlaDrupal

    服务器端渲染

    **服务器端渲染** (**SSR**) 是一种应用程序架构,其中**服务器**负责渲染单页应用程序。这与**客户端渲染**相反,后者是构建 JavaScript 应用程序最常见和最直接的方式。服务器端渲染对客户端设备来说更容易,因为你只向它们发送渲染过的 HTML 文件,但与客户端渲染的应用程序相比,它的设置可能很困难。

    本模块中介绍的所有框架都支持服务器端渲染和客户端渲染。请查看 React 的 Next.js、Vue 的 Nuxt(是的,这令人困惑,不,这些项目没有关联!)、Ember 的 FastBoot 和 Angular 的 Angular Universal

    **注意:** 有些 SSR 解决方案是由社区编写和维护的,而有些则是框架维护者提供的“官方”解决方案。

    静态站点生成器

    静态网站生成器是一种程序,它动态生成多页网站的所有网页——包括任何相关的 CSS 或 JavaScript——以便它们可以发布到任意数量的地方。例如,发布主机可以是 GitHub pages 分支、Netlify 实例,或你选择的任何私有服务器。这种方法有许多优点,主要围绕性能(用户的设备不需要用 JavaScript 构建页面;它已经完成)和安全性(静态页面具有更少的攻击向量)。这些网站仍然可以在需要时使用 JavaScript,但它们不**依赖**它。静态网站生成器需要时间来学习,就像任何其他工具一样,这可能会成为你开发过程的障碍。

    静态网站可以有任意数量的独特页面。就像框架赋能你快速编写客户端 JavaScript 应用程序一样,静态网站生成器为你提供了一种快速创建 HTML 文件的方式,否则你将不得不单独编写这些文件。与框架一样,静态网站生成器允许开发人员编写定义网页常见部分的组件,并将这些组件组合起来创建最终页面。在静态网站生成器的上下文中,这些组件被称为**模板**。由静态网站生成器构建的网页甚至可以托管框架应用程序:例如,如果你希望你的静态生成网站的某个特定页面在用户访问时启动 React 应用程序,你也可以这样做。

    静态网站生成器已经存在了相当长的时间,并且它们正在不断优化和创新。存在一系列选择,包括 AstroEleventyHugoJekyllGatsby,它们建立在各种技术栈上并提供独特的功能。其他选项,例如 DocusaurusVitePress,则使用客户端框架而不是模板,但生成同样优化的静态文件。

    如果你想全面了解静态站点生成器,请查看 Tatiana Mac 的 Eleventy 入门指南。在该系列的第一篇文章中,她解释了什么是静态站点生成器,以及它与发布 Web 内容的其他方式之间的关系。

    总结

    至此,我们对框架的介绍就结束了——我们还没有教你任何代码,但希望我们已经为你提供了有用的背景知识,让你了解为什么首先要使用框架以及如何选择框架,并让你对学习更多和深入其中感到兴奋!

    我们的下一篇文章将深入探讨更底层的内容,研究框架通常提供的特定功能类型,以及它们的工作原理。