diff --git a/src/platforms/web/server/modules/attrs.js b/src/platforms/web/server/modules/attrs.js index fb90e8b5..286f6894 100644 --- a/src/platforms/web/server/modules/attrs.js +++ b/src/platforms/web/server/modules/attrs.js @@ -1,12 +1,16 @@ -import { isBooleanAttr, isEnumeratedAttr } from 'web/util/index' +import { isBooleanAttr, isEnumeratedAttr, propsToAttrMap } from 'web/util/index' export default function renderAttrs (node) { - if (node.data.attrs || node.data.staticAttrs) { - return serialize(node.data.staticAttrs) + serialize(node.data.attrs) + if (node.data.attrs || node.data.props || node.data.staticAttrs) { + return ( + serialize(node.data.staticAttrs) + + serialize(node.data.attrs) + + serialize(node.data.props, true) + ) } } -function serialize (attrs) { +function serialize (attrs, asProps) { let res = '' if (!attrs) { return res @@ -16,6 +20,9 @@ function serialize (attrs) { // leave it to the style module continue } + if (asProps) { + key = propsToAttrMap[key] || key.toLowerCase() + } if (attrs[key] != null) { if (isBooleanAttr(key)) { res += ` ${key}="${key}"` diff --git a/src/platforms/web/util/attrs.js b/src/platforms/web/util/attrs.js index ebadf0ad..06c2506e 100644 --- a/src/platforms/web/util/attrs.js +++ b/src/platforms/web/util/attrs.js @@ -14,5 +14,12 @@ export const isBooleanAttr = makeMap( 'truespeed,typemustmatch,visible' ) +export const propsToAttrMap = { + acceptCharset: 'accept-charset', + className: 'class', + htmlFor: 'for', + httpEquiv: 'http-equiv', +} + export const xlinkNS = 'http://www.w3.org/1999/xlink' export const isXlink = name => name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' diff --git a/src/server/create-streaming-renderer.js b/src/server/create-streaming-renderer.js index 367356e5..f49325a8 100644 --- a/src/server/create-streaming-renderer.js +++ b/src/server/create-streaming-renderer.js @@ -2,25 +2,30 @@ import RenderStream from './render-stream' import { renderStartingTag } from './render-starting-tag' export function createStreamingRenderer (modules, directives, isUnaryTag) { - function renderComponent (component, write, next) { + function renderComponent (component, write, next, isRoot) { component.$mount() - renderNode(component._vnode, write, next) + renderNode(component._vnode, write, next, isRoot) } - function renderNode (node, write, next) { + function renderNode (node, write, next, isRoot) { if (node.componentOptions) { node.data.hook.init(node) - renderComponent(node.child, write, next) + renderComponent(node.child, write, next, isRoot) } else { if (node.tag) { - renderElement(node, write, next) + renderElement(node, write, next, isRoot) } else { write(node.text, next) } } } - function renderElement (el, write, next) { + function renderElement (el, write, next, isRoot) { + if (isRoot) { + if (!el.data) el.data = {} + if (!el.data.attrs) el.data.attrs = {} + el.data.attrs['server-rendered'] = true + } const startTag = renderStartingTag(el, modules, directives) const endTag = `` if (isUnaryTag(el.tag)) { @@ -50,7 +55,7 @@ export function createStreamingRenderer (modules, directives, isUnaryTag) { return function renderToStream (component) { return new RenderStream((write, done) => { - renderComponent(component, write, done) + renderComponent(component, write, done, true) }) } } diff --git a/src/server/create-sync-renderer.js b/src/server/create-sync-renderer.js index dc70067a..10ec77c4 100644 --- a/src/server/create-sync-renderer.js +++ b/src/server/create-sync-renderer.js @@ -1,33 +1,44 @@ import { renderStartingTag } from './render-starting-tag' export function createSyncRenderer (modules, directives, isUnaryTag) { - function renderComponent (component) { + function renderComponent (component, isRoot) { component.$mount() - return renderNode(component._vnode) + return renderNode(component._vnode, isRoot) } - function renderNode (node) { + function renderNode (node, isRoot) { if (node.componentOptions) { node.data.hook.init(node) - return renderComponent(node.child) + return renderComponent(node.child, isRoot) } else { return node.tag - ? renderElement(node) + ? renderElement(node, isRoot) : node.text } } - function renderElement (el) { + function renderElement (el, isRoot) { + if (isRoot) { + if (!el.data) el.data = {} + if (!el.data.attrs) el.data.attrs = {} + el.data.attrs['server-rendered'] = true + } const startTag = renderStartingTag(el, modules, directives) + const endTag = `` if (isUnaryTag(el.tag)) { return startTag + } else if (!el.children || !el.children.length) { + return startTag + endTag } else { - const children = el.children - ? el.children.map(renderNode).join('') - : '' - return startTag + children + `` + let children = '' + for (let i = 0; i < el.children.length; i++) { + children += renderNode(el.children[i]) + } + return startTag + children + endTag } } - return renderComponent + return function renderToString (component) { + return renderComponent(component, true) + } } diff --git a/test/ssr/ssr.stream.spec.js b/test/ssr/ssr.stream.spec.js index 1eceb638..27abb302 100644 --- a/test/ssr/ssr.stream.spec.js +++ b/test/ssr/ssr.stream.spec.js @@ -10,6 +10,7 @@ describe('SSR: renderToStream', () => {

yoyo

{{ test }} + `, @@ -31,7 +32,15 @@ describe('SSR: renderToStream', () => { res += chunk }) stream.on('end', () => { - expect(res).toContain('

yoyo

hi
hahahaha
') + expect(res).toContain( + '
' + + '

yoyo

' + + '
' + + 'hi' + + '' + + '
hahahaha
' + + '
' + ) done() }) }) diff --git a/test/ssr/ssr.sync.spec.js b/test/ssr/ssr.sync.spec.js index 56370fe6..248b8e19 100644 --- a/test/ssr/ssr.sync.spec.js +++ b/test/ssr/ssr.sync.spec.js @@ -6,13 +6,13 @@ describe('SSR: renderToString', () => { it('static attributes', () => { expect(renderVmWithOptions({ template: '
' - })).toContain('
') + })).toContain('
') }) it('unary tags', () => { expect(renderVmWithOptions({ template: '' - })).toContain('') + })).toContain('') }) it('dynamic attributes', () => { @@ -22,13 +22,13 @@ describe('SSR: renderToString', () => { foo: 'hi', baz: 123 } - })).toContain('
') + })).toContain('
') }) it('static class', () => { expect(renderVmWithOptions({ template: '
' - })).toContain('
') + })).toContain('
') }) it('dynamic class', () => { @@ -39,7 +39,7 @@ describe('SSR: renderToString', () => { hasQux: true, hasQuux: false } - })).toContain('
') + })).toContain('
') }) it('dynamic style', () => { @@ -49,7 +49,7 @@ describe('SSR: renderToString', () => { fontSize: 14, color: 'red' } - })).toContain('
') + })).toContain('
') }) it('text interpolation', () => { @@ -59,7 +59,7 @@ describe('SSR: renderToString', () => { foo: 'server', bar: 'rendering' } - })).toContain('
server side rendering
') + })).toContain('
server side rendering
') }) it('child component (hoc)', () => { @@ -84,7 +84,7 @@ describe('SSR: renderToString', () => { } } } - })).toContain('
hello bar
') + })).toContain('
hello bar
') }) it('everything together', () => { @@ -94,6 +94,7 @@ describe('SSR: renderToString', () => {

yoyo

{{ test }} + `, @@ -108,7 +109,15 @@ describe('SSR: renderToString', () => { } } } - })).toContain('

yoyo

hi
hahahaha
') + })).toContain( + '
' + + '

yoyo

' + + '
' + + 'hi' + + '' + + '
hahahaha
' + + '
' + ) }) })