# Vue
# Vue 响应式原理
Vue 双向数据绑定主要是指:视图变化更新数据,数据变化更新视图。如图所示:
即:
- 输入框内容变化时,Data 中数据同时变化
- Data 中数据变化时,文本节点内容同时变化
其中 View 更新 Data 可能通过事件监听的方式实现,所以 Vue 的双向数据绑定工作主要是如何根据 Data 变化更新 View。
Vue 主要通过以下 4 个步骤来实现数据双向绑定的:
实现一个监听器 Observer:对数据对象进行深度遍历,利用 Object.defineProperty() 给属性都加上 getter 和 setter,来监听数据变化。
实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。
实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和订阅者 Watcher 进行统一管理。
# 派发更新
dep.notify()
会遍历 Watcher 触发watcher.update()
,update()
添加到更新队列中,在下一次$nextTick
时执行更新操作。
# nextTick
数据的变化到 DOM 的重新渲染是一个异步过程,发生在下一个 tick。这就是我们平时在开发的过程中,比如从服务端接口去获取数据的时候,数据做了修改,如果我们的某些方法去依赖了数据修改后的 DOM 变化,我们就必须在 nextTick
后执行。比如下面的伪代码:
getData(res).then(() => {
this.xxx = res.data
this.$nextTick(() => {
// 这里我们可以获取变化后的 DOM
})
})
Vue.js 提供了 2 种调用 nextTick
的方式,一种是全局 API Vue.nextTick
,一种是实例上的方法 vm.$nextTick
,无论我们使用哪一种,最后都是调用 next-tick.js
中实现的 nextTick
方法。
# Object.defineProperty()有什么缺点?Vue2 中是如何 hack 的?
proxy 相比 defineProperty()优势:
- 数组变化也能监听到
- 不需要深度遍历监听
let data = { a: 1 }
let reactiveData = new Proxy(data, {
get: function (target, name) {
// ...
},
// ...
})
# key 的作用
- 让 vue 精准的追踪到每一个元素,高效的更新虚拟 DOM。
- 触发过渡
<transition>
<span :key="text">{{ text }}</span>
</transition>
当 text 改变时,这个元素的 key 属性就发生了改变,在渲染更新时,Vue 会认为这里新产生了一个元素,而老的元素由于 key 不存在了,所以会被删除,从而触发了过渡。妙啊!
# scoped 样式穿透
scoped 虽然避免了组件间样式污染,但是很多时候我们需要修改组件中的某个样式,但是又不想去除 scoped 属性。
- 使用/deep/
//Parent
<template>
<div class="wrap">
<Child />
</div>
</template>
<style lang="scss" scoped>
.wrap /deep/ .box {
background: red;
}
</style>
//Child
<template>
<div class="box"></div>
</template>
- 使用两个 style 标签
//Parent
<template>
<div class="wrap">
<Child />
</div>
</template>
<style lang="scss" scoped>
//其他样式
</style>
<style lang="scss">
.wrap .box {
background: red;
}
</style>
//Child
<template>
<div class="box"></div>
</template>
# vue-router
- mode
hash
history
- 跳转
this.$router.push()
<router-link to=""></router-link>
- 占位
<router-view></router-view>
# vuex
state
: 状态中心mutations
: 更改状态actions
: 异步更改状态getters
: 获取状态modules
: 将state
分成多个modules
,便于管理