]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: refs
authorEvan You <yyx990803@gmail.com>
Mon, 3 Jun 2019 05:44:45 +0000 (13:44 +0800)
committerEvan You <yyx990803@gmail.com>
Mon, 3 Jun 2019 05:44:45 +0000 (13:44 +0800)
packages/runtime-core/src/componentProps.ts
packages/runtime-core/src/componentProxy.ts
packages/runtime-core/src/createRenderer.ts
packages/runtime-core/src/patchFlags.ts
packages/runtime-core/src/vnode.ts
packages/shared/src/index.ts

index 77c07c651b6bc00ab23defd2975c9e5a2f71d62c..58e16fe0d6c878b8c9bc334ee9216ec9eeab10e4 100644 (file)
@@ -7,7 +7,8 @@ import {
   isString,
   isFunction,
   isArray,
-  isObject
+  isObject,
+  isReservedProp
 } from '@vue/shared'
 import { warn } from './warning'
 import { Data, ComponentInstance } from './component'
@@ -101,10 +102,8 @@ export function resolveProps(
 
   if (rawProps != null) {
     for (const key in rawProps) {
-      // key, ref, slots are reserved
-      if (key === 'key' || key === 'ref' || key === 'slots') {
-        continue
-      }
+      // key, ref are reserved
+      if (isReservedProp(key)) continue
       // any non-declared data are put into a separate `attrs` object
       // for spreading
       if (hasDeclaredProps && !options.hasOwnProperty(key)) {
index 0b11228f35795c6f64feaa352a59d5fd4626192f..4d30a9bec6f80cb33b9b0b16c5b62f9bf88090ba 100644 (file)
@@ -19,6 +19,12 @@ export const RenderProxyHandlers = {
           return target.slots
         case '$refs':
           return target.refs
+        case '$parent':
+          return target.parent
+        case '$root':
+          return target.root
+        case '$el':
+          return target.vnode && target.vnode.el
         default:
           break
       }
index c5728c33d19d8ed5a73392ffcfdb1f8f7ea25a7f..8c5860e67b6874b909269ea68f5ca054333f1371 100644 (file)
@@ -14,7 +14,13 @@ import {
   createComponentInstance,
   setupStatefulComponent
 } from './component'
-import { isString, EMPTY_OBJ, EMPTY_ARR } from '@vue/shared'
+import {
+  isString,
+  EMPTY_OBJ,
+  EMPTY_ARR,
+  isReservedProp,
+  isFunction
+} from '@vue/shared'
 import {
   TEXT,
   CLASS,
@@ -225,12 +231,14 @@ export function createRenderer(options: RendererOptions) {
     isSVG: boolean,
     optimized: boolean
   ) {
-    // mount
     if (n1 == null) {
       mountElement(n2, container, anchor, parentComponent, isSVG)
     } else {
       patchElement(n1, n2, parentComponent, isSVG, optimized)
     }
+    if (n2.ref !== null && parentComponent !== null) {
+      setRef(n2.ref, parentComponent, n2.el)
+    }
   }
 
   function mountElement(
@@ -246,6 +254,7 @@ export function createRenderer(options: RendererOptions) {
     const { props, shapeFlag } = vnode
     if (props != null) {
       for (const key in props) {
+        if (isReservedProp(key)) continue
         hostPatchProp(el, key, props[key], null, isSVG)
       }
     }
@@ -385,7 +394,7 @@ export function createRenderer(options: RendererOptions) {
   ) {
     if (oldProps !== newProps) {
       for (const key in newProps) {
-        if (key === 'key' || key === 'ref') continue
+        if (isReservedProp(key)) continue
         const next = newProps[key]
         const prev = oldProps[key]
         if (next !== prev) {
@@ -402,7 +411,7 @@ export function createRenderer(options: RendererOptions) {
       }
       if (oldProps !== EMPTY_OBJ) {
         for (const key in oldProps) {
-          if (key === 'key' || key === 'ref') continue
+          if (isReservedProp(key)) continue
           if (!(key in newProps)) {
             hostPatchProp(
               el,
@@ -539,6 +548,13 @@ export function createRenderer(options: RendererOptions) {
         n2.el = n1.el
       }
     }
+    if (n2.ref !== null && parentComponent !== null) {
+      setRef(
+        n2.ref,
+        parentComponent,
+        (n2.component as ComponentInstance).renderProxy
+      )
+    }
   }
 
   function mountComponent(
@@ -553,9 +569,9 @@ export function createRenderer(options: RendererOptions) {
       Component,
       parentComponent
     ))
-    instance.update = effect(function updateComponent() {
+    instance.update = effect(function componentEffect() {
       if (instance.vnode === null) {
-        // initial mount
+        // mountComponent
         instance.vnode = initialVNode
         resolveProps(instance, initialVNode.props, Component.props)
         resolveSlots(instance, initialVNode.children)
@@ -575,7 +591,7 @@ export function createRenderer(options: RendererOptions) {
           queuePostFlushCb(instance.m)
         }
       } else {
-        // component update
+        // updateComponent
         // This is triggered by mutation of component's own state (next: null)
         // OR parent calling processComponent (next: VNode)
         const { next } = instance
@@ -593,11 +609,17 @@ export function createRenderer(options: RendererOptions) {
         if (instance.bu !== null) {
           invokeHooks(instance.bu)
         }
+        // reset refs
+        // only needed if previous patch had refs
+        if (instance.refs !== EMPTY_OBJ) {
+          instance.refs = {}
+        }
         patch(
           prevTree,
           nextTree,
-          // may have moved
+          // parent may have changed if it's in a portal
           hostParentNode(prevTree.el),
+          // anchor may have changed if it's in a fragment
           getNextHostNode(prevTree),
           instance,
           isSVG
@@ -944,7 +966,7 @@ export function createRenderer(options: RendererOptions) {
   }
 
   function move(vnode: VNode, container: HostNode, anchor: HostNode) {
-    if (vnode.component != null) {
+    if (vnode.component !== null) {
       move(vnode.component.subTree, container, anchor)
       return
     }
@@ -1015,6 +1037,22 @@ export function createRenderer(options: RendererOptions) {
       : getNextHostNode(vnode.component.subTree)
   }
 
+  function setRef(
+    ref: string | Function,
+    parent: ComponentInstance,
+    value: HostNode | ComponentInstance
+  ) {
+    const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs
+    if (isString(ref)) {
+      refs[ref] = value
+    } else {
+      if (__DEV__ && !isFunction(ref)) {
+        // TODO warn invalid ref type
+      }
+      ref(value, refs)
+    }
+  }
+
   return function render(vnode: VNode | null, dom: HostNode): VNode | null {
     if (vnode == null) {
       if (dom._vnode) {
index bbaafcd88d1c5c0feaf31928066da08ba0d152c9..ff7a4ce52059015ca73b163273365bb9734fd237 100644 (file)
@@ -49,3 +49,7 @@ export const UNKEYED = 1 << 6
 // iterated value, or dynamic slot names).
 // Components with this flag are always force updated.
 export const DYNAMIC_SLOTS = 1 << 7
+
+// Indicates an element with ref. This includes static string refs because the
+// refs object is refreshed on each update and all refs need to set again.
+export const REF = 1 << 8
index f5b6033442db681dfb226208f78167d45a35473c..66dc226fc76462b635b64d253b380c429ef31d6d 100644 (file)
@@ -35,6 +35,7 @@ export interface VNode {
   type: VNodeTypes
   props: { [key: string]: any } | null
   key: string | number | null
+  ref: string | Function | null
   children: NormalizedChildren
   component: ComponentInstance | null
 
@@ -65,6 +66,9 @@ const blockStack: (VNode[] | null)[] = []
 //   function render() {
 //     return (openBlock(),createBlock('div', null, [...]))
 //   }
+//
+// disableTracking is true when creating a fragment block, since a fragment
+// always diffs its children.
 export function openBlock(disableTrackng?: boolean) {
   blockStack.push(disableTrackng ? null : [])
 }
@@ -116,6 +120,7 @@ export function createVNode(
     type,
     props,
     key: props && props.key,
+    ref: props && props.ref,
     children: null,
     component: null,
     el: null,
index 002aab837d4ade7b1cc3980ea2fafbe49b1d05da..1884ae88f949105aafbf79ae25ffe88b91c0e95a 100644 (file)
@@ -14,6 +14,9 @@ export const isString = (val: any): val is string => typeof val === 'string'
 export const isObject = (val: any): val is Record<any, any> =>
   val !== null && typeof val === 'object'
 
+export const isReservedProp = (key: string): boolean =>
+  key === 'key' || key === 'ref'
+
 const camelizeRE = /-(\w)/g
 export const camelize = (str: string): string => {
   return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))