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

compiler: properly expose modifiers to directives (fix #1399)

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

+ 53 - 41
src/compiler/compile.js

@@ -10,8 +10,8 @@ var resolveAsset = _.resolveAsset
 // special binding prefixes
 var bindRE = /^v-bind:|^:/
 var onRE = /^v-on:|^@/
-var literalRE = /\.literal$/
 var argRE = /:(.*)$/
+var modifierRE = /\.[^\.]+/g
 var transitionRE = /^(v-bind:|:)?transition$/
 
 // terminal directives
@@ -480,8 +480,10 @@ function checkComponent (el, options) {
     var descriptor = {
       name: 'component',
       expression: component.id,
-      literal: !component.dynamic,
-      def: internalDirectives.component
+      def: internalDirectives.component,
+      modifiers: {
+        literal: !component.dynamic
+      }
     }
     var componentLinkFn = function (vm, el, host, scope, frag) {
       vm._bindDir(descriptor, el, host, scope, frag)
@@ -568,34 +570,35 @@ function makeTerminalNodeLinkFn (el, dirName, value, options, def) {
 function compileDirectives (attrs, options) {
   var i = attrs.length
   var dirs = []
-  var attr, name, raw, value, dirName, arg, dirDef, isLiteral, tokens
+  var attr, name, value, rawName, rawValue, dirName, arg, modifiers, dirDef, tokens
   while (i--) {
     attr = attrs[i]
-    name = attr.name
-    raw = value = attr.value
+    name = rawName = attr.name
+    value = rawValue = attr.value
     tokens = textParser.parse(value)
+    // reset arg
+    arg = null
+    // check modifiers
+    modifiers = parseModifiers(name)
+    name = name.replace(modifierRE, '')
 
     // attribute interpolations
     if (tokens) {
       value = textParser.tokensToExp(tokens)
-      pushDir('bind', publicDirectives.bind, {
-        arg: name,
-        interp: true
-      })
+      arg = name
+      pushDir('bind', publicDirectives.bind, true)
     } else
 
     // special attribute: transition
     if (transitionRE.test(name)) {
-      pushDir('transition', internalDirectives.transition, {
-        literal: !bindRE.test(name)
-      })
+      modifiers.literal = !bindRE.test(name)
+      pushDir('transition', internalDirectives.transition)
     } else
 
     // event handlers
     if (onRE.test(name)) {
-      pushDir('on', publicDirectives.on, {
-        arg: name.replace(onRE, '')
-      })
+      arg = name.replace(onRE, '')
+      pushDir('on', publicDirectives.on)
     } else
 
     // attribute bindings
@@ -604,19 +607,13 @@ function compileDirectives (attrs, options) {
       if (dirName === 'style' || dirName === 'class') {
         pushDir(dirName, internalDirectives[dirName])
       } else {
-        pushDir('bind', publicDirectives.bind, {
-          arg: dirName
-        })
+        arg = dirName
+        pushDir('bind', publicDirectives.bind)
       }
     } else
 
     // normal directives
     if (name.indexOf('v-') === 0) {
-      // check literal
-      isLiteral = literalRE.test(name)
-      if (isLiteral) {
-        name = name.replace(literalRE, '')
-      }
       // check arg
       arg = (arg = name.match(argRE)) && arg[1]
       if (arg) {
@@ -637,14 +634,11 @@ function compileDirectives (attrs, options) {
       }
 
       if (dirDef) {
-        if (!isLiteral && _.isLiteral(value)) {
+        if (_.isLiteral(value)) {
           value = _.stripQuotes(value)
-          isLiteral = true
+          modifiers.literal = true
         }
-        pushDir(dirName, dirDef, {
-          arg: arg,
-          literal: isLiteral
-        })
+        pushDir(dirName, dirDef)
       }
     }
   }
@@ -654,23 +648,22 @@ function compileDirectives (attrs, options) {
    *
    * @param {String} dirName
    * @param {Object|Function} def
-   * @param {Object} [opts]
+   * @param {Boolean} [interp]
    */
 
-  function pushDir (dirName, def, opts) {
+  function pushDir (dirName, def, interp) {
     var parsed = dirParser.parse(value)
-    var dir = {
+    dirs.push({
       name: dirName,
-      attr: name,
-      raw: raw,
+      attr: rawName,
+      raw: rawValue,
       def: def,
+      arg: arg,
+      modifiers: modifiers,
       expression: parsed.expression,
-      filters: parsed.filters
-    }
-    if (opts) {
-      _.extend(dir, opts)
-    }
-    dirs.push(dir)
+      filters: parsed.filters,
+      interp: interp
+    })
   }
 
   if (dirs.length) {
@@ -678,6 +671,25 @@ function compileDirectives (attrs, options) {
   }
 }
 
+/**
+ * Parse modifiers from directive attribute name.
+ *
+ * @param {String} name
+ * @return {Object}
+ */
+
+function parseModifiers (name) {
+  var res = Object.create(null)
+  var match = name.match(modifierRE)
+  if (match) {
+    var i = match.length
+    while (i--) {
+      res[match[i].slice(1)] = true
+    }
+  }
+  return res
+}
+
 /**
  * Build a link function for all directives on a single node.
  *

+ 2 - 1
src/directive.js

@@ -35,8 +35,9 @@ function Directive (descriptor, vm, el, host, scope, frag) {
   this.name = descriptor.name
   this.expression = descriptor.expression
   this.arg = descriptor.arg
+  this.modifiers = descriptor.modifiers
   this.filters = descriptor.filters
-  this.literal = descriptor.literal
+  this.literal = this.modifiers && this.modifiers.literal
   // private
   this._locked = false
   this._bound = false

+ 7 - 4
test/unit/specs/compiler/compile_spec.js

@@ -50,7 +50,7 @@ if (_.inBrowser) {
 
     it('normal directives', function () {
       el.setAttribute('v-a', 'b')
-      el.innerHTML = '<p v-a:hello="a" v-b="1">hello</p><div v-b.literal="hi"></div>'
+      el.innerHTML = '<p v-a:hello.a.b="a" v-b="1">hello</p><div v-b.literal="hi"></div>'
       var defA = { priority: 1 }
       var defB = { priority: 2 }
       var options = _.mergeOptions(Vue.options, {
@@ -80,21 +80,24 @@ if (_.inBrowser) {
       expect(args[0].name).toBe('a')
       expect(args[0].expression).toBe('a')
       expect(args[0].def).toBe(defA)
+      // args + multiple modifiers
       expect(args[0].arg).toBe('hello')
+      expect(args[0].modifiers.a).toBe(true)
+      expect(args[0].modifiers.b).toBe(true)
       expect(args[1]).toBe(el.firstChild)
       // 3 (expression literal)
       args = vm._bindDir.calls.argsFor(isAttrReversed ? 1 : 2)
       expect(args[0].name).toBe('b')
       expect(args[0].expression).toBe('1')
       expect(args[0].def).toBe(defB)
-      expect(args[0].literal).toBe(true)
+      expect(args[0].modifiers.literal).toBe(true)
       expect(args[1]).toBe(el.firstChild)
       // 4 (explicit literal)
       args = vm._bindDir.calls.argsFor(3)
       expect(args[0].name).toBe('b')
       expect(args[0].expression).toBe('hi')
       expect(args[0].def).toBe(defB)
-      expect(args[0].literal).toBe(true)
+      expect(args[0].modifiers.literal).toBe(true)
       expect(args[1]).toBe(el.lastChild)
       // check the priority sorting
       // the "b"s should be called first!
@@ -229,7 +232,7 @@ if (_.inBrowser) {
       var args = vm._bindDir.calls.argsFor(0)
       expect(args[0].name).toBe('component')
       expect(args[0].expression).toBe('my-component')
-      expect(args[0].literal).toBe(true)
+      expect(args[0].modifiers.literal).toBe(true)
       expect(args[0].def).toBe(internalDirectives.component)
       expect(_.warn).not.toHaveBeenCalled()
     })

+ 6 - 2
test/unit/specs/directive_spec.js

@@ -34,7 +34,9 @@ describe('Directive', function () {
       name: 'test',
       def: def,
       expression: 'a',
-      literal: false,
+      modifiers: {
+        literal: false
+      },
       filters: [{ name: 'test' }]
     }, vm, el)
     d._bind()
@@ -66,7 +68,9 @@ describe('Directive', function () {
       expression: 'a',
       raw: 'a',
       def: def,
-      literal: true
+      modifiers: {
+        literal: true
+      }
     }, vm, el)
     d._bind()
     expect(d._watcher).toBeUndefined()

+ 6 - 2
test/unit/specs/directives/internal/transition_spec.js

@@ -18,7 +18,9 @@ if (_.inBrowser) {
         name: 'transition',
         raw: 'test',
         def: def,
-        literal: true
+        modifiers: {
+          literal: true
+        }
       }, vm, el)
       dir._bind()
       var transition = dir.el.__v_trans
@@ -43,7 +45,9 @@ if (_.inBrowser) {
         name: 'transition',
         raw: 'test',
         def: def,
-        literal: true
+        modifiers: {
+          literal: true
+        }
       }, vm1, el)
       dir.el.__vue__ = vm2
       dir._bind()