瀏覽代碼

support "is" on any element

Evan You 10 年之前
父節點
當前提交
5cf33aeb37

+ 6 - 2
src/compiler/transclude.js

@@ -83,11 +83,15 @@ function transcludeTemplate (el, options) {
         // single nested component
         tag === 'component' ||
         _.resolveAsset(options, 'components', tag) ||
-        replacer.hasAttribute(config.prefix + 'component') ||
+        replacer.hasAttribute('is') ||
+        replacer.hasAttribute(':is') ||
+        replacer.hasAttribute('bind-is') ||
         // element directive
         _.resolveAsset(options, 'elementDirectives', tag) ||
         // for block
-        replacer.hasAttribute(config.prefix + 'for')
+        replacer.hasAttribute(config.prefix + 'for') ||
+        // if block
+        replacer.hasAttribute(config.prefix + 'if')
       ) {
         return frag
       } else {

+ 36 - 24
src/util/component.js

@@ -12,35 +12,47 @@ var _ = require('./index')
 exports.commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/
 exports.checkComponent = function (el, options) {
   var tag = el.tagName.toLowerCase()
-  if (tag === 'component') {
-    // dynamic syntax
-    var exp = el.getAttribute('is')
-    if (exp != null) {
-      el.removeAttribute('is')
-      return { id: exp }
-    } else {
-      exp = _.getBindAttr(el, 'is')
-      if (exp != null) {
-        return { id: exp, dynamic: true }
-      }
-    }
-  } else if (!exports.commonTagRE.test(tag)) {
+  var hasAttrs = el.hasAttributes()
+  if (!exports.commonTagRE.test(tag) && tag !== 'component') {
     if (_.resolveAsset(options, 'components', tag)) {
       return { id: tag }
-    } else if (process.env.NODE_ENV !== 'production') {
-      if (tag.indexOf('-') > -1 ||
-          /HTMLUnknownElement/.test(Object.prototype.toString.call(el))) {
-        _.warn(
-          'Unknown custom element: <' + tag + '> - did you ' +
-          'register the component correctly?'
-        )
+    } else {
+      var is = hasAttrs && getIsBinding(el)
+      if (is) {
+        return is
+      } else if (process.env.NODE_ENV !== 'production') {
+        if (tag.indexOf('-') > -1 ||
+            /HTMLUnknownElement/.test(Object.prototype.toString.call(el))) {
+          _.warn(
+            'Unknown custom element: <' + tag + '> - did you ' +
+            'register the component correctly?'
+          )
+        }
       }
     }
+  } else if (hasAttrs) {
+    return getIsBinding(el)
   }
-  /* eslint-disable no-cond-assign */
-  if (tag = _.attr(el, 'component')) {
-  /* eslint-enable no-cond-assign */
-    return { id: tag }
+}
+
+/**
+ * Get "is" binding from an element.
+ *
+ * @param {Element} el
+ * @return {Object|undefined}
+ */
+
+function getIsBinding (el) {
+  // dynamic syntax
+  var exp = el.getAttribute('is')
+  if (exp != null) {
+    el.removeAttribute('is')
+    return { id: exp }
+  } else {
+    exp = _.getBindAttr(el, 'is')
+    if (exp != null) {
+      return { id: exp, dynamic: true }
+    }
   }
 }
 

+ 2 - 2
test/unit/specs/api/lifecycle_spec.js

@@ -253,12 +253,12 @@ if (_.inBrowser) {
 
       it('safely teardown partial compilation', function () {
         var vm = new Vue({
-          template: '<div v-component="dialog"><div v-partial="hello"></div></div>',
+          template: '<test><partial name="hello"></partial></test>',
           partials: {
             hello: 'Hello {{name}}'
           },
           components: {
-            dialog: {
+            test: {
               template: '<slot></slot>'
             }
           }

+ 7 - 2
test/unit/specs/compiler/transclude_spec.js

@@ -67,8 +67,8 @@ if (_.inBrowser) {
       res = transclude(el, options)
       expect(res instanceof DocumentFragment).toBe(true)
 
-      // single component: v-component
-      options.template = '<div v-component="test"></div>'
+      // single component: is
+      options.template = '<div is="test"></div>'
       res = transclude(el, options)
       expect(res instanceof DocumentFragment).toBe(true)
 
@@ -82,6 +82,11 @@ if (_.inBrowser) {
       options.template = '<div v-for="item in list"></div>'
       res = transclude(el, options)
       expect(res instanceof DocumentFragment).toBe(true)
+
+      // v-if
+      options.template = '<div v-if="ok"></div>'
+      res = transclude(el, options)
+      expect(res instanceof DocumentFragment).toBe(true)
     })
 
     it('direct fragment instance', function () {

+ 1 - 1
test/unit/specs/directives/internal/ref_spec.js

@@ -34,7 +34,7 @@ if (_.inBrowser) {
       expect(vm.$.test2.$options.id).toBe('test2')
     })
 
-    it('with dynamic v-component', function (done) {
+    it('with dynamic component', function (done) {
       var vm = new Vue({
         el: el,
         components: components,

+ 5 - 5
test/unit/specs/directives/public/component_spec.js

@@ -48,10 +48,10 @@ if (_.inBrowser) {
       expect(el.innerHTML).toBe('<p>123</p>')
     })
 
-    it('allow v-component on table elements', function () {
+    it('"is" on table elements', function () {
       var vm = new Vue({
         el: el,
-        template: '<table><tbody><tr v-component="test"></tr></tbody></table>',
+        template: '<table><tbody><tr is="test"></tr></tbody></table>',
         components: {
           test: {
             data: function () {
@@ -239,7 +239,7 @@ if (_.inBrowser) {
       vm.ok = true
       _.nextTick(function () {
         expect(vm.$children.length).toBe(1)
-        expect(vm._directives.length).toBe(3) // v-if, v-component, v-text
+        expect(vm._directives.length).toBe(3) // v-if, component, v-text
         expect(el.textContent).toBe('hello world')
         done()
       })
@@ -483,7 +483,7 @@ if (_.inBrowser) {
     })
 
     it('already mounted warn', function () {
-      el.setAttribute('v-component', 'test')
+      el.setAttribute('is', 'test')
       new Vue({
         el: el
       })
@@ -494,7 +494,7 @@ if (_.inBrowser) {
       expect(function () {
         new Vue({
           el: el,
-          template: '<div v-component="non-existent"></div>'
+          template: '<div is="non-existent"></div>'
         })
       }).not.toThrow()
     })

+ 2 - 2
test/unit/specs/directives/public/for/for_spec.js

@@ -106,13 +106,13 @@ if (_.inBrowser) {
       assertMutations(vm, el, done)
     })
 
-    it('v-component', function (done) {
+    it('is component', function (done) {
       var vm = new Vue({
         el: el,
         data: {
           items: [{a: 1}, {a: 2}]
         },
-        template: '<p v-for="item in items" v-component="test" bind-index="$index" bind-item="item"></p>',
+        template: '<p v-for="item in items" is="test" bind-index="$index" bind-item="item"></p>',
         components: {
           test: {
             props: ['index', 'item'],

+ 1 - 1
test/unit/specs/directives/public/if_spec.js

@@ -65,7 +65,7 @@ if (_.inBrowser) {
       })
     })
 
-    it('v-if + v-component', function (done) {
+    it('v-if + component', function (done) {
       var attachSpy = jasmine.createSpy()
       var detachSpy = jasmine.createSpy()
       var readySpy = jasmine.createSpy()

+ 8 - 0
test/unit/specs/util/component_spec.js

@@ -33,5 +33,13 @@ describe('Util - component', function () {
       components: { test: true }
     })
     expect(res.id).toBe('test')
+
+    // is on undefined custom element
+    el = document.createElement('test2')
+    el.setAttribute('is', 'what')
+    res = _.checkComponent(el, {
+        components: {}
+    })
+    expect(res.id).toBe('what')
   })
 })