vue3.0的组件的Proxy响应式对象发生改变时,组件将重新渲染,新的虚拟dom就要和旧的虚拟dom对比,来更新组件里的html元素,对比过程就是diff算法。
vue3.0响应式过程主要reactive方法把传入的对象参数转换成响应式对象。主要是用es6的Proxy对象进行数据劫持,定义全局WeakMap的reactiveMap对象,对对象进行收集,如果里面已经存在该对象的Proxy,则不进行劫持,反正则用该对象传入Proxy对象中进行数据劫持,生成新的proxy对象,收集到reactiveMap里。
vue3.0还有一个重要的方法就是effect方法,用来收集依赖,更新视图。内部方法Track是收集依赖,内部方法trigger是触发依赖,所以响应式对象里的get方法就执行的是Track,set方法就执行的是trigger,每次调用effect方法时,定义的全局变量activeEffect就为当前的effect方法,如果effect参数的回调函数用到了proxy对象的变量,立刻会执行Track方法,把响应的activeEffect收集起来,如果proxy对象值改变时,则会拿到收集到的activeEffect方法,执行里面的回调,更新视图。
组件新建,更新代码为:
effect(function componentEffect() {
// 需要创建一个effect,在effect中调用render,这样render 方法中获取数据会收集这个effect
// 属性改变重新执行
// 判断 第一次加载
// instance时组件对象
if (!instance.isMounted) {
// 获取到render(setup)返回值
let proxy = instance.proxy
let subTree = instance.subTree = instance.render.call(proxy, proxy) // 执行 render 组件中 创建 渲染节点
console.log(subTree)
patch(null, subTree, container)
instance.isMounted = true
} else {
// 更新操作
let proxy = instance.proxy
// 以前的虚拟dom
const prevTree = instance.subTree
// 最新的虚拟dom
const nextTree = instance.render.call(proxy, proxy)
instance.subTree = nextTree
// diff算法,更新节点
patch(prevTree, nextTree, container)
}
})
// 元素操作 增删改查
export const nodeOps = {
// 创建元素 createElement 注意: vue runtime-dom => 平台
createElement: tagName => document.createElement(tagName),
remove: child => {
let parent = child.parentNode
if (parent) {
parent.removeChild(child)
}
},
insert: (child, parent, ancher = null) => {
parent.insertBefore(child, ancher)
},
querySelector: select => document.querySelector(select),
setElementText: (el, text) => {
el.textContent = text
},
createText: text => document.createTextNode(text),
setText: (node, text) => node.nodeValue = text
}
// 判断是否是同一个元素
const isSomeVode = (n1,n2) => {
return n1.type == n2.type && n1.key == n2.key
}
const unmount = (vnode) => {
nodeOps.remove(vnode.el)// el虚拟dom的真实元素,在挂载的时候赋值
}
// n1是旧的,n2是新的
const patch = (n1, n2, container,ancher = null) => {
// 针对不同的类型 1 组件 2元素 3 文本
// 对比 vue3:1判断是不是同一个元素 2 同一元素 (1 props children)
// 判断是不是同一个元素
if (n1&&!isSomeVode(n1, n2)) {
unmount(n1)
n1 = null
}
let {shapeFlag, type} = n2
switch (type) {
case TEXT:
processText(n1, n2, container)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
console.log('元素')
processElement(n1, n2, container, ancher)
} else if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
processComponent(n1, n2, container)
}
}
}