# VUE 原理解析
# dom-diff
- dom-diff(进行同层比较)
- 数据更改触发set函数
- 执行对应的 Dep.notify函数
- 通过patch 对应新旧虚拟节点
- 判断两个节点是否一样
- 不一样:用新的虚拟节点整个替换老节点
- 一样:根据子节点情况接着进行比较分四种情况
- 老节点有子节点,新节点没有(删除老节点)
- 老节点没有子节点,新节点有子节点(把新节点的子节点 append 到 老节点中)
- 都有文本节点(以新换旧)
- 都有子节点
- 进入 patchVnode 函数 做的事情
- 新旧虚拟dom若是同一个地址,则无操作
- 找到当前节点对应的真实dom elm 获取老节点的子节点和新节点的子节点
- 判断 新节点的文本节点是否是undefined
- 若是
- 比较新旧节点的子节点是否都存在,都存在但不一样
- 调用 updateChildren 方法更新
- 新节点有子节点 老节点没有
- 调用 addVnodes将新节点中的子节点添加到 elm中
- 新节点没有子节点 老节点有
- 调用 removeVnodes 移除老节点中的子节点
- 老节点的文本存在的话
- 调用setTextContent 把老节点对应的 elm 的文本设置为空
- 比较新旧节点的子节点是否都存在,都存在但不一样
- 若新节点的文本不是空
- 调用 setTextContent 把元素 elm 中的文本设置成新的内容
- 若是
# 数据双向绑定原理
function observe(data) {
// 判断data是否是对象
if (Object.property.toSting.call(data) !== '[object Object]') return;
let keys = Object.keys(data);
keys.forEach((key) => {
definedReactive$$1(data, key, data[key]);
})
}
function definedReactive$$1(target, key, val) {
var dep = new Dep();
Object.defineProperty(target, key, {
enumerable: true,
get() {
if (Dep.target) {
dep.addSub(Dep.target);
}
return val;
},
set(newV) {
if (newV !== val) {
val = newV;
dep.notify();
}
}
})
}
function nodeToFragment(el, vm){
let fragment = document.createDocumentFragment();
let child;
while(child = el.firstChild) {
complie(child, vm);
fragment.appendChild(child);
}
el.appendChild(fragment);
}
function complie(node, vm){
// 编译node 节点类型 元素节点考虑行内属性,文本节点直接替换
if (node.nodeType === 1) {
let attrs = node.attributes;
[...attrs].forEach((item) => {
if (/^v-/.test(item.nodeName)) {
let vName = item.nodeValue;
let val = vm.$data[vName];
new Watcher(node, vName, vm);
node.value = val;
node.addEvemtListener('input', (e)=>{
vm.$data[vName] = e.target.value;
})
}
})
[...node.childNodes].forEach((item) => {
// 递归编译子节点
complie(item, vm);
})
} else {
let str = node.textContent;
if (/\{\{(\w+)\}\}/.test(str)) {
str = str.replace(/\{\{(\w+)\}\}/, (a,b) => {
new Watcher(node, b, vm);
return vm.$data[b];
});
node.textContent = str;
}
}
}
class Watcher {
constructor(node, key, vm){
Dep.target = this; // this就是 Watcher实例
this.node = node;
this.key = key;
this.vm = vm;
this.getValue(); // 把当前的Watcher放在事件池里
Dep.target = null;
}
getValue(){
this.value = this.vm.$data[this.key]
}
update(){ // 负责更新dom
this.getValue();
if (this.node.nodeType === 1) {
this.node.value = this.value;
} else {
this.node.textContent = this.value;
}
}
}
class Dep {
constructor(){
this.subs = [];
}
addSub(sub){
this.subs.push(sub)
}
notity(){
this.subs.forEach((sub) => {
// subs 存储的都是一些Watcher实例
sub.update();
})
}
}
function Vue(option) {
this.$el = document.querySelector(option.el);
this.$data = option.data;
observe(this.$data);
nodeToFragment(this.$el, this);
}
# 异步更新
$nextTick 原理
← 组件库开发流程