Browse Source

fix nested svg namespaces

dev
Evan You 9 years ago
parent
commit
579ff4ddbc
  1. 5
      src/compiler/parser/index.js
  2. 44
      src/core/vdom/create-element.js
  3. 15
      src/core/vdom/helpers.js
  4. 15
      src/core/vdom/patch.js
  5. 9
      test/unit/features/options/el.spec.js
  6. 2
      test/unit/modules/compiler/parser.spec.js
  7. 1
      test/unit/modules/vdom/create-element.spec.js
  8. 6
      test/unit/modules/vdom/patch/element.spec.js

5
src/compiler/parser/index.js

@ -3,7 +3,7 @@
import { decodeHTML } from 'entities' import { decodeHTML } from 'entities'
import { parseHTML } from './html-parser' import { parseHTML } from './html-parser'
import { parseText } from './text-parser' import { parseText } from './text-parser'
import { hyphenate, cached, no } from 'shared/util' import { cached, no } from 'shared/util'
import { import {
pluckModuleFunction, pluckModuleFunction,
getAndRemoveAttr, getAndRemoveAttr,
@ -59,9 +59,6 @@ export function parse (
expectHTML: options.expectHTML, expectHTML: options.expectHTML,
isUnaryTag: options.isUnaryTag, isUnaryTag: options.isUnaryTag,
start (tag, attrs, unary) { start (tag, attrs, unary) {
// normalize tag name
tag = hyphenate(tag)
// check namespace. // check namespace.
// inherit parent ns if there is one // inherit parent ns if there is one
const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag) const ns = (currentParent && currentParent.ns) || platformGetTagNamespace(tag)

44
src/core/vdom/create-element.js

@ -41,38 +41,16 @@ function _createElement (
// in case of component :is set to falsy value // in case of component :is set to falsy value
return emptyVNode() return emptyVNode()
} }
if (typeof tag === 'string') { const Ctor = typeof tag === 'string'
const namespace = config.getTagNamespace(tag) ? resolveAsset(context.$options, 'components', tag)
let Ctor : tag
if (config.isReservedTag(tag)) { if (Ctor) {
return new VNode( return createComponent(Ctor, data, parent, context, host, children)
tag, data, normalizeChildren(children, namespace), } else if (typeof tag === 'string') {
undefined, undefined, const ns = config.getTagNamespace(tag)
namespace, context, host return new VNode(
) tag, data, normalizeChildren(children, ns),
} else if ((Ctor = resolveAsset(context.$options, 'components', tag))) { undefined, undefined, ns, context, host
return createComponent(Ctor, data, parent, context, host, children, tag) )
} else {
if (process.env.NODE_ENV !== 'production') {
if (
!namespace &&
!(config.ignoredElements && config.ignoredElements.indexOf(tag) > -1) &&
config.isUnknownElement(tag)
) {
warn(
'Unknown custom element: <' + tag + '> - did you ' +
'register the component correctly? For recursive components, ' +
'make sure to provide the "name" option.'
)
}
}
return new VNode(
tag, data, normalizeChildren(children, namespace),
undefined, undefined,
namespace, context, host
)
}
} else {
return createComponent(tag, data, parent, context, host, children)
} }
} }

15
src/core/vdom/helpers.js

@ -36,7 +36,9 @@ export function normalizeChildren (
last.text += c.text last.text += c.text
} else { } else {
// inherit parent namespace // inherit parent namespace
if (ns && c.tag) c.ns = ns if (ns) {
applyNS(c, ns)
}
res.push(c) res.push(c)
} }
} }
@ -49,6 +51,17 @@ function createTextVNode (val) {
return new VNode(undefined, undefined, undefined, String(val)) return new VNode(undefined, undefined, undefined, String(val))
} }
function applyNS (vnode, ns) {
if (vnode.tag && !vnode.ns) {
vnode.ns = ns
if (vnode.children) {
for (let i = 0, l = vnode.children.length; i < l; i++) {
applyNS(vnode.children[i], ns)
}
}
}
}
export function updateListeners ( export function updateListeners (
on: Object, on: Object,
oldOn: Object, oldOn: Object,

15
src/core/vdom/patch.js

@ -7,6 +7,7 @@
* of making flow understand it is not worth it. * of making flow understand it is not worth it.
*/ */
import config from '../config'
import VNode from './vnode' import VNode from './vnode'
import { isPrimitive, _toString, warn } from '../util/index' import { isPrimitive, _toString, warn } from '../util/index'
@ -90,6 +91,20 @@ export function createPatchFunction (backend) {
const children = vnode.children const children = vnode.children
const tag = vnode.tag const tag = vnode.tag
if (isDef(tag)) { if (isDef(tag)) {
if (process.env.NODE_ENV !== 'production') {
if (
!vnode.ns &&
!(config.ignoredElements && config.ignoredElements.indexOf(tag) > -1) &&
config.isUnknownElement(tag)
) {
warn(
'Unknown custom element: <' + tag + '> - did you ' +
'register the component correctly? For recursive components, ' +
'make sure to provide the "name" option.',
vnode.context
)
}
}
elm = vnode.elm = vnode.ns elm = vnode.elm = vnode.ns
? nodeOps.createElementNS(vnode.ns, tag) ? nodeOps.createElementNS(vnode.ns, tag)
: nodeOps.createElement(tag) : nodeOps.createElement(tag)

9
test/unit/features/options/el.spec.js

@ -42,7 +42,11 @@ describe('Options el', () => {
it('svg element', () => { it('svg element', () => {
const parent = document.createElement('div') const parent = document.createElement('div')
parent.innerHTML = '<svg><text :x="x" :y="y" :fill="color">{{ text }}</text></svg>' parent.innerHTML =
'<svg>' +
'<text :x="x" :y="y" :fill="color">{{ text }}</text>' +
'<g><clipPath><foo></foo></clipPath></g>' +
'</svg>'
const vm = new Vue({ const vm = new Vue({
el: parent.childNodes[0], el: parent.childNodes[0],
data: { data: {
@ -57,6 +61,9 @@ describe('Options el', () => {
expect(vm.$el.childNodes[0].getAttribute('y')).toBe(vm.y.toString()) expect(vm.$el.childNodes[0].getAttribute('y')).toBe(vm.y.toString())
expect(vm.$el.childNodes[0].getAttribute('fill')).toBe(vm.color) expect(vm.$el.childNodes[0].getAttribute('fill')).toBe(vm.color)
expect(vm.$el.childNodes[0].textContent).toBe(vm.text) expect(vm.$el.childNodes[0].textContent).toBe(vm.text)
// nested, non-explicitly listed SVG elements
expect(vm.$el.childNodes[1].childNodes[0].namespaceURI).toContain('svg')
expect(vm.$el.childNodes[1].childNodes[0].childNodes[0].namespaceURI).toContain('svg')
}) })
it('warn cannot find element', () => { it('warn cannot find element', () => {

2
test/unit/modules/compiler/parser.spec.js

@ -46,7 +46,7 @@ describe('parser', () => {
it('camelCase element', () => { it('camelCase element', () => {
const ast = parse('<MyComponent><p>hello world</p></MyComponent>', baseOptions) const ast = parse('<MyComponent><p>hello world</p></MyComponent>', baseOptions)
expect(ast.tag).toBe('my-component') expect(ast.tag).toBe('MyComponent')
expect(ast.plain).toBe(true) expect(ast.plain).toBe(true)
expect(ast.children[0].tag).toBe('p') expect(ast.children[0].tag).toBe('p')
expect(ast.children[0].plain).toBe(true) expect(ast.children[0].plain).toBe(true)

1
test/unit/modules/vdom/create-element.spec.js

@ -62,7 +62,6 @@ describe('create-element', () => {
expect(vnode.ns).toBeUndefined() expect(vnode.ns).toBeUndefined()
expect(vnode.context).toEqual(vm) expect(vnode.context).toEqual(vm)
expect(vnode.componentOptions).toBeUndefined() expect(vnode.componentOptions).toBeUndefined()
expect(`Unknown custom element: <${tag}> - did you`).toHaveBeenWarned()
}) })
it('render empty vnode with falsy tag using createElement', () => { it('render empty vnode with falsy tag using createElement', () => {

6
test/unit/modules/vdom/patch/element.spec.js

@ -16,6 +16,12 @@ describe('element', () => {
expect(elm.namespaceURI).toBe('http://www.w3.org/2000/svg') expect(elm.namespaceURI).toBe('http://www.w3.org/2000/svg')
}) })
it('should warn unknown element', () => {
const vnode = new VNode('unknown')
patch(null, vnode)
expect(`Unknown custom element: <unknown>`).toHaveBeenWarned()
})
it('should create an elements which having text content', () => { it('should create an elements which having text content', () => {
const vnode = new VNode('div', {}, [createTextVNode('hello world')]) const vnode = new VNode('div', {}, [createTextVNode('hello world')])
const elm = patch(null, vnode) const elm = patch(null, vnode)

Loading…
Cancel
Save