| ✍️ Tangxt | ⏳ 2020-07-20 | 🏷️ MVC、单向绑定 |
input 框输入数据,更改内存里边 data 旗下的 n 值n ,改变页面视图里 input 框里边的值! -> 手段 Object.defineProperty
Vue 做的双向绑定是借鉴 Angular 的,而 Angular 则是借鉴 C#的 WPF(最早把双向绑定投入到大规模的使用中)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
哪个数据变了,就改哪里,而不是整个视图不管数据变没变,都改一遍……
总之,这不是无差别更新……而是有差别攻击……
为啥没有做呢? -> 因为没有必要哈!
MVC 搞到最后,只有一个 View 了 -> 为啥会这样呢? -> 因为 Vue 的功能太多了
很多前端其实看不出 Vue 的 MVC 是在哪儿的……
data 的设计是:
{
data: {
// 数据库 数据
book: {
id: null,
name: '未命名',
number: 0
},
n: 1 // -> UI 数据 (也叫用户数据) -> 服务于 input 元素等
}
}
本来
model.data是数据库数据,而vue.data是 UI 数据 -> 方方说book之外的数据都是 UI 数据
额……我在想,不管是book还是n都是 UI 数据吧!只是前者的数据源来自于数据库,而后者是来自于用户的!而它们都被渲染到 UI 中……所以说它们都是 UI 数据也不过分吧?
controller 弄到 methods 里边去了,还混到模板 HTML 里边去了model.data 给干掉,交给 Vue 的 data 来存!Vue(代码组织形式一次比一次好,从例1到例7可以看出,注意,知道每次变化是为什么,是很重要的,对了,这是一年的前端变化哦!方方用一节课的时间就介绍完了,所以很难理解也是很正常的!) -> MVC 中的 V 做了那么多的事情,其实这是有点反 MVC 的,但现在的前端好像也不太在意哈! -> 反正用了 Vue 之后,这代码组织结构也是极其的好,阅读性也是极其的好……book,而像n这样的则是 UI 数据,也叫临时数据!这与之前的理解是矛盾的……想起当初第一次接触MVC的时候,看了半个多月都不怎么懂,而当时,方方就说不能理解那就跳过,过几个月之后,你再看就很容易明白为啥要这样干了……事实证明,确实如方方所说的那样……
讲 React 单向绑定的基本思路,不讲 React API
(),不然就会直接返回一个undefined -> 模板语法是{}MVC -> M Vue C -> M Vue -> Vue(对新手来说,是可以接收的,因为这是一点一点过来的哈!)data:{user:{name:'xxx',email:'1@xx.com'}} -> 渲染用户名)、组件B(data:{user:{name:'xxx',email:'1@xx.com'}} -> 渲染用户邮箱) -> 双向绑定,B输入新邮箱地址,而A的email还是旧的,同样,name也是如此 -> 为啥不把它们合并成一个组件呢? -> 假如还要引入很多个组件,那么这综合起来的组件,其data就很大了!而这就很不模块化了……
F,把A和B的data给删咯!在F里创建一个data:{user:{name:'xxx',email:'1@xx.com'}} -> 每次用户往A的input里边输入name,那么就会通知F组件更新其data.user.name,然后再把最新的name数据交给A去渲染视图……this.state = {book:{},n:1} -> render()this.setState({book:response.data})(智能的,只会处理那个变化了的状态) -> render()state -> render() -> view(input框里的默认值1) -> 用户往input框了输入内容(如12) -> 触发change()事件 -> update state -> render() -> view(input框里的值12)change事件等,是无法输入内容的,因为input里的值是来自于state的,你不改变state的值,那么就不会重新渲染,总之,state是多少,那么render出来的view上的数据就是多少……有种state是与view绑定的感觉,但这种绑定是单向的…… -> 说白了,React的视图是只读的,要更新视图,需要通过一个事件来搞 -> 智能的局部更新,即只改某个state变了的的那部分
v-model给人的感觉才是实现了双向绑定……因为v-model是语法糖呀!它的本质也是监听了视图里的数据变化,然后修改data旗下的n值,然后再去render的!而 React 则是直接的、存粹的、无糖的…… -> 对了,Vue的跨父子组件的双向绑定可以用.sync来搞(数据是prop,本质也是事件监听……)……单向绑定:半自动的双向绑定;双向绑定:全自动的双向绑定!

this.add.bind(this),为啥总是要加bind呢? -> 一开始React其实不用让你加bind(this)的,而是自动加的,然而一些前端用了一段时间之后,就反对这种方式了,这理由跟我们不使用双向绑定差不多,即这样自动的话,有时会显得很智障,还不如让我们全部自己写呢?请不要画蛇添足好吧!总之,反对思路是「全自动不如我半自动,而且全自动还有bug呢!」所以,React在下一个版本就放弃加bind了,都让开发者自己加bind,那么何时加bind?函数不需要this,那就不加bind呗!但正常情况下,一般都会加上的! -> 还有this.add也没有省略这个this,虽然代码理解起来简单了,但似乎又有点啰嗦……n为1显示,再点击,n为2隐藏)反正这些数据不会影响到其它组件!双向绑定看起来很 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 来得简洁……但写的时候思路是很顺畅的,阅读性也是极其的好……
我们来想想为什么双向绑定不好呢?
因为「双向绑定」有一点点反「组件化」:跨组件的双向绑定很奇怪。
但是局部使用双向绑定是非常爽的。
Q:请说一下你对单向绑定的理解?
举栗子说明,如:
选择2:
当面试官问你「你是如何理解单向数据流的?」
第一句话就说:redux的单向数据流是怎么做的?
找一张图 -> 需要理解很多redux的理念

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

Q:面试官问你,请说一下你是如何理解虚拟DOM的?
虚拟DOM就是对DOM的底层封装,我们不用直接去操作DOM,只要告诉虚拟DOM最新的状态是什么,那么虚拟DOM自己就会去更新DOM的一部分……
虚拟DOM最大的优点就是:它可以根据两次数据变化去局部更新
Q:「双向绑定的实现方式?」

原因应该是我们怎么知道
data.name = 'Jack'这段代码是去改input呢?
有三种姿势:
AngularJS 1.x 的姿势是「在每次代码变动的时候,给你封装一个东西」
如你第一次初始化:
{
data: {
name: 'xxx'
}
}
angular就会去调用render()
那如果我第二次修改data呢?如使用一个setTimeout,xxs后 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

如果你悄悄地用原生的ajax方法,把self.phone给改了,那么angular是完全不知道self.phone这个数据是变化了的
所以 Dirty Checking 的意思(思路)是,把你所有改数据的入口,都加一个守卫,只要经过了这个入口,它就知道等会你出来的时候,我就要去看一下数据变没变……
网上很多文章都会告诉你什么是脏数据检查,但却没有告诉你什么时候做脏数据检查! -> 把你要操作数据的API,前边加个$,重新写了一遍……
Vue 咩有把 ajax 进行封装,用的是第三方库 axios 做的 get、post ,那么为啥 Vue 不去做脏数据检查呢?
因为 Vue 用的是 getter、setter 来监听属性 呀!
如何知道在 JS 里边改了 data.name?

注意,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也有缺点,那就是无法监听后来追加的属性……即无法监听一开始不存在的属性 或者 被我们删除的属性
proxy是做双向绑定的最终方案!proxy可以做到不管数据一开始存不存在,只要你设置了就会有响应式去更新UI!
Vue 3.0 计划用
proxy来重写 数据响应式

可以看到dataProxy全权代理对data的操作!不管存在的,还是之后添加了才存在的,都可以感知到!
请填空:
class X{
__N行代码__
}
var view = new X({
data: {
name: 'frank'
}
})
console.log(view.name === 'frank') // 输出 true
view.name = 'jack' // 输出「有人修改了 name」
请填写 N 行代码,使得代码按照注释那样输出结果(有额外的输出不扣分)
提示:
Object.defineProperty或者Proxy都行
全靠自觉!
➹:Redux 基础概念 · 从零开始学 ReactJS(ReactJS 101)
➹:译 & Web 应用程序中的数据和 UI 分离 - 掘金
➹:在 2016 年学 JavaScript 是一种什么样的体验? - 知乎
编程里边的 理解:
来买宝贝的(View) -> 主持人(Presenter) -> 宝贝(Model),宝贝谁买了,就在谁身上,就像是哪个视图要了数据,就在谁身上渲染一样……(Model和View是不能接触的,说白了View 与 Model 不发生联系)

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

{
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 事件):

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

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

在产品设计里边:
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,那么就会继续迭代下去,说白了,先用最低的成本试水,如果市场反应好,那继续迭代下去:

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


➹:Running hypothesis driven experiments using the MVP - UX Collective
➹:Using The MVP Approach To Create A Successful Project
➹:Everything You Need to Know for Building MVP for Startups
➹:MVC,MVP 和 MVVM 的图示 - 阮一峰的网络日志
为啥 React 不实现双向绑定呢?
不实现双向绑定是React设计决定,因为React的目标从来不是“让开发者写更少的代码”,而是让“代码结构更加清晰易于维护”。
有的库采用了双向绑定,利于快速上手,但是不得不承受对应的缺点,软件上十全十美的方案几乎没有,就看如何均衡,就看对于特定问题如何取舍。
最好是在业务数据上单向数据流,但是在功能组件开发上允许双向绑定,这样综合效用最高
react对这个问题的处理方法,很朴素,却暗合了中间件模式,让上层程序更容易理解
我突然明白 ant-design-vue 为啥没有用 v-model 了,而是用了脱糖的写法……
➹:单向数据绑定和双向数据绑定的优缺点,适合什么场景? - 知乎
➹:react不实现双向绑定的原因是什么呢,提高用户动手能力? - 知乎
➹:今天想跟大家分享下 double check in 和 dirty check in - 知乎