]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: template ref handling + ref unmount
authorEvan You <yyx990803@gmail.com>
Mon, 19 Aug 2019 22:06:20 +0000 (18:06 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 19 Aug 2019 22:06:20 +0000 (18:06 -0400)
packages/runtime-core/src/createRenderer.ts
packages/runtime-dom/src/modules/props.ts
packages/runtime-dom/src/patchProp.ts

index e9a17697a24ef891e7687e8845ba560ba34508a1..565e5728863e6aca7b7af3cc726aeb12faa22ae6 100644 (file)
@@ -31,7 +31,14 @@ import {
   FULL_PROPS
 } from './patchFlags'
 import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
-import { effect, stop, ReactiveEffectOptions } from '@vue/reactivity'
+import {
+  effect,
+  stop,
+  ReactiveEffectOptions,
+  isRef,
+  Ref,
+  toRaw
+} from '@vue/reactivity'
 import { resolveProps } from './componentProps'
 import { resolveSlots } from './componentSlots'
 import {
@@ -80,7 +87,11 @@ export interface RendererOptions {
     oldValue: any,
     isSVG: boolean,
     prevChildren?: VNode[],
-    unmountChildren?: (children: VNode[]) => void
+    parentComponent?: ComponentInstance | null,
+    unmountChildren?: (
+      children: VNode[],
+      parentComponent: ComponentInstance | null
+    ) => void
   ): void
   insert(el: HostNode, parent: HostNode, anchor?: HostNode): void
   remove(el: HostNode): void
@@ -121,7 +132,7 @@ export function createRenderer(options: RendererOptions) {
     // patching & not same type, unmount old tree
     if (n1 != null && !isSameType(n1, n2)) {
       anchor = getNextHostNode(n1)
-      unmount(n1, true)
+      unmount(n1, parentComponent, true)
       n1 = null
     }
 
@@ -237,7 +248,7 @@ export function createRenderer(options: RendererOptions) {
       patchElement(n1, n2, parentComponent, isSVG, optimized)
     }
     if (n2.ref !== null && parentComponent !== null) {
-      setRef(n2.ref, parentComponent, n2.el)
+      setRef(n2.ref, n1 && n1.ref, parentComponent, n2.el)
     }
   }
 
@@ -306,7 +317,7 @@ export function createRenderer(options: RendererOptions) {
 
       if (patchFlag & FULL_PROPS) {
         // element props contain dynamic keys, full diff needed
-        patchProps(el, n2, oldProps, newProps, isSVG)
+        patchProps(el, n2, oldProps, newProps, parentComponent, isSVG)
       } else {
         // class
         // this flag is matched when the element has dynamic class bindings.
@@ -343,6 +354,7 @@ export function createRenderer(options: RendererOptions) {
                 prev,
                 isSVG,
                 n1.children as VNode[],
+                parentComponent,
                 unmountChildren
               )
             }
@@ -361,7 +373,7 @@ export function createRenderer(options: RendererOptions) {
       }
     } else if (!optimized) {
       // unoptimized, full diff
-      patchProps(el, n2, oldProps, newProps, isSVG)
+      patchProps(el, n2, oldProps, newProps, parentComponent, isSVG)
     }
 
     if (dynamicChildren != null) {
@@ -389,6 +401,7 @@ export function createRenderer(options: RendererOptions) {
     vnode: VNode,
     oldProps: any,
     newProps: any,
+    parentComponent: ComponentInstance | null,
     isSVG: boolean
   ) {
     if (oldProps !== newProps) {
@@ -404,6 +417,7 @@ export function createRenderer(options: RendererOptions) {
             prev,
             isSVG,
             vnode.children as VNode[],
+            parentComponent,
             unmountChildren
           )
         }
@@ -419,6 +433,7 @@ export function createRenderer(options: RendererOptions) {
               null,
               isSVG,
               vnode.children as VNode[],
+              parentComponent,
               unmountChildren
             )
           }
@@ -550,6 +565,7 @@ export function createRenderer(options: RendererOptions) {
     if (n2.ref !== null && parentComponent !== null) {
       setRef(
         n2.ref,
+        n1 && n1.ref,
         parentComponent,
         (n2.component as ComponentInstance).renderProxy
       )
@@ -690,7 +706,7 @@ export function createRenderer(options: RendererOptions) {
     if (shapeFlag & TEXT_CHILDREN) {
       // text children fast path
       if (prevShapeFlag & ARRAY_CHILDREN) {
-        unmountChildren(c1 as VNode[])
+        unmountChildren(c1 as VNode[], parentComponent)
       }
       hostSetElementText(container, c2 as string)
     } else {
@@ -719,7 +735,7 @@ export function createRenderer(options: RendererOptions) {
           )
         } else {
           // c2 is null in this case
-          unmountChildren(c1 as VNode[], true)
+          unmountChildren(c1 as VNode[], parentComponent, true)
         }
       }
     }
@@ -754,7 +770,7 @@ export function createRenderer(options: RendererOptions) {
     }
     if (oldLength > newLength) {
       // remove old
-      unmountChildren(c1, true, commonLength)
+      unmountChildren(c1, parentComponent, true, commonLength)
     } else {
       // mount new
       mountChildren(c2, container, anchor, parentComponent, isSVG, commonLength)
@@ -855,7 +871,7 @@ export function createRenderer(options: RendererOptions) {
     // i = 0, e1 = 0, e2 = -1
     else if (i > e2) {
       while (i <= e1) {
-        unmount(c1[i], true)
+        unmount(c1[i], parentComponent, true)
         i++
       }
     }
@@ -898,7 +914,7 @@ export function createRenderer(options: RendererOptions) {
         const prevChild = c1[i]
         if (patched >= toBePatched) {
           // all new children have been patched so this can only be a removal
-          unmount(prevChild, true)
+          unmount(prevChild, parentComponent, true)
           continue
         }
         let newIndex
@@ -914,7 +930,7 @@ export function createRenderer(options: RendererOptions) {
           }
         }
         if (newIndex === undefined) {
-          unmount(prevChild, true)
+          unmount(prevChild, parentComponent, true)
         } else {
           newIndexToOldIndexMap[newIndex - s2] = i + 1
           if (newIndex >= maxNewIndexSoFar) {
@@ -981,28 +997,45 @@ export function createRenderer(options: RendererOptions) {
     }
   }
 
-  function unmount(vnode: VNode, doRemove?: boolean) {
+  function unmount(
+    vnode: VNode,
+    parentComponent: ComponentInstance | null,
+    doRemove?: boolean
+  ) {
+    // unset ref
+    if (vnode.ref !== null && parentComponent !== null) {
+      setRef(vnode.ref, null, parentComponent, null)
+    }
+
     const instance = vnode.component
     if (instance != null) {
       unmountComponent(instance, doRemove)
       return
     }
+
     const shouldRemoveChildren = vnode.type === Fragment && doRemove
     if (vnode.dynamicChildren != null) {
-      unmountChildren(vnode.dynamicChildren, shouldRemoveChildren)
+      unmountChildren(
+        vnode.dynamicChildren,
+        parentComponent,
+        shouldRemoveChildren
+      )
     } else if (vnode.shapeFlag & ARRAY_CHILDREN) {
-      unmountChildren(vnode.children as VNode[], shouldRemoveChildren)
+      unmountChildren(
+        vnode.children as VNode[],
+        parentComponent,
+        shouldRemoveChildren
+      )
     }
+
     if (doRemove) {
       hostRemove(vnode.el)
       if (vnode.anchor != null) hostRemove(vnode.anchor)
     }
   }
 
-  function unmountComponent(
-    { bum, effects, update, subTree, um }: ComponentInstance,
-    doRemove?: boolean
-  ) {
+  function unmountComponent(instance: ComponentInstance, doRemove?: boolean) {
+    const { bum, effects, update, subTree, um } = instance
     // beforeUnmount hook
     if (bum !== null) {
       invokeHooks(bum)
@@ -1013,7 +1046,7 @@ export function createRenderer(options: RendererOptions) {
       }
     }
     stop(update)
-    unmount(subTree, doRemove)
+    unmount(subTree, instance, doRemove)
     // unmounted hook
     if (um !== null) {
       queuePostFlushCb(um)
@@ -1022,11 +1055,12 @@ export function createRenderer(options: RendererOptions) {
 
   function unmountChildren(
     children: VNode[],
+    parentComponent: ComponentInstance | null,
     doRemove?: boolean,
     start: number = 0
   ) {
     for (let i = start; i < children.length; i++) {
-      unmount(children[i], doRemove)
+      unmount(children[i], parentComponent, doRemove)
     }
   }
 
@@ -1037,13 +1071,35 @@ export function createRenderer(options: RendererOptions) {
   }
 
   function setRef(
-    ref: string | Function,
+    ref: string | Function | Ref<any>,
+    oldRef: string | Function | Ref<any> | null,
     parent: ComponentInstance,
-    value: HostNode | ComponentInstance
+    value: HostNode | ComponentInstance | null
   ) {
     const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs
+    const rawData = toRaw(parent.data)
+
+    // unset old ref
+    if (oldRef !== null && oldRef !== ref) {
+      if (isString(oldRef)) {
+        refs[oldRef] = null
+        const oldSetupRef = rawData[oldRef]
+        if (isRef(oldSetupRef)) {
+          oldSetupRef.value = null
+        }
+      } else if (isRef(oldRef)) {
+        oldRef.value = null
+      }
+    }
+
     if (isString(ref)) {
+      const setupRef = rawData[ref]
+      if (isRef(setupRef)) {
+        setupRef.value = value
+      }
       refs[ref] = value
+    } else if (isRef(ref)) {
+      ref.value = value
     } else {
       if (__DEV__ && !isFunction(ref)) {
         // TODO warn invalid ref type
@@ -1055,7 +1111,7 @@ export function createRenderer(options: RendererOptions) {
   return function render(vnode: VNode | null, dom: HostNode): VNode | null {
     if (vnode == null) {
       if (dom._vnode) {
-        unmount(dom._vnode, true)
+        unmount(dom._vnode, null, true)
       }
     } else {
       patch(dom._vnode, vnode, dom)
index 153c9b2049378d85147c96561b1a85bb1f5eb20b..dc3ad2fac57be0ae33211535909212628a763101 100644 (file)
@@ -3,10 +3,11 @@ export function patchDOMProp(
   key: string,
   value: any,
   prevChildren: any,
+  parentComponent: any,
   unmountChildren: any
 ) {
   if ((key === 'innerHTML' || key === 'textContent') && prevChildren != null) {
-    unmountChildren(prevChildren)
+    unmountChildren(prevChildren, parentComponent)
   }
   el[key] = value
 }
index bbf785ffe33bd43cb2fce51478b213d0ed3aed1a..6148708cd9a3512000c0e2e452c6fc7c5dcfdd05 100644 (file)
@@ -13,6 +13,7 @@ export function patchProp(
   prevValue: any,
   isSVG: boolean,
   prevChildren?: VNode[],
+  parentComponent?: any,
   unmountChildren?: any
 ) {
   switch (key) {
@@ -27,7 +28,14 @@ export function patchProp(
       if (isOn(key)) {
         patchEvent(el, key.slice(2).toLowerCase(), prevValue, nextValue)
       } else if (!isSVG && key in el) {
-        patchDOMProp(el, key, nextValue, prevChildren, unmountChildren)
+        patchDOMProp(
+          el,
+          key,
+          nextValue,
+          prevChildren,
+          parentComponent,
+          unmountChildren
+        )
       } else {
         patchAttr(el, key, nextValue, isSVG)
       }