VUE

对 Vue 的理解

  • 不完全的MVVM
  • 双向数据绑定 Object.defineProperties + 发布订阅模式
  • 优点
    • 低耦合
    • 可重用性
    • 可测试

什么是 MVVM

1
MVVM Model-View-ViewModel 是一种设计思想,可以在Model 中定义数据修改 和 操作的业务逻辑;View 代表 UI组件,他负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象。
Copied!
  • 在 MVVM 架构下,View 和 Model 之间并没有直接的联系,而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互是双向的,因此 View 数据的变化会同步到 Model 中,而 Model 数据也会立即反应到 View 上。
  • ViewModel 通过双向数据绑定把 View 层 和 Model 层链接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,负责的数据状态维护完全由 MVVM 来统一管理

Vue 的优点是什么

  • 低耦合
  • 可重用性
  • 可测试

Vue 生命周期

  • 创建前后:beforeCreate 阶段,vue实例的挂载元素 el 和 数据对象 data 都为 undefined,还未初始化,在 created 阶段,vue实例的数据对象 data 有了,el (DOM节点对象) 还没有
  • 载入前后:在 beforeMount 阶段,vue 实例的 $el 和 data 都初始化来,但还是挂载之前为虚拟的dom节点,data.message 还未替换。在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染
  • 更新前后
  • 销毁前后
  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroy

Vue3.0 有那些新特性

Vue 2 组件通讯方式

  • VUEX
  • Event Hub
  • $emit / Props
  • provide / inject: 父组件中通过provide来提供变量, 然后再子组件中通过inject来注入变量。
  • ref
  • localStorage / SessionStorage
  • $attrs / $listeners
  • 父子组件通信: props; $parent / $children; provide / inject ; ref ; $attrs / $listeners
  • 兄弟组件通信: eventBus ; vuex
  • 跨级通信: eventBus;Vuex;provide / inject 、$attrs / $listeners
更多

Vue 双向绑定的实现原理

在 Vue2.0 中通过 Object.defineProperty 实现对象的 get 和 set 方法实现数据劫持。结合 发布 / 订阅者模式 实现。但是这种实现方式无法完成对数组的监听,Vue2.0中重写了数组的 push pop shift 等方法实现对数组改变的监听。而Vue3中通过使用 ES6 中的 Proxy,实现对所有数据类型数据变化的监听。
1
var Book = {}
2
var name = '';
3
Object.defineProperty(Book, 'name', {
4
set: function (value) {
5
name = value;
6
console.log('你取了一个书名叫做' + value);
7
},
8
get: function () {
9
return '《' + name + '》'
10
}
11
})
12
13
Book.name = 'vue权威指南'; // 你取了一个书名叫做vue权威指南
14
console.log(Book.name); // 《vue权威指南》
Copied!
  • 监听器 Observer:劫持并监听所有属性,如果有变动,通知订阅者
  • 订阅者 Watcher: 可以收到属性的变化通知,并执行相应的函数,从而更新视图
  • 解析器 Compile :可以扫描和解析每个节点的相关指令,并根据初始化模版数据以及初始化相应的订阅器

实现双向数据绑定

  • 在页面元素 button 中绑定 {{count}}
  • 在编译过程是,针对这个 button 会产生一个 Watcher(vm, exp, cb(newValue,oldValue)),vm 是 Vue 对象,exp 是数据绑定的数据;cb 的逻辑是用来更新页面
  • 实现发布订阅模式,Watcher初始化的时候会将 Dep.target 设置为this,也就是watcher自己,同时会触发 count 的 getter 方法,getter里面会调用 Dep 的 depend 方法,depend 方法会调用 Watcher 的 addDep 方法,addDep 方法就是将 Watcher 自己存放在 Dep 的事件池里面。

双向绑定 Proxy 比 definedProperty

实现双向绑定的方法:

  • KnockoutJS 观察者模式的双向绑定
  • Ember 基于数据模型的双向绑定
  • Angular 基于脏检查的双向绑定
  • Vue Object.definePropertiesProxy

数据劫持实现双向数据绑定

``jsx harmony // 遍历对象,对其属性值进行劫持 Object.keys(data).forEach(function(key) { Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function() { console.log('get'); }, set: function(newVal) { // 当属性值发生变化时我们可以进行额外操作 console.log(大家好,我系${newVal}`); say(newVal); }, }); });
1
优势
2
3
1. 无需显示调用
4
2. 可精确得知变化数据
5
6
##### 实现思路
7
8
1、 利用 `Proxy` 和 `Object.defineProperty` 生成的 Observer 针对对象/对象属性进行劫持,在属性发生变化后通知订阅者
9
2、 解析器 `Compile` 解析模版中的 `Directive` 指令,收集指令所依赖的方法和数据,等待数据变化然后进行渲染
10
3、 `Watcher` 属于 `Observer` 和 `Compile` 的桥梁,他将接收到的 Observer 产生的数据变化,并根据 Compile 提供的指令进行视图渲染,使得数据变化促使视图变化
11
12
##### definedProperty 的缺陷
13
14
- 无法实现对数组修改的监听,Vue2 重写了 数组的 push、pop、shift、unshift、splice、sort、reverse 方法,但是 `vm.items[index] = newValue` 这种赋值的修改是无法监测的。Proxy 可以
15
- 只能劫持对象的单个属性,那么我们需要对对象的每个属性进行遍历,如果属性值也是对象则需要深度遍历。
16
17
##### Proxy 的 特点
18
19
Proxy 在ES6 中被提出,他在目标对象之间架设了一层`拦截`,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
20
21
- Proxy 可以直接监听对象而非属性
22
23
```jsx harmony
24
const input = document.getElementById('input');
25
const p = document.getElementById('p');
26
const obj = {};
27
28
const newObj = new Proxy(obj, {
29
get: function(target, key, receiver) {
30
console.log(`getting ${key}!`);
31
return Reflect.get(target, key, receiver);
32
},
33
set: function(target, key, value, receiver) {
34
console.log(target, key, value, receiver);
35
if (key === 'text') {
36
input.value = value;
37
p.innerHTML = value;
38
}
39
return Reflect.set(target, key, value, receiver);
40
},
41
});
42
43
input.addEventListener('keyup', function(e) {
44
newObj.text = e.target.value;
45
});
Copied!
  • Proxy 可以直接监听数组的变化
```jsx harmony let obj = { a: 1, b: 2, c: [1,2,3, {d:1, t:2}] }
let newProxy = new Proxy(obj, { get: (target, key, receiver) => { console.log('get'); return Reflect.get(target, key, receiver) }, set: (target, key, value, receiver) => { console.log(target, key, value, receiver); return Reflect.set(target, key, value, receiver); } })
1
### computed 和 watch 有什么区别
2
3
一个是用于计算一些数据,计算完成后当成一个属性使用,后续其依赖的值改变了之后,才会被修改。而watch则是用于监听数据的改变并做一个任务。
4
5
##### computed
6
7
- 计算属性,更多用于计算值的厂家
8
- 具备缓存性,computed的值是在getter执行后会缓存的,只有它依赖的属性值发生改变后,下次获取computed 的值时才会重新调用对应的getter计算
9
- 适用于计算比较消耗性能的计算场景
10
11
##### watch
12
13
- 更多的是观察的作用,类似数据监听毁掉,用于观察 props $emit 或者本组件的值
14
- 无缓存行,页面重新渲染时也不会执行
15
16
### 如何理解VUE的响应式系统
17
18
- 任何一个Vue组件都会有一个与之对应的 Watcher 实例
19
- Vue 的 data 上的 属性会被添加 getter 和 setter
20
- Vue 组件 render 函数执行时,data 上的属性会被 touch,getter方法会被调用,此时 Vue 会去记录此 Vue component 所依赖的所有的data(这个过程成为依赖收集)。
21
- data 被改动时,setter 方法会被调用,此时 Vue 会去同志所有依赖于此 data 的组件调用他们的 render 函数进行更新。
22
23
### Vue 可以通过数据劫持精准探测数据变化,为什么还需要对 DOM 进行 diff 检测差异
24
25
现代前端框架有两种侦测方法:pull 和 push
26
27
pull代表为 React。在 React 中常调用 setState API 显示更新。然后 React 会进行一层层的 VDOM Diff 操作找出差异。然后 Patch 到 DOM 上,React 一开始不知道哪里发生变化了,只知道有变化了,然后再比较暴力的 Diff 查找哪里发生变化。
28
29
push方式的代表则是 Vue 的响应式系统。当初始化时就会对数据进行依赖收集,一旦数据变化,响应式系统就会立即得知,因此 Vue 是一开始就知道 在哪发生变化的。但是 Vue 绑定一个数据就需要一个 Watcher,一旦我们的绑定颗粒度太高就会产生大量 Watcher,这会带来内存及依赖追踪的开销,而颗粒度过低会无法精准侦测变化,因此 Vue 的设计是选择中等颗粒度的方案。在组件级别进行 push 侦测,然后在组件内部进行 VDOM Diff 获取更加具体的差异。而 VDOM 是 pull 操作,Vue 是 push + pull 结合的方式进行变化侦测的。
30
31
### Vue 中的 key 到底有什么用?
32
33
key 是vue 中 vnode 标记的唯一 id,通过这个key在 diff 操作的时候可以更准确、快速。
34
35
- 准确: 如果不加key,那么vue会选择复用节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug.
36
- 快速: key的唯一性可以被Map数据结构充分利用,相比于遍历查找的时间复杂度O(n),Map的时间复杂度仅仅为O(1).
37
38
### Vue diff 算法
39
40
![](../images/diff.png)
41
42
- [详解vue的diff算法](https://juejin.im/post/5affd01551882542c83301da)
43
44
## VUEX
45
46
### Vuex是什么,我们为什么需要vuex
47
48
Vuex是 Vue 的状态管理库,类似 React 的 Redux,可以给Vue应用提供一个集中式存储的全局状态。
49
50
我们可以简略的看 MVC 和 MVVM 框架,状态在 Model -> Controller -> View -> Model 之间流转。从 Model 到 View 的部分已经被 Vue 之类的框架处理了,我们的状态管理核心要解决的就是从 View 到 Model
51
52
Vuex 的优势在于:
53
54
- 实现跨组件的通讯
55
- 清晰的数据流,方便业务理解和错误排查
56
- 每个实例/组件仍然可以拥有和管理自己的私有状态
57
- 记录变更 (mutation)、保存状态快照、历史回滚/时光旅行的先进的调试工具
58
59
### vuex有哪几种属性?
60
61
有五种,分别是 State、 Getter、Mutation 、Action、 Module
62
63
- state:存储数据,存储状态;在根实例中注册了store 后,用 this.$store.state 来访问;对应vue里面的data;存放数据方式为响应式,vue组件从store中读取数据,如数据发生变化,组件也会对应的更新。
64
- getter:可以认为是 store 的计算属性,它的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
65
- mutation:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
66
- action:可以包含异步操作,通过提交 mutation 间接更变状态。
67
- module:将 store 分割成模块,每个模块都具有state、mutation、action、getter、甚至是嵌套子模块。
68
69
### Vue.js 中 ajax 请求代码应该写在组件的 methods 中还是 vuex 的 actions 中?
70
71
一、如果请求来的数据是不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入vuex 的state里。
72
73
二、如果被其他地方复用,这个很大几率上是需要的,如果需要,请将请求放入action里,方便复用,并包装成promise返回,在调用处用async await处理返回的数据。如果不要复用这个请求,那么直接写在vue文件里很方便。
74
75
### 不用 Vuex 会带来什么问题?
76
77
一、可维护性会下降,你要想修改数据,你得维护三个地方
78
79
二、可读性会下降,因为一个组件里的数据,你根本就看不出来是从哪来的
80
81
三、增加耦合,大量的上传派发,会让耦合性大大的增加,本来Vue用Component就是为了减少耦合,现在这么用,和组件化的初衷相背。
82
83
### 如何通过访问this.$store.getters.a就能返回对应方法执行后的返回值
84
85
```js
86
Object.defineProperty(store.getters, key, {
87
get: function() { return store._vm[key] },
88
enumerable: true
89
})
Copied!
Last modified 1yr ago