八个例子讲解现代前端框架前置知识(讲义)

这是《JS 深入浅出》(收费课程)第七课的讲义,主要讲「为什么会有双向绑定和单向绑定」,也就是历史和前端的心路历程。

JS 深入浅出 - 写代码啦!xiedaimala.com

起源

说到前端框架,就总会谈论到什么「双向绑定」和「单向绑定」这些概念了。

但是要理解这些概览,最好还是从其最原始的形态入手,也就是自己搞出双向绑定和单向绑定。

这就要说到 MVC 了。

2010 年,Backbone.js 第一版发布,三年后在腾讯工作的我才开始用这么个东西。由此可以看出英文世界的前端知识一直都是领先于其他语言的。

有人说 Backbone.js 是基于 MVC 思想的,也有人说 Backbone.js 是基于 MVP 思想的,我不打算给大家明确的答案,因为不管是 MVC 还是 MVP,都是类似的。今天我们从需求的角度来理解 MVX(X 可以是任何东西)思路。

意大利面条式的代码

使用框架的人总会说不使用框架的人写的是「意大利面条」



因为这些代码长长短短,还互相交织,你中有我,我中有你。

虽然乍看起来这种代码没有问题,但是时间久了之后,这种代码极难维护。

例子1:jsbin.com/noraye/8/edit?

一些程序员想出了解决办法

一些程序员通过自己的总结,发现这些代码总是可以分成三类:

  1. 专门操作远程数据的代码(fetchDb 和 saveDb 等等)
  2. 专门呈现页面元素的代码(innerHTML 等等)
  3. 其他控制逻辑的代码(点击某按钮之后做啥的代码)

为什么分成这三类呢?因为我们前端抄袭了后端的分类思想,后端代码也经常分为三类:

  1. 专门操作 MySQL 数据库的代码
  2. 专门渲染 HTML 的代码
  3. 其他控制逻辑的代码(用户请求首页之后去读数据库,然后渲染 HTML 作为响应等等)

这些思路经过慢慢的演化,最终被广大程序员完善为 MVC 思想。

  1. M 专门负责数据
  2. V 专门负责表现
  3. C 负责其他逻辑

如果我们来反思一下,会发现这个分类是无懈可击的:

  1. 每个网页都有数据
  2. 每个网页都有表现(具体为 HTML)
  3. 每个网页都有其他逻辑

于是乎,MVC 成了经久不衰的设计模式(设计模式就是「套路」的意思)

现在我们来改写一下例子1。

例子2:jsbin.com/yuwopuf/3/edi

改进了以下几点:

  1. 把意大利面条变成三块有结构有组织的对象:model、view 和 controller
  2. model 只负责存储数据、请求数据、更新数据
  3. view 只负责渲染 HTML(可接受一个 data 来定制数据)
  4. controller 负责调度 model 和 view

模板代码(也就是类)

一个页面或模块只需要 model view controller 三个对象
第二个页面就需要再来 model2 view2 controller2 三个对象
第三个页面就需要再来 model3 view3 controller3 三个对象
……
第N个页面就需要再来 modelN viewN controllerN 三个对象

你每次写一个 model 都要写很类似的代码
你每次写一个 view 都要写很类似的代码
你每次写一个 controller 都要写很类似的代码

为什么不利用模板代码(俗称面向对象)把重复的代码写到一个类呢(JS里面就是把「共有属性」放到原型里)

代码如下

例子3:jsbin.com/sodojac/5/edi

烦人的地方

一般来说,如果代码有重复或类似,就能优化(也可以不优化)。

例3的代码有这样的重复代码,我们一个一个来解决。

第一个烦人的地方:每次用 model 获取数据之后,还要「手动」调用 this.view.render(this.model.data),你看代码中有四处手动调用了 updateModel。

怎么解决呢?一个方案是给 Model 加上事件机制。

优化后的代码如下
例子4:jsbin.com/sodojac/10/ed

第二个烦人的地方有个 BUG:

每次 render 都会更新 #app 的 innerHTML,这可能会丢失用户的写在页面某个 input 里面的数据。(上课示例)

这有两个解决办法:

2.1. 用户只要输入了什么,就记录在 JS 的 data 里。(数据绑定的初步思想出现了)
2.2. 不要粗暴的操作 innerHTML,而是只更新需要更新的部位(虚拟 DOM 的初步思想出现了)

Angular 就是基于第一个思想而发明的,而 React 则是基于第二个思想。

有些人还觉得有第三个烦人的地方:

events 能不能直接写到 HTML 上面,而不是写到 JS 里。(上课示例)

确实这样写代码更直观,所以 Angular 和 React 都采纳了这一想法。但是又一波人不喜欢这种写法,他们认为 HTML 和 JS 应该是分离的:

我们花了好几年才普及内容与行为分离的观点,没想到 Angular 和 React 一下子就回到解放前了。

这种争论到 2017 年才渐渐休止,大家逐渐都接受了直接在 HTML 上绑定 JS 事件的写法。

接下来我们介绍 Angular 对于 2.1 问题的解法。

Angular 与 Vue 的双向绑定

由于 Angular 太复杂(概念),很多人表示学不会,这时出现了一个简化版的 Angular —— Vue 0.8。

当时的 Vue 主要借鉴了 Angular 的双向绑定思想,所以我们用 Vue 来举例更好理解,这样我就不用花一个小时来向你介绍 Angular 了。

例子5:jsbin.com/vixoku/4/edit?

Vue 代替了 View,这就是 Vue 的名字和其读音的来历。

Vue 的双向绑定(也是 Angular 的双向绑定)有这些功能:

  1. 只要 JS 改变了 view.number 或 view.name 或 view.n (注意 Vue 把 data 里面的 number、name 和 n 放到了 view 上面,没有 view.data 这个东西), HTML 就会局部更新
  2. 只要用户在 input 里输入了值,JS 里的 view.n 就会更新。

这就像双向绑定:JS 数据与页面元素互相绑定。

同时 Vue 也实现了局部更新(2.2)。

Vue 还有很多其他功能,使得 Controller 显得多余:

例子6:jsbin.com/ruzikax/2/edi

然后 model.data 也显得多余:

例子7:jsbin.com/cuhurit/5/edi

可以看到,事情变得「容易」了很多。

React —— 单向绑定

双向绑定看起来很 magic(魔法),但是有些人觉得单向更好:

例子8:jsbin.com/fodasut/3/edi

我们来想想为什么双向绑定不好呢?

因为「双向绑定」有一点点反「组件化」:跨组件的双向绑定很奇怪。

但是局部使用双向绑定是非常爽的。

单向绑定的要点

  1. 单向
  2. VirtualDOM

双向绑定的要点

  1. 实现方式
    1. Dirty Checking(AngularJS 1.x)的方式
    2. Reactive
      1. 使用 getter setter,缺点是无法监听不存在的属性
      2. 使用 Proxy

文章被以下专栏收录