vue

✍️ Tangxt ⏳ 2020-07-20 🏷️ MVC、单向绑定

09-MVC 与单向绑定

★Vue 代替 Controller

1)双向绑定

DOM 与 内存的关系

用代码说明一下:

<div id="app">
  <input type="text" v-model="n">
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
  let vm = new Vue({
    el: '#app',
    data: {
      n: 1
    }
  })
  console.log(vm.n)
  setTimeout(() => {
    console.log(vm.n)
  }, 5000)
</script>

双向绑定

改 DOM 自动映射到内存,改内存自动映射到 DOM

2)局部更新

哪个数据变了,就改哪里,而不是整个视图不管数据变没变,都改一遍……

总之,这不是无差别更新……而是有差别攻击……

3)Vue 没有提供 `model` API,但提供了处理 `events` 的 API

为啥没有做呢? -> 因为没有必要哈!

MVC 搞到最后,只有一个 View 了 -> 为啥会这样呢? -> 因为 Vue 的功能太多了

很多前端其实看不出 Vue 的 MVC 是在哪儿的……

4)`model`有个`data`,`vue`也有一个`data`,为何不二合一呢?

data 的设计是:

{
  data: {
    // 数据库 数据
    book: {
      id: null,
      name: '未命名',
      number: 0
    },
    n: 1 // -> UI 数据 (也叫用户数据) -> 服务于 input 元素等
  }
}

本来model.data是数据库数据,而vue.data是 UI 数据 -> 方方说book之外的数据都是 UI 数据

额……我在想,不管是book还是n都是 UI 数据吧!只是前者的数据源来自于数据库,而后者是来自于用户的!而它们都被渲染到 UI 中……所以说它们都是 UI 数据也不过分吧?

5)小结

想起当初第一次接触MVC的时候,看了半个多月都不怎么懂,而当时,方方就说不能理解那就跳过,过几个月之后,你再看就很容易明白为啥要这样干了……事实证明,确实如方方所说的那样……

★单向绑定

讲 React 单向绑定的基本思路,不讲 React API

1)信息点

2)React —— 单向绑定

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

例子8: https://jsbin.com/fodasut/3/edit?html,js,output

简化版demo ->只更新UI,没有保存到数据库 -> 关于 React 代码的写法,我不知道是不是约定大于配置,如这样onChange={e => this.handleChange(e)},需要用个箭头函数包裹一下,原来是onChange={this.changeN}这样的,还有这个方法名得这样叫handleChange,即得加个handle前缀,还有该book旗下某个数据时需要用到Object.assign()(整体改……),它会比较数据哪个变化了,然后局部更新虚拟DOM……我之前还以为是这样做的 this.setState({book:{number: this.state.n + this.state.book.number}}),因为这样才会让人感到有局部更新的效果 -> 总体上看,React 写法的代码,不如 Vue 来得简洁……但写的时候思路是很顺畅的,阅读性也是极其的好……

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

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

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

3)单向绑定的要点

  1. 单向
  2. VirtualDOM

Q:请说一下你对单向绑定的理解?

举栗子说明,如:

  1. 上边那个例子代码,纯单向数据流的例子,这种姿势回答很朴素,如果是面对面的面试,可以当着面试官的面来解释这个代码,而这样做是最好的,但很多情况下,这个条件达不到哈!
  2. 直接使用一个单向数据流的库来回答这个问题

选择2:

当面试官问你「你是如何理解单向数据流的?」

第一句话就说:redux的单向数据流是怎么做的?

找一张图 -> 需要理解很多redux的理念

redux

搞清楚MVC之后,正确的代入到图中的饺子(框框的内容)

redux

Q:面试官问你,请说一下你是如何理解虚拟DOM的?

虚拟DOM就是对DOM的底层封装,我们不用直接去操作DOM,只要告诉虚拟DOM最新的状态是什么,那么虚拟DOM自己就会去更新DOM的一部分……

虚拟DOM最大的优点就是:它可以根据两次数据变化去局部更新

了解:如何理解虚拟DOM? - 知乎

4)双向绑定的要点

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

Q:「双向绑定的实现方式?」

双向绑定

原因应该是我们怎么知道data.name = 'Jack'这段代码是去改 input 呢?

有三种姿势:

1、Dirty Checking(AngularJS 1.x) -> 就是 回调 哈!

AngularJS 1.x 的姿势是「在每次代码变动的时候,给你封装一个东西」

如你第一次初始化:

{ 
  data: {
    name: 'xxx'
  }
}

angular就会去调用render()

那如果我第二次修改data呢?如使用一个setTimeoutxxs后 data.name = 'Jack'

但angular并不能发现你是在更改input呀!

于是angular想了这么一个办法,不要用setTimeout,而是用我封装好的$setTimeout(angular提供的一个函数)

$setTimeout的功能跟setTimeout的功能完全一样,可既然完全一样,为啥还要写呢? -> 因为它会让data.name = 'Jack'执行完之后,调用render

那么render它会做什么呢? -> 它会看你的数据与上一次数据有什么不同?

简单来说,angular1.x 能在所有能 操作data的话, 都封装一个函数哈!

同样,我们发送Ajax请求也得用angular提供的API,该API会在数据响应回来之后调用render

总之,angular的思路是:

在你可能改代码的地方儿都给你提供一个特殊的函数,而这个特殊的函数,会在你代码执行完之后去调用render方法,而render方法则会看一下旧的数据是怎样的,然后新的数据又是怎样的 -> 对比一下 -> 对比之后,局部更新UI

可以看到angular是很奇怪的,居然把一些API给重写了,而 Vue 显然就没有这么干!

文档:AngularJS: Tutorial: 7 - XHR & Dependency Injection

$http

如果你悄悄地用原生的ajax方法,把self.phone给改了,那么angular是完全不知道self.phone这个数据是变化了的

所以 Dirty Checking 的意思(思路)是,把你所有改数据的入口,都加一个守卫,只要经过了这个入口,它就知道等会你出来的时候,我就要去看一下数据变没变……

网上很多文章都会告诉你什么是脏数据检查,但却没有告诉你什么时候做脏数据检查! -> 把你要操作数据的API,前边加个$,重新写了一遍……

2、Reactive

Vue 咩有把 ajax 进行封装,用的是第三方库 axios 做的 get、post ,那么为啥 Vue 不去做脏数据检查呢?

因为 Vue 用的是 getter、setter 来监听属性 呀!

如何知道在 JS 里边改了 data.name

get & set

注意,Vue会对data旗下所有的数据改造成getter&setter,之后为data旗下追加属性的话,你得通过Vue.set(object, propertyName, value)来才会有数据响应式的效果,不然你直接data.h = '6'这样是没有响应式效果的,即不会与页面上用到的数据同步哈!

总之,Vue会给data旗下所有的属性加个拦截器(监听器)之类的……

相较于angular1.x的,Vue 用到的getter、setter姿势显然要聪明很多,毕竟angular1.x必须要给所有的入口安插一个守卫才行,如把http变成$http,把setTimeout变成$setTimeout……才有可能实现检查到数据的变化

当然,Vue也有缺点,那就是无法监听后来追加的属性……即无法监听一开始不存在的属性 或者 被我们删除的属性

3、最终方案 `proxy`

proxy是做双向绑定的最终方案!proxy可以做到不管数据一开始存不存在,只要你设置了就会有响应式去更新UI!

Vue 3.0 计划用 proxy 来重写 数据响应式

proxy

可以看到dataProxy全权代理对data的操作!不管存在的,还是之后添加了才存在的,都可以感知到!

★测试

1)填空题

请填空:

class X{
    __N行代码__
}

var view = new X({
    data: {
        name: 'frank'
    }
})

console.log(view.name === 'frank') // 输出 true
view.name = 'jack' // 输出「有人修改了 name」

请填写 N 行代码,使得代码按照注释那样输出结果(有额外的输出不扣分)

提示:Object.defineProperty 或者 Proxy 都行

2)请将课堂中的代码都敲一遍,运行看看,想想每次修改代码之后,代码哪里变好了?

全靠自觉!

★了解更多

➹:Redux 基础概念 · 从零开始学 ReactJS(ReactJS 101)

➹:译 & Web 应用程序中的数据和 UI 分离 - 掘金

➹:在 2016 年学 JavaScript 是一种什么样的体验? - 知乎

★总结

★Q&A

1)MVP是什么?

编程里边的 理解:

来买宝贝的(View) -> 主持人(Presenter) -> 宝贝(Model),宝贝谁买了,就在谁身上,就像是哪个视图要了数据,就在谁身上渲染一样……(Model和View是不能接触的,说白了View 与 Model 不发生联系)

MVP

MVP, MVVM甚至MXXX都只是MVC的变体而已,至于将重点放在C点还是V点(没人会希望放到M点吧?!)就自然引出了若干种所谓的模式,其实模式只有一种,你称为P也好VM也好,它都只是C的实例而已。总是设法弄出一些所谓的Business Word的家伙们,是前端开发最大的敌人!

我个人理解的MVC是: V -> C -> M -> V (V接收用户指令,数据通信都是单向的)

MVC

{
  add() {
    // 业务逻辑
    let newData = {
      number: this.model.data.number + 1
    }
    // 要求Model改变状态
    this.updateModel(newData)
  },
  updateModel(newData) {
    // 发送Ajax请求,把响应回来的数据,保存到 Model 里边
    this.model.update(newData).then(() => {
      // 把 新数据 发送给 View 去 Render
      this.view.render(this.model.data)
    })
  }
}

View 传送指令到 Controller -> Controller 完成业务逻辑后,要求 Model 改变状态 -> Model 将新的数据发送到 View,用户得到反馈

还有一种是直接通过controller接受指令(改变 URL 触发 hashChange 事件):

MVC2

总之:

对了,Backbone.js 是变体的MVC,其 Controller 非常薄,只起到路由的作用,而 View 非常厚,业务逻辑都部署在 View,即由 View 直接要求 Model 改变状态,所以,Backbone 索性取消了 Controller,只保留一个 Router(路由器)( Backbone.js 有 Router 的概念,毫无疑问,它是借鉴了后端的 Web MVC 概念……而像 Vue 这样的,借鉴了经典 MVC,以及 Angular 的双向绑定,还有 React 的 虚拟DOM……)

Backbone.js

Backbone这种MVC,目前不怎么用了,目前大多用 MVVM、MVP 这样的,即 M 与 V 是不直接通信的

MVP & MVVM

在产品设计里边:

a minimum viable product (MVP) is the version of a new product that allows a team to collect the minimum amount of validated learning about customers with the least effort.

即 MVP 是最小可行性产品,如果该产品对用户ok,那么就会继续迭代下去,说白了,先用最低的成本试水,如果市场反应好,那继续迭代下去:

MVP

我觉得很多培训机构就是这样的,课程不断迭代……而老学员两眼泪汪汪……

其它示例:

MVP

MVP

➹:Running hypothesis driven experiments using the MVP - UX Collective

➹:Using The MVP Approach To Create A Successful Project

➹:MVC、MVP、MVVM,我到底该怎么选? - 掘金

➹:goldze/MVVMHabit: 👕基于谷歌最新AAC架构,MVVM设计模式的一套快速开发库,整合Okhttp+RxJava+Retrofit+Glide等主流模块,满足日常开发需求。使用该框架可以快速开发一个高质量、易维护的Android应用。

➹:Everything You Need to Know for Building MVP for Startups

➹:MVC,MVP 和 MVVM 的图示 - 阮一峰的网络日志

2)单向绑定与双向绑定

为啥 React 不实现双向绑定呢?

不实现双向绑定是React设计决定,因为React的目标从来不是“让开发者写更少的代码”,而是让“代码结构更加清晰易于维护”。

有的库采用了双向绑定,利于快速上手,但是不得不承受对应的缺点,软件上十全十美的方案几乎没有,就看如何均衡,就看对于特定问题如何取舍。

最好是在业务数据上单向数据流,但是在功能组件开发上允许双向绑定,这样综合效用最高

react对这个问题的处理方法,很朴素,却暗合了中间件模式,让上层程序更容易理解

我突然明白 ant-design-vue 为啥没有用 v-model 了,而是用了脱糖的写法……

➹:单向数据绑定和双向数据绑定的优缺点,适合什么场景? - 知乎

➹:react不实现双向绑定的原因是什么呢,提高用户动手能力? - 知乎

➹:Vue的双向绑定和单向数据流 - 掘金

3)脏数据?

➹:今天想跟大家分享下 double check in 和 dirty check in - 知乎

➹:VueJs到底从Angular1.x抄了什么? - 知乎

➹:Angular的脏检测跟Vue的数据劫持相比有什么优势和劣势? - 知乎