Jelajahi Sumber

deprecate {{>yield}} and use <content> syntax

Evan You 12 tahun lalu
induk
melakukan
1f3175bb32

+ 62 - 6
src/compiler.js

@@ -177,7 +177,6 @@ function Compiler (vm, options) {
     }
 
     // done!
-    compiler.rawContent = null
     compiler.init = false
 
     // post compile / ready hook
@@ -197,14 +196,16 @@ CompilerProto.setupElement = function (options) {
         : options.el || document.createElement(options.tagName || 'div')
 
     var template = options.template,
-        child, frag, replacer, i, attr, attrs
+        child, replacer, i, attr, attrs
 
     if (template) {
         // collect anything already in there
-        /* jshint boss: true */
-        frag = this.rawContent = document.createDocumentFragment()
-        while (child = el.firstChild) {
-            frag.appendChild(child)
+        if (el.hasChildNodes()) {
+            this.rawContent = document.createElement('div')
+            /* jshint boss: true */
+            while (child = el.firstChild) {
+                this.rawContent.appendChild(child)
+            }
         }
         // replace option: use the first node in
         // the template directly
@@ -227,6 +228,8 @@ CompilerProto.setupElement = function (options) {
         } else {
             el.appendChild(template.cloneNode(true))
         }
+
+        this.setupContent(el)
     }
 
     // apply element options
@@ -242,6 +245,59 @@ CompilerProto.setupElement = function (options) {
     return el
 }
 
+/**
+ *  Deal with <content> insertion points
+ *  per the Web Components spec
+ */
+CompilerProto.setupContent = function (el) {
+
+    var outlets = slice.call(el.getElementsByTagName('content')),
+        raw = this.rawContent,
+        outlet, select, i, j, main
+
+    i = outlets.length
+    if (i) {
+        // first pass, collect corresponding content
+        // for each outlet.
+        while (i--) {
+            outlet = outlets[i]
+            if (raw) {
+                select = outlet.getAttribute('select')
+                if (select) { // select content
+                    outlet.content =
+                        slice.call(raw.querySelectorAll(select))
+                } else { // default content
+                    main = outlet
+                }
+            } else { // fallback content
+                outlet.content =
+                    slice.call(outlet.childNodes)
+            }
+        }
+        // second pass, actually insert the contents
+        for (i = 0, j = outlets.length; i < j; i++) {
+            outlet = outlets[i]
+            if (outlet === main) continue
+            insert(outlet, outlet.content)
+        }
+        // finally insert the main content
+        if (raw) {
+            insert(main, slice.call(raw.childNodes))
+        }
+    }
+
+    function insert (outlet, contents) {
+        var parent = outlet.parentNode,
+            i = 0, j = contents.length
+        for (; i < j; i++) {
+            parent.insertBefore(contents[i], outlet)
+        }
+        parent.removeChild(outlet)
+    }
+
+    this.rawContent = null
+}
+
 /**
  *  Setup observer.
  *  The observer listens for get/set/mutate events on all VM

+ 15 - 13
src/directives/partial.js

@@ -9,30 +9,32 @@ module.exports = {
 
     bind: function () {
 
-        var compiler = this.compiler,
-            id = this.expression
+        var id = this.expression
         if (!id) return
 
-        var partial = id === 'yield'
-            ? this.compiler.rawContent
-            : this.compiler.getOption('partials', id)
+        var el       = this.el,
+            compiler = this.compiler,
+            partial  = compiler.getOption('partials', id)
 
         if (!partial) {
-            utils.warn('Unknown partial: ' + id)
+            if (id === 'yield') {
+                utils.warn('{{>yield}} syntax has been deprecated. Use <content> tag instead.')
+            } else {
+                utils.warn('Unknown partial: ' + id)
+            }
             return
         }
 
         partial = partial.cloneNode(true)
 
         // comment ref node means inline partial
-        if (this.el.nodeType === 8) {
+        if (el.nodeType === 8) {
 
             // keep a ref for the partial's content nodes
             var nodes = [].slice.call(partial.childNodes),
-                ref = this.el,
-                parent = ref.parentNode
-            parent.insertBefore(partial, ref)
-            parent.removeChild(ref)
+                parent = el.parentNode
+            parent.insertBefore(partial, el)
+            parent.removeChild(el)
             // compile partial after appending, because its children's parentNode
             // will change from the fragment to the correct parentNode.
             // This could affect directives that need access to its element's parentNode.
@@ -41,8 +43,8 @@ module.exports = {
         } else {
 
             // just set innerHTML...
-            this.el.innerHTML = ''
-            this.el.appendChild(partial.cloneNode(true))
+            el.innerHTML = ''
+            el.appendChild(partial.cloneNode(true))
 
         }
     }

+ 3 - 5
src/directives/view.js

@@ -16,7 +16,7 @@ module.exports = {
         // cache original content
         /* jshint boss: true */
         var node,
-            frag = this.inner = document.createDocumentFragment()
+            frag = this.inner = document.createElement('div')
         while (node = el.firstChild) {
             frag.appendChild(node)
         }
@@ -30,13 +30,11 @@ module.exports = {
         var Ctor  = this.compiler.getOption('components', value)
         if (!Ctor) return
 
-        var inner = this.inner.cloneNode(true)
-
         this.childVM = new Ctor({
             el: this.raw.cloneNode(true),
             parent: this.vm,
-            created: function () {
-                this.$compiler.rawContent = inner
+            compilerOptions: {
+                rawContent: this.inner.cloneNode(true)
             }
         })
 

+ 1 - 1
src/utils.js

@@ -313,7 +313,7 @@ function enableDebug () {
         if (!config.silent && console) {
             console.warn(msg)
             if (config.debug && console.trace) {
-                console.trace(msg)
+                console.trace()
             }
         }
     }

+ 3 - 3
test/functional/fixtures/routing.html

@@ -53,21 +53,21 @@
 Vue.config('debug', true)
 
 Vue.component('home', {
-    template: '<h1>Home</h1><div class="content">{{>yield}}</div>',
+    template: '<h1>Home</h1><div class="content"><content/></div>',
     created: function () {
         this.msg = "Home sweet home!"
     }
 })
 
 Vue.component('page1', {
-    template: '<h1>Page1</h1><div class="content">{{>yield}}</div>',
+    template: '<h1>Page1</h1><div class="content"><content/></div>',
     created: function () {
         this.msg = "Welcome to page 1!"
     }
 })
 
 Vue.component('page2', {
-    template: '<h1>Page2</h1><div class="content">{{>yield}}</div>',
+    template: '<h1>Page2</h1><div class="content"><content/></div>',
     created: function () {
         this.msg = "Welcome to page 2!"
     }

+ 27 - 6
test/functional/fixtures/template.html

@@ -4,7 +4,6 @@
 <div id="conditional" v-partial="{{ok ? 'global' : 'nope'}}"></div>
 <div id="japan">{{> local}}</div>
 <div id="repeat">{{> repeat}}</div>
-<div id="yielder"><x-yielder><p>{{a}}</p><p>{{b}}</p></x-yielder></div>
 
 <script type="text/v-template" id="test">
     <p>{{hi}}!</p>
@@ -14,10 +13,27 @@
     <p v-repeat="items">{{title}}</p>
 </script>
 
-<script type="text/v-template" id="yield-template">
-    <h1>before</h1>{{>yield}}<h2>after</h2>
+<!-- content insertion point tests -->
+<div id="content">
+<my-compponent>
+    <p class="b">{{b}}</p>
+    <p class="rest1">rest</p>
+    <p class="rest2">rest</p>
+    <p class="a">{{a}}</p>
+</my-compponent>
+</div>
+
+<script type="text/v-template" id="content-template">
+<h1>before</h1>
+<content select=".a"></content>
+<content select=".b"></content>
+<content></content>
+<h2>after</h2>
 </script>
 
+<!-- content fallback test -->
+<div id="fallback"></div>
+
 <script src="../../../dist/vue.js"></script>
 <script>
 
@@ -76,12 +92,17 @@
         }
     })
 
-    Vue.component('x-yielder', {
-        template: '#yield-template',
+    Vue.component('my-compponent', {
+        template: '#content-template',
         data: {
             a: 'A', b: 'B'
         }
     })
 
-    new Vue({el:'#yielder'})
+    new Vue({el:'#content'})
+
+    new Vue({
+        el: '#fallback',
+        template: '<content>This is fallback</content>'
+    })
 </script>

+ 6 - 3
test/functional/specs/template.js

@@ -1,4 +1,4 @@
-casper.test.begin('Templates and Partials', 6, function (test) {
+casper.test.begin('Templates and Partials', 7, function (test) {
     
     casper
     .start('./fixtures/template.html')
@@ -9,8 +9,11 @@ casper.test.begin('Templates and Partials', 6, function (test) {
         test.assertSelectorHasText('#hawaii', 'Aloha', 'extend option')
         test.assertSelectorHasText('#repeat', 'Repeat', 'inline partial with repeat')
         test.assertEvalEquals(function () {
-            return document.querySelector('#yielder').innerHTML
-        }, '<x-yielder><h1>before</h1><p>A</p><p>B</p><h2>after</h2></x-yielder>')
+            return document.querySelector('#content').textContent
+                .replace(/\n/g, ' ')
+                .replace(/\s+/g, ' ')
+        }, ' before A B rest rest after ')
+        test.assertSelectorHasText('#fallback', 'This is fallback')
     })
     .run(function () {
         test.done()

+ 40 - 0
test/unit/specs/misc.js

@@ -207,4 +207,44 @@ describe('Misc Features', function () {
 
     })
 
+    describe('content insertion points', function () {
+        
+        it('should insert original content', function () {
+            var div = document.createElement('div')
+            div.innerHTML = '<h1>hello!</h1>'
+            var t = new Vue({
+                el: div,
+                template: '<h1>before</h1><content></content><p>after</p>'
+            })
+            assert.strictEqual(t.$el.innerHTML, '<h1>before</h1><h1>hello!</h1><p>after</p>')
+        })
+
+        it('should respect "select" attributes', function () {
+            var div = document.createElement('div')
+            div.innerHTML = '<h1>hi</h1><h1>ha</h1><p>hehe</p>'
+            var t = new Vue({
+                el: div,
+                template: '<h1>before</h1>' +
+                    '<content></content>' +
+                    '<content select="h1:nth-of-type(2)"></content>' +
+                    '<content select="h1:nth-of-type(1)"></content>' +
+                    '<p>after</p>'
+            })
+            assert.strictEqual(t.$el.innerHTML,
+                '<h1>before</h1>' +
+                '<p>hehe</p>' +
+                '<h1>ha</h1><h1>hi</h1>' +
+                '<p>after</p>'
+            )
+        })
+
+        it('should use fallback content if no rawContent is available', function () {
+            var t = new Vue({
+                template: '<content>Hello!</content>'
+            })
+            assert.strictEqual(t.$el.innerHTML, 'Hello!')
+        })
+
+    })
+
 })