一、源码中的位运算
- 按位于 运算
if (shapeFlag & ShapeFlags.TELEPORT) {
解释:如果shapFlag本身值为8,type为1的话,那么转换为二进制(js都是32位)那就是
shapFlag:00000000 00000000 00000000 00001000
type: 00000000 00000000 00000000 00000001
结果为: 00000000 00000000 00000000 00000000
按位进行运算,如果两个都为为1那就是1,否则为0,所以结果为0
- 按位或 赋值
vnode.shapeFlag |= type
// 和这个代码一样
vnode.shapeFlag = vnode.shapeFlag |type
解释:如果shapFlag本身值为8,type为1的话,那么转换为二进制(js都是32位)那就是
shapFlag:00000000 00000000 00000000 00001000
type: 00000000 00000000 00000000 00000001
结果为: 00000000 00000000 00000000 00001001
按位进行运算,如果有一个为1那就是1,所以结果为9
二、挂载vnode时,不同属性的挂载方式不同,这是为什么?
比如:
class使用的是vnode.className = 'newClass'
value是使用el[key] = 'newValue'
type是使用dom.setAttribute('type', 'newType')
主要原因为
HTML Attribute和DOM properties区别
- HTML Attribute:html标签上的属性,例如下面的target,href等
- DOM properties:所在dom对象属性,是通过
document.getElementById('xx')
获取到的dom对象
Element.setAttribute():可以设置元素上的某个属性值
dom.xxx:相当于直接修改指定对象的属性
但是对于相同的属性,两种方式的名称不同
例如class,使用dom修改为dom.className=''
,但是attr的方式就是dom.setAttribute('class', '')
小知识点:设置class时,className的性能高于setAttribute。所以vue进行了一个判断,优化性能,尽量使用className
三、事件挂载
vue的事件更新本质上其实就是下面的demo事件,触发点击时间本质上是触发invoker.value事件,所以挂载、更新、卸载等操作只需要处理invoker的value属性。
const eleBtn = document.querySelector('button')
const invoker = () => {
invoker.value();
}
invoker.value = () => {
alert('hh');
}
eleBtn.addEventListener('click', invoker)
setTimeout(() => {
invoker.value = () => {
alert('pp');
}
}, 2000)
vue源码如下:
export function patchEvent(
el: Element & { _vei?: Record<string, Invoker | undefined> },
rawName: string,
prevValue: EventValue | null,
nextValue: EventValue | null,
instance: ComponentInternalInstance | null = null
) {
// vei = vue event invokers
const invokers = el._vei || (el._vei = {})
const existingInvoker = invokers[rawName]
if (nextValue && existingInvoker) {
// patch
existingInvoker.value = nextValue
} else {
const [name, options] = parseName(rawName)
if (nextValue) {
// add
const invoker = (invokers[rawName] = createInvoker(nextValue, instance))
// 对浏览器的addEventLister进行了封装
addEventListener(el, name, invoker, options)
} else if (existingInvoker) {
// remove
removeEventListener(el, name, existingInvoker, options)
invokers[rawName] = undefined
}
}
}