Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • reactivity响应式

  • runtime-core运行时核心模块

  • runtime-dom运行时DOM模块

    • createApp创建实例解析
    • patchProp属性补丁解析
      • 概览
      • 更新方法
        • patchClass
        • patchStyle
        • patchEvent
        • patchDOMProp
        • patchAttr
        • shared.isOn
        • shared.isModeListener
  • 5.18源码学习》
  • runtime-dom运行时DOM模块
东流
2025-09-16
目录

patchProp属性补丁解析

# 概览

在《createApp创建实例解析》中提到pathProp用于更新DOM元素的属性,本文会相信解析下patchProp方法中是如何更新不同类型的属性。

const patchProp = (el, key, prevValue, nextValue, namespace, parentComponent) => {
    // el:目标DOM元素 key:属性名 preValue:旧值 nextValue:新值 namespace:命名空间  parentComponent:父组件实例 
    
    // isSVG表示是否是SVG元素
    const isSVG = namespace === 'svg';
    if (key === 'class') {
        // 处理class属性
        patchClass(el, nextValue, isSVG);
    } else if (key === "style") {
        // 处理style样式属性
        patchStyle(key, prevValue, nextValue);
    } else if (shared.isOn(key)) {
        // 处理事件监听器,isOn判断属性名即事件名称是否以on开头
        if (!shared.isModeListener(key)) {
          // isModeListener过滤掉onUpdate特殊事件 
          patchEvent(el, key, prevValue, nextValue, parentComponent);
        }
    } else if (key[0] === '.' ? (key = key.slice(1), true) : 
               key[0] === "^" ? (key = key.slice(1), false) : 
               shouldSetAsProp(el, key, nextValue, isSVG)) {
        // 处理DOM属性        
        patchDOMProp(el, key, nextValue);
        
        if (!el.tagName.includes("-") && (key === "value" || key === "checked" || key === "selected")) {
        // 处理特殊属性 value/checked/selected  
          patchAttr(el, key, nextValue, isSVG, parentComponent, key !== "value")
        }
    } else if (el._isVueCE && (/[A-Z]/.test(key) || !shared.isstring(nextValue))) {
        // 处理Web Components属性
        patchDOMProp(el, shared, camelize(key), nextValue, parentComponent, key)
    } else {
        // 处理其他属性
        if (key === 'true-value') {
            el._trueValue = nextValue;
        } else if (key === 'false-value') {
            el._falseValue = nextValue;
        }
        patchAttr(el, key, nextValue, isSVG)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 更新方法

# patchClass

function patchClass(el, value, isSVG) {
  const transitionClasses = el[vtcKey];
  if (transitionClasses) {
    value = (value ? [value, ...transitionClasses] : [...transitionClasses]).join(" ");
  }
  if (value == null) {
    el.removeAttribute("class");
  } else if (isSVG) {
    el.setAttribute("class", value);
  } else {
    el.className = value;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# patchStyle

const displayRE = /(^|;)\s*display\s*:/;
function patchStyle(el, prev, next) {
  const style = el.style;
  const isCssString = shared.isString(next);
  let hasControlledDisplay = false;
  if (next && !isCssString) {
    if (prev) {
      if (!shared.isString(prev)) {
        for (const key in prev) {
          if (next[key] == null) {
            setStyle(style, key, "");
          }
        }
      } else {
        for (const prevStyle of prev.split(";")) {
          const key = prevStyle.slice(0, prevStyle.indexOf(":")).trim();
          if (next[key] == null) {
            setStyle(style, key, "");
          }
        }
      }
    }
    for (const key in next) {
      if (key === "display") {
        hasControlledDisplay = true;
      }
      setStyle(style, key, next[key]);
    }
  } else {
    if (isCssString) {
      if (prev !== next) {
        const cssVarText = style[CSS_VAR_TEXT];
        if (cssVarText) {
          next += ";" + cssVarText;
        }
        style.cssText = next;
        hasControlledDisplay = displayRE.test(next);
      }
    } else if (prev) {
      el.removeAttribute("style");
    }
  }
  if (vShowOriginalDisplay in el) {
    el[vShowOriginalDisplay] = hasControlledDisplay ? style.display : "";
    if (el[vShowHidden]) {
      style.display = "none";
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# patchEvent

const veiKey = Symbol("_vei");
function patchEvent(el, rawName, prevValue, nextValue, instance = null) {
  const invokers = el[veiKey] || (el[veiKey] = {});
  const existingInvoker = invokers[rawName];
  if (nextValue && existingInvoker) {
    existingInvoker.value = nextValue;
  } else {
    const [name, options] = parseName(rawName);
    if (nextValue) {
      const invoker = invokers[rawName] = createInvoker(
        nextValue,
        instance
      );
      addEventListener(el, name, invoker, options);
    } else if (existingInvoker) {
      removeEventListener(el, name, existingInvoker, options);
      invokers[rawName] = void 0;
    }
  }
}

function addEventListener(el, event, handler, options) {
  el.addEventListener(event, handler, options);
}
function removeEventListener(el, event, handler, options) {
  el.removeEventListener(event, handler, options);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# patchDOMProp

function patchDOMProp(el, key, value, parentComponent, attrName) {
  if (key === "innerHTML" || key === "textContent") {
    if (value != null) {
      el[key] = key === "innerHTML" ? unsafeToTrustedHTML(value) : value;
    }
    return;
  }
  const tag = el.tagName;
  if (key === "value" && tag !== "PROGRESS" && // custom elements may use _value internally
  !tag.includes("-")) {
    const oldValue = tag === "OPTION" ? el.getAttribute("value") || "" : el.value;
    const newValue = value == null ? (
      // #11647: value should be set as empty string for null and undefined,
      // but <input type="checkbox"> should be set as 'on'.
      el.type === "checkbox" ? "on" : ""
    ) : String(value);
    if (oldValue !== newValue || !("_value" in el)) {
      el.value = newValue;
    }
    if (value == null) {
      el.removeAttribute(key);
    }
    el._value = value;
    return;
  }
  let needRemove = false;
  if (value === "" || value == null) {
    const type = typeof el[key];
    if (type === "boolean") {
      value = shared.includeBooleanAttr(value);
    } else if (value == null && type === "string") {
      value = "";
      needRemove = true;
    } else if (type === "number") {
      value = 0;
      needRemove = true;
    }
  }
  try {
    el[key] = value;
  } catch (e) {
  }
  needRemove && el.removeAttribute(attrName || key);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# patchAttr

const xlinkNS = "http://www.w3.org/1999/xlink";
function patchAttr(el, key, value, isSVG, instance, isBoolean = shared.isSpecialBooleanAttr(key)) {
  if (isSVG && key.startsWith("xlink:")) {
    if (value == null) {
      el.removeAttributeNS(xlinkNS, key.slice(6, key.length));
    } else {
      el.setAttributeNS(xlinkNS, key, value);
    }
  } else {
    if (value == null || isBoolean && !shared.includeBooleanAttr(value)) {
      el.removeAttribute(key);
    } else {
      el.setAttribute(
        key,
        isBoolean ? "" : shared.isSymbol(value) ? String(value) : value
      );
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# shared.isOn

# shared.isModeListener

编辑 (opens new window)
上次更新: 2025/09/16, 07:26:19
createApp创建实例解析

← createApp创建实例解析

最近更新
01
createApp创建实例解析
09-15
02
渲染器renderer源码解析
09-15
03
生命周期函数Lifecycle解析
09-15
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式