组件化和 MVVM
组件化:
很早以前就有 组件化 的概念,比如 asp, jsp, php。node也有类似的组件化,但是传统组件化,只是做分模块做静态渲染,更新数据时需要操作 DOM 节点,也就是 jQuery 比较流行时,vue 和 react 在 此基础上增加了创新,即为 数据驱动视图,通过 MVVM 原理实现。
数据驱动视图:
view:代表 template 模板中的内容
model: 代表 vue中的data,react中的 state。
viewModel: 泛指view和model的连接层,通过监听事件和指令修改model中的数据
响应式原理
通过核心api Object.defineproperty 监听属性变化
通过 Vue.set 和 Vue.delete 监听对象属性的增加和删除
通过 重写数组原型 监听数组
- updateView - 更新视图
- 重写数组原型
const oldArrayProperty = Array.prototype;
const arrProto = Object.create(oldArrayProperty);
[...数组原型的基础方法].forEach(methodName => {
arrProto[methodName] = function() {
updateView() // 更新视图
oldArrayProperty[methodName].call(this, ...arguments)
}
})
- 核心API -> 深度监听 -> 核心API 监听
function defineReactive(target, key value){
observe(value) // 多层对象深度监听,一次性递归到底
Object.defineProperty(target, key {
get(){
return value
},
set(newValue){
if(newValue !== value){
observe(newValue) // 对象类型监听,如果从 直接变量 修改为 对象则需要深度监听
value = newValue
updateView() // 更新视图
}
}
})
}
- 递归深度监听
function observe(target){
if(typeof target !== 'object' || target === null) return target;
if(Aray.isArray{target}){
target.__proto__ = arrProto
}
for(let key in target){
defineReactive(target, key , target[key])
}
}
- model
const data = {
name: '张三'
}
// 开启响应式监听
observe(data)
vdom 和 diff 算法
vdom
总所周知,DOM 操作比较费时,一般都是通过jQuery操作DOM,但 vue 和 react 都是数据驱动视图,有效的通过 vdom 优化DOM 操作;
所以vdom可以说是 用 JS 模拟 dom 结构,计算出最小的变更,再去操作 DOM
diff 算法
首先 vue 把树 diff 的时间复杂度O(n^3) , 优化到 O(n);
根据统一层级对比,不做跨级比较;
elem 不相同,则删掉重建,不再做深度比较;
elem 和 key 两者都相同,则认为是相同节点,通过 patchVnode 做比较
patchVnode 通过对比 oldVnnode 和 newVnode做比较,对应判断是应该做 add 还是 remove操作
最终节点和key都相同并且都有chillren 的节点 通过 update Children 做对比,最终返回最新的vnode渲染到 DOM 节点中。
模板编译
了解模板编译之前需要先了解一下开发中不常用到的 with 语法;在此不赘述。
通过 vue-template-complier 将模板解析为 render 函数,render 通过 with 语法返回一个 vnode 节点
组件渲染和更新过程
渲染-解析 template 模板为render 函数,触发监听 model 的 getter 和 setter;
执行render 函数,生成 vnode 节点。通过 patch 函数,挂在到 对应的包裹容器中
更行-当 model 中值被改变的时候,会触发 getter 中 已经观察者模式中的数据,触发 setter。
重新执行 render 函数,生成 newVnode ,通过 patch 函数中的oldVnode,newVnode 对比(diff),得出最小差值的新节点后更新视图
异步渲染
首先 $nextTick 是在新增或者删除完节点之后立即可以获得元素的真实内容和节点长度时使用。
多次 setData 的时候会汇总 data 设置,一次性更新视图,减少 DOM 操作次数,提高性能。