React 和 Vue 哪些事儿 - 1. 构建用户界面的两种框架

175 阅读7分钟

声明

预备知识要求掌握 HTMLCSSJavaScriptES6+,了解 Node.jsnpm

本教程仅是慢课堂对 React 和 Vue 两个框架的狭隘认识,坐井观天难免疏误,请您对后续内容降低期待,一切以框架官方文档为准。

正文开始:

1. 什么是前端

简单地讲,前端应对的是用户界面,而用户界面是联结人与计算机之间的桥梁。

前端需要考虑用户界面的两大职责:展示和交互。

展示

将数据(一般为文字和图像)展示给用户查看,并考虑视图展示时的布局排版、样式美化等。

再复杂一点讲,数据内容还包含图表、音视频等。数据可以是静态写死固定的,也可以是随时间流转或用户操作而动态变化的,前端通常需要根据数据的变化去调整视图。展示的数据如果是通过请求服务器接口获得,则要遵循传输规范,即 HTTP 协议

交互

响应和处理用户的操作,用户的操作行为包括鼠标点击、键盘输入、屏幕触摸、页面滚动等,必要时需要通过表单等收集用户提交的信息。

另外还需要处理非人为触发的计算机系统事件,例如定时器到期、网络请求完成、视频播放进度改变等。

展示和交互

2. 前端发展催生框架

1995 年 Web 1.0 上古时代,互联网内容是全静态的展示。而 2000 年左右进入 Web 2.0 时代,用户渴求在互联网上沟通、互动、参与,这样前端就发展出了动态 HTML、AJAX、JavaScript 操纵 DOM 等。那个时代的代表产品有 Gmail、Google Maps、Youtube 等。前端展示和交互随着互联网的发展面临愈发严峻的挑战。

技术方面,Web 2.0 时代的代表库是 jQuery,它简化了 DOM 操作,抹平了各浏览器的兼容性差异,方便前端进行样式修改、动画实现、AJAX 请求、事件监听等,并且有丰富的插件和扩展。

历史车轮滚滚,2009 至 2014 年,随着前后端彻底的分离,单页面应用的普及,Angular、React、Vue 相继诞生,这些前端框架都提倡组件化开发、声明式渲染,用数据驱动视图,前端开发人员终于从繁琐的 DOM 操作中解脱出来。

再之后,小程序、hybrid、RN、Flutter 一套代码四处运行、Electron 桌面应用,我们来到了大前端时代,当然这是后话。

现如今,React、Vue、Angular、Preact、Ember、Svelte、Alpine、Lit、Solid、Qwik、Stencil …… 前端框架遍地开花。

3. React 和 Vue 的特点

React官网这样介绍:

React 是用于构建 Web 和原生交互界面的库。React 组件接收数据并返回应该出现在屏幕上的内容。可以通过响应交互(例如用户输入)向它们传递新数据,然后 React 将更新屏幕以匹配新数据。

Vue官网如此描述:

Vue 是用于构建用户界面的渐进式 JavaScript 框架。它提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM。

根据上述官方介绍,二者都是用来构建用户界面的框架/库。通过声明数据/状态来驱动视图,当数据发生变化时,框架可以自动更新视图,而不再需要我们使用 JavaScript 去手动操作 DOM。二者均采用组件化,通过拆分、复用、组合而成整个应用程序。

数据驱动

数据驱动即声明式编程,我们更加关心数据本身,需要考量如何用数据去表述界面视图;相对应的旧做法是命令式,即通过代码一步一步详细描述视图的绘制和更新步骤。

例如,要实现一个待办事项列表应用,功能要求为:

  • 往列表中添加新的待办事项
  • 删除某条待办事项
  • 切换某条待办事项的完成与否

如果用命令式编程来写,大概需要这样的逻辑:

// 定义待办事项列表数据,数据结构为数组,数组元素为每一条待办事项,其信息包含事项描述和完成状态
const todoList = [{
  title: '笑到流眼泪',
  done: true
}, {
  title: '亲吻世界上最美的女孩',
  done: false
}];

const $ = str => document.querySelector(str);
// 列表容器 DOM 节点
const $ul = $('ul');

// 添加逻辑,需要创建节点、设置节点属性、给父容器添加子节点
const addTodo = title => {
  todoList.push({
    title,
    done: false
  });
  const $li = document.createElement('li');
  $li.innerHTML = `
    <label>
      <input type="checkbox" /> ${title}
    </label>
  `;
  $ul.appendChild($li);
};

// 删除逻辑,需要查找节点和通过父容器删除子节点
const delTodo = i => {
  todoList.splice(i, 1);
  const $li = $(`ul li:nth-child(${i + 1})`);
  $ul.removeChild($li);
};

// 切换逻辑,需要查找节点和修改节点 DOM 属性
const toggleTodo = i => {
  todoList[i].done = !todoList[i].done;
  const $checkbox = $(`ul li:nth-child(${i + 1}) input`);
  $checkbox.checked = todoList[i].done;
};

尽管只节选了部分代码,显而易见,在上述命令式写法中,需求的实现除了要修改数据外,还需要对视图的 DOM 树进行繁复琐碎的手动操作。针对上述代码,即便是引入 jQuery 做简化,或者对 DOM 操作逻辑进行统一封装,无论如何还是需要手动维护视图和数据的一致性。

通过 JavaScript 手动操作 DOM 的代码繁琐冗长,容易引发 bug,而且调整 DOM 会频繁触发浏览器的布局重排和图形重绘过程,导致性能问题。

让我们来看一下 React 是如何处理的:

// 定义数据和修改数据的方法
const [todoList, setTodoList] = useState([{
  title: '笑到流眼泪',
  done: true
}, {
  title: '亲吻世界上最美的女孩',
  done: false
}]);

const addTodo = title => {
  setTodoList([...todoList, {
    title,
    done: false
  }]);
};

const delTodo = i => {
  setTodoList(todoList.filter((_, index) => index !== i));
};

const toggleTodo = i => {
  setTodoList(todoList.map((item, index) => ({
    ...item,
    done: index === i ? !item.done : item.done
  })));
};

如果用 Vue 实现是这样的:

const todoList = reactive([{
  title: '笑到流眼泪',
  done: true
}, {
  title: '亲吻世界上最美的女孩',
  done: false
}]);

const addTodo = title => {
  todoList.push({
    title,
    done: false
  });
};

const delTodo = i => {
  todoList.splice(i, 1);
};

const toggleTodo = i => {
  todoList[i].done = !todoList[i].done;
};

我们此时还不清楚上述两段代码中的 useState 和 reactive 分别是什么鬼,但是大概也感受到了,之前操作 DOM 的代码部分统统消失了。React 和 Vue 两段代码略有差异但功能相同,具体原因后续内容会详尽说明。

在这两个框架中,我们仅将关注点放在数据上即可。默认数据的初始渲染,以及数据变化后的视图同步更新,React 和 Vue 框架会自动帮我们高效处理。当然,后续需要学习一点点绑定语法,从而告诉框架视图和数据的对应关系。

虚拟 DOM

驱动视图的数据发生变化后,React 和 Vue 框架同步界面视图的操作过程是十分高效的。原因是二者都采用了虚拟 DOM 的方案。

虚拟 DOM 是存在 JavaScript 内存中的树形结构数据,每次渲染发生时,会比对这一次产生的和上一次渲染的虚拟 DOM,计算结果差异,然后有效地进行最小化的 DOM 操作更新。

组件化

组件化,或者说模块化开发的核心思想是分而治之,如果一个问题作为整体很难理解透彻和解决,就把它拆分为多个小问题,各个击破,最后再将结果综合起来。

通过划分不同的功能模块降低耦合,各组件模块只关注自己的 UI/DOM 区域,模块内有自己的结构、逻辑、样式,各模块之间有跟其他模块交互的方式或接口。

React 使用函数组件,而 Vue 使用单文件组件,形式不同,但目的一致,例如下面音乐播放器应用视图和代码组件的对应关系,在两个框架中基本是相同的:

组件化开发

繁荣生态

React 和 Vue 框架自身只关注视图层,而复杂应用的实现,还包括下面内容:

  • 路由和导航,即通过不同 URL 路径对应不同的页面组件,从而实现多页应用
  • 全局统一状态管理,将应用的公共数据/状态储存在单一数据源中进行管理
  • 项目脚手架工具,提供项目初始化、开发调试、构建打包等功能
  • 其他第三方库/插件,例如 UI 组件库、数据请求、CSS 样式、服务端渲染等

React 和 Vue 框架本身,以及上述库和插件,共同构成了繁荣的生态,为前端开发提供了丰富的工具和资源。