1、Vue的优势
1)轻量级框架(压索之后20KB大小)
2)组件化开发:vue是单页面应用,使页面局部刷新,不用每次跳转页面都要请求所有数据和dom;提升访问速度和用户体验。
3)组件在系统内部可复用,组件和组件之间可以嵌套,如果项目比较复杂,可以极大简化代码量,对后期的需求变更和维护也更加友好。
4)Virtual DOM他就是一种可以预先通过JavaScript进行各种计算,把最终的DOM操作计算出来并优化,由于这个DOM操作属于预处理操作,并没有真实的操作DOM,所以叫做虚拟DOM。最后在计算完毕才真正将DOM操作提交,将DOM操作变化反映到DOM树上。
2、Vue的缺点
1)单页面应用,不利于SEO优化
2)所有的资源文件(eg:css,js,img)统一放在一个文件中,一旦文件非常大时,页面加载速度会变得非常慢,首屏加载时间慢
3)不支持低版本浏览器(IE8),因IE8不支持EM5特性,而Object.defineProperty 是ES5 特性;
什么是响应式
数据发生变化后,会重新对页面渲染,这就是Vue响应式。
vue实现数据响应式,是通过数据劫持侦测数据变化,发布订阅模式进行依赖收集与视图更新,换句话说是Observe,Watcher以及Compile三者相互配合
- Observe实现数据劫持,递归给对象属性,绑定setter和getter函数,属性改变时,通知订阅者
- Compile解析模板,把模板中变量换成数据,绑定更新函数,添加订阅者,收到通知就执行更新函数
- Watcher作为Observe和Compile中间的桥梁,订阅Observe属性变化的消息,触发Compile更新函数
如何侦测数据的变化:
在javascript中,使用 Object.defineProperty和ES6的 Proxy,这就是进行数据劫持或数据代理。
1.Object.defineProperty
Vue通过设定对象属性的 setter/getter 方法来监听数据的变化,通过getter进行依赖收集,而每个setter方法就是一个观察者,在数据变更的时候通知订阅者更新视图。
如下图:
var data = { name: 'yck' }
observe(data)
let name = data.name // -> get value
data.name = 'yyy' // -> change value
function observe(obj) {
// 判断类型
if (!obj || typeof obj !== 'object') {
return
}
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
function defineReactive(obj, key, val) {
// 递归子属性
observe(val)
let dp = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
console.log('get value')
// 将 Watcher 添加到订阅
if (Dep.target) {
dp.addSub(Dep.target)
}
return val
},
set: function reactiveSetter(newVal) {
console.log('change value')
val = newVal
// 执行 watcher 的 update 方法
dp.notify()
}
})
}
Object.defineProperty 的缺陷:如果通过下标方式修改数组数据或者给对象新增属性并不会触发组件的重新渲染,因为 Object.defineProperty 不能拦截到这些操作,更精确的来说,对于数组而言,大部分操作都是拦截不到的,只是 Vue 内部通过重写函数的方式解决了这个问题。
方法2.Proxy
Proxy 的代理是针对整个对象的,而不是对象的某个属性,因此不同于 Object.defineProperty 的必须遍历对象每个属性, Proxy 只需要做一层代理就可以监听同级结构下的所有属性变化,当然对于深层结构,递归还是需要进行的。此外 Proxy支持代理数组的变化。
什么上依赖即一个 Watcher 类来表示观察订阅依赖。
为什么要收集依赖?
当属性发生变化后,我们要通知用到数据的地方,而使用这个数据的地方有很多,而且类型还不一样,既有可能是模板,也有可能是用户写的一个watch,这时需要抽象出一个能集中处理这些情况的类。如下图:
// 通过 Dep 解耦属性的依赖和更新操作
// Dep它用来收集依赖、删除依赖和向依赖发送消息等
//存放Watcher 观察者对象
class Dep {
constructor() {
this.subs = []
}
// 添加依赖
addSub(sub) {
this.subs.push(sub)
}
// 更新
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
}
class Watcher
{
constructor(obj, key, cb) {
// 将 Dep.target 指向自己
// 然后触发属性的 getter 添加监听
// 最后将 Dep.target 置空
Dep.target = this
this.cb = cb
this.obj = obj
this.key = key
this.value = obj[key]
Dep.target = null
}
update() {
// 获得新值
this.value = this.obj[this.key]
// 调用 update 方法更新 Dom
this.cb(this.value)
}
}
var data = { name: 'yck' }
observe(data)
function update(value) {
document.querySelector('div').innerText = value
}
// 模拟解析到 `{{name}}` 触发的操作
new Watcher(data, 'name', update)
// update Dom innerText
data.name = 'yyy'
前端研发群:
永久有效,有需要一起学习的小伙伴,可以加一下jjljzh。