Просмотр исходного кода

unify v-ref/v-el, and handle v-ref on/inside v-for

Evan You 10 лет назад
Родитель
Сommit
7b2dc500b7

+ 2 - 2
src/compiler/codegen/directives/el.js

@@ -1,6 +1,6 @@
-export function el (el, dir) {
+export function ref (el, dir) {
   if (!el.hooks) el.hooks = {}
-  const code = `$els["${dir.arg}"]=vnode.elm`
+  const code = `$refs["${dir.arg}"]=vnode.elm || vnode.data.child`
   if (el.hooks.insert) {
     el.hooks.insert = el.hooks.insert
       .replace(/^function\(vnode\)\{(.*)\}$/, `function(vnode){$1;${code}}`)

+ 0 - 2
src/compiler/codegen/directives/index.js

@@ -2,7 +2,6 @@ import { model } from './model'
 import { show } from './show'
 import { html } from './html'
 import { ref } from './ref'
-import { el } from './el'
 export { genHandlers } from './on'
 
 export const directives = {
@@ -10,7 +9,6 @@ export const directives = {
   show,
   html,
   ref,
-  el,
   cloak: function () {} // noop
 }
 

+ 26 - 1
src/compiler/codegen/directives/ref.js

@@ -1,3 +1,28 @@
 export function ref (el, dir) {
-  el.ref = dir.arg
+  if (!el.hooks) {
+    el.hooks = {}
+  }
+  // go up and check if this node is inside a v-for
+  let isFor = false
+  let parent = el
+  while (parent) {
+    if (parent.for !== undefined) {
+      isFor = true
+    }
+    parent = parent.parent
+  }
+  // register ref:
+  // __r__(name, ref, vFor?, remove?)
+  const code = `__r__("${dir.arg}", vnode.data.child || vnode.elm, ${isFor ? 'true' : 'false'}`
+  patchHook(el.hooks, 'insert', `${code})`)
+  patchHook(el.hooks, 'destroy', `${code}, true)`)
+}
+
+const replaceRE = /^function\(vnode\)\{(.*)\}$/
+function patchHook (hooks, name, code) {
+  if (hooks[name]) {
+    hooks[name] = hooks[name].replace(replaceRE, `function(vnode){$1;${code}}`)
+  } else {
+    hooks[name] = `function(vnode){${code}}`
+  }
 }

+ 0 - 4
src/compiler/codegen/index.js

@@ -66,10 +66,6 @@ function genData (el) {
   if (el.key) {
     data += `key:${el.key},`
   }
-  // ref
-  if (el.ref) {
-    data += `ref:"${el.ref}",`
-  }
   // slot names
   if (el.attrsMap.slot) {
     data += `slot:"${el.attrsMap.slot}",`

+ 5 - 13
src/runtime/instance/lifecycle.js

@@ -2,27 +2,19 @@ import Watcher from '../observer/watcher'
 import { query, toArray } from '../util/index'
 
 export function initLifecycle (vm) {
-  vm.$children = []
-  vm._isDestroyed = false
-  vm._isBeingDestroyed = false
-  vm.$refs = {}
-  vm.$els = {}
-
   const options = vm.$options
 
-  // parent
   vm.$parent = options.parent
   vm.$root = vm.$parent ? vm.$parent.$root : vm
   if (vm.$parent) {
     vm.$parent.$children.push(vm)
   }
 
-  // context & ref
-  vm._context = options._context
-  vm._ref = options._renderData && options._renderData.ref
-  if (vm._ref) {
-    vm._context.$refs[vm._ref] = vm
-  }
+  vm.$children = []
+  vm.$refs = {}
+
+  vm._isDestroyed = false
+  vm._isBeingDestroyed = false
 }
 
 export function lifecycleMixin (Vue) {

+ 22 - 0
src/runtime/instance/render.js

@@ -48,6 +48,28 @@ export function renderMixin (Vue) {
         : val
   }
 
+  // register ref
+  Vue.prototype.__r__ = function (key, ref, vFor, remove) {
+    const refs = this.$refs
+    if (remove) {
+      if (vFor) {
+        refs[key].$remove(ref)
+      } else {
+        refs[key] = undefined
+      }
+    } else {
+      if (vFor) {
+        if (refs[key]) {
+          refs[key].push(ref)
+        } else {
+          refs[key] = [ref]
+        }
+      } else {
+        refs[key] = ref
+      }
+    }
+  }
+
   Vue.prototype._update = function (vnode) {
     if (this._mounted) {
       callHook(this, 'beforeUpdate')

+ 20 - 6
src/runtime/vdom/component.js

@@ -2,7 +2,7 @@ import Vue from '../instance/index'
 import { callHook } from '../instance/lifecycle'
 import { warn } from '../util/index'
 
-export default function Component (Ctor, data, parent, children, context) {
+export default function Component (Ctor, data, parent, children) {
   if (typeof Ctor === 'object') {
     Ctor = Vue.extend(Ctor)
   }
@@ -14,14 +14,29 @@ export default function Component (Ctor, data, parent, children, context) {
       'dependencies and optimizes re-rendering.'
     )
   }
+  // merge hooks on the placeholder node itself
+  const hook = { init, insert, prepatch, destroy }
+  if (data.hook) {
+    for (let key in data.hook) {
+      let existing = hook[key]
+      let fromParent = data.hook[key]
+      hook[key] = existing ? mergeHook(existing, fromParent) : fromParent
+    }
+  }
   // return a placeholder vnode
   return {
     tag: 'component',
     key: data && data.key,
-    data: {
-      hook: { init, insert, prepatch, destroy },
-      Ctor, data, parent, children, context
-    }
+    data: { hook, Ctor, data, parent, children }
+  }
+}
+
+function mergeHook (a, b) {
+  // since all hooks have at most two args, use fixed args
+  // to avoid having to use fn.apply().
+  return (_, __) => {
+    a(_, __)
+    b(_, __)
   }
 }
 
@@ -29,7 +44,6 @@ function init (vnode) {
   const data = vnode.data
   const child = new data.Ctor({
     parent: data.parent,
-    _context: data.context,
     _renderData: data.data,
     _renderChildren: data.children
   })

+ 2 - 2
src/runtime/vdom/create-element.js

@@ -18,7 +18,7 @@ export default function createElement (tag, data, children) {
     if (isReservedTag(tag)) {
       return VNode(tag, data, flatten(children))
     } else if ((Ctor = resolveAsset(context.$options, 'components', tag))) {
-      return Component(Ctor, data, parent, children, context)
+      return Component(Ctor, data, parent, children)
     } else {
       if (process.env.NODE_ENV !== 'production' && !data.svg) {
         warn(
@@ -30,7 +30,7 @@ export default function createElement (tag, data, children) {
       return VNode(tag, data, flatten(children && children()))
     }
   } else {
-    return Component(tag, data, parent, children, context)
+    return Component(tag, data, parent, children)
   }
 }