Browse Source

add server-rendered mark for ssr output

dev
Evan You 9 years ago
parent
commit
38540b07e4
  1. 15
      src/platforms/web/server/modules/attrs.js
  2. 7
      src/platforms/web/util/attrs.js
  3. 19
      src/server/create-streaming-renderer.js
  4. 33
      src/server/create-sync-renderer.js
  5. 11
      test/ssr/ssr.stream.spec.js
  6. 27
      test/ssr/ssr.sync.spec.js

15
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) { export default function renderAttrs (node) {
if (node.data.attrs || node.data.staticAttrs) { if (node.data.attrs || node.data.props || node.data.staticAttrs) {
return serialize(node.data.staticAttrs) + serialize(node.data.attrs) return (
serialize(node.data.staticAttrs) +
serialize(node.data.attrs) +
serialize(node.data.props, true)
)
} }
} }
function serialize (attrs) { function serialize (attrs, asProps) {
let res = '' let res = ''
if (!attrs) { if (!attrs) {
return res return res
@ -16,6 +20,9 @@ function serialize (attrs) {
// leave it to the style module // leave it to the style module
continue continue
} }
if (asProps) {
key = propsToAttrMap[key] || key.toLowerCase()
}
if (attrs[key] != null) { if (attrs[key] != null) {
if (isBooleanAttr(key)) { if (isBooleanAttr(key)) {
res += ` ${key}="${key}"` res += ` ${key}="${key}"`

7
src/platforms/web/util/attrs.js

@ -14,5 +14,12 @@ export const isBooleanAttr = makeMap(
'truespeed,typemustmatch,visible' '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 xlinkNS = 'http://www.w3.org/1999/xlink'
export const isXlink = name => name.charAt(5) === ':' && name.slice(0, 5) === 'xlink' export const isXlink = name => name.charAt(5) === ':' && name.slice(0, 5) === 'xlink'

19
src/server/create-streaming-renderer.js

@ -2,25 +2,30 @@ import RenderStream from './render-stream'
import { renderStartingTag } from './render-starting-tag' import { renderStartingTag } from './render-starting-tag'
export function createStreamingRenderer (modules, directives, isUnaryTag) { export function createStreamingRenderer (modules, directives, isUnaryTag) {
function renderComponent (component, write, next) { function renderComponent (component, write, next, isRoot) {
component.$mount() 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) { if (node.componentOptions) {
node.data.hook.init(node) node.data.hook.init(node)
renderComponent(node.child, write, next) renderComponent(node.child, write, next, isRoot)
} else { } else {
if (node.tag) { if (node.tag) {
renderElement(node, write, next) renderElement(node, write, next, isRoot)
} else { } else {
write(node.text, next) 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 startTag = renderStartingTag(el, modules, directives)
const endTag = `</${el.tag}>` const endTag = `</${el.tag}>`
if (isUnaryTag(el.tag)) { if (isUnaryTag(el.tag)) {
@ -50,7 +55,7 @@ export function createStreamingRenderer (modules, directives, isUnaryTag) {
return function renderToStream (component) { return function renderToStream (component) {
return new RenderStream((write, done) => { return new RenderStream((write, done) => {
renderComponent(component, write, done) renderComponent(component, write, done, true)
}) })
} }
} }

33
src/server/create-sync-renderer.js

@ -1,33 +1,44 @@
import { renderStartingTag } from './render-starting-tag' import { renderStartingTag } from './render-starting-tag'
export function createSyncRenderer (modules, directives, isUnaryTag) { export function createSyncRenderer (modules, directives, isUnaryTag) {
function renderComponent (component) { function renderComponent (component, isRoot) {
component.$mount() component.$mount()
return renderNode(component._vnode) return renderNode(component._vnode, isRoot)
} }
function renderNode (node) { function renderNode (node, isRoot) {
if (node.componentOptions) { if (node.componentOptions) {
node.data.hook.init(node) node.data.hook.init(node)
return renderComponent(node.child) return renderComponent(node.child, isRoot)
} else { } else {
return node.tag return node.tag
? renderElement(node) ? renderElement(node, isRoot)
: node.text : 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 startTag = renderStartingTag(el, modules, directives)
const endTag = `</${el.tag}>`
if (isUnaryTag(el.tag)) { if (isUnaryTag(el.tag)) {
return startTag return startTag
} else if (!el.children || !el.children.length) {
return startTag + endTag
} else { } else {
const children = el.children let children = ''
? el.children.map(renderNode).join('') for (let i = 0; i < el.children.length; i++) {
: '' children += renderNode(el.children[i])
return startTag + children + `</${el.tag}>` }
return startTag + children + endTag
} }
} }
return renderComponent return function renderToString (component) {
return renderComponent(component, true)
}
} }

11
test/ssr/ssr.stream.spec.js

@ -10,6 +10,7 @@ describe('SSR: renderToStream', () => {
<p class="hi">yoyo</p> <p class="hi">yoyo</p>
<div id="ho" :class="{ red: isRed }"></div> <div id="ho" :class="{ red: isRed }"></div>
<span>{{ test }}</span> <span>{{ test }}</span>
<input :value="test">
<test></test> <test></test>
</div> </div>
`, `,
@ -31,7 +32,15 @@ describe('SSR: renderToStream', () => {
res += chunk res += chunk
}) })
stream.on('end', () => { stream.on('end', () => {
expect(res).toContain('<div><p class="hi">yoyo</p><div id="ho" class="red"></div><span>hi</span><div class="a">hahahaha</div></div>') expect(res).toContain(
'<div server-rendered="true">' +
'<p class="hi">yoyo</p>' +
'<div id="ho" class="red"></div>' +
'<span>hi</span>' +
'<input value="hi">' +
'<div class="a">hahahaha</div>' +
'</div>'
)
done() done()
}) })
}) })

27
test/ssr/ssr.sync.spec.js

@ -6,13 +6,13 @@ describe('SSR: renderToString', () => {
it('static attributes', () => { it('static attributes', () => {
expect(renderVmWithOptions({ expect(renderVmWithOptions({
template: '<div id="foo" bar="123"></div>' template: '<div id="foo" bar="123"></div>'
})).toContain('<div id="foo" bar="123"></div>') })).toContain('<div id="foo" bar="123" server-rendered="true"></div>')
}) })
it('unary tags', () => { it('unary tags', () => {
expect(renderVmWithOptions({ expect(renderVmWithOptions({
template: '<input value="123">' template: '<input value="123">'
})).toContain('<input value="123">') })).toContain('<input value="123" server-rendered="true">')
}) })
it('dynamic attributes', () => { it('dynamic attributes', () => {
@ -22,13 +22,13 @@ describe('SSR: renderToString', () => {
foo: 'hi', foo: 'hi',
baz: 123 baz: 123
} }
})).toContain('<div qux="quux" id="hi" bar="123"></div>') })).toContain('<div qux="quux" id="hi" bar="123" server-rendered="true"></div>')
}) })
it('static class', () => { it('static class', () => {
expect(renderVmWithOptions({ expect(renderVmWithOptions({
template: '<div class="foo bar"></div>' template: '<div class="foo bar"></div>'
})).toContain('<div class="foo bar"></div>') })).toContain('<div server-rendered="true" class="foo bar"></div>')
}) })
it('dynamic class', () => { it('dynamic class', () => {
@ -39,7 +39,7 @@ describe('SSR: renderToString', () => {
hasQux: true, hasQux: true,
hasQuux: false hasQuux: false
} }
})).toContain('<div class="foo bar baz qux"></div>') })).toContain('<div server-rendered="true" class="foo bar baz qux"></div>')
}) })
it('dynamic style', () => { it('dynamic style', () => {
@ -49,7 +49,7 @@ describe('SSR: renderToString', () => {
fontSize: 14, fontSize: 14,
color: 'red' color: 'red'
} }
})).toContain('<div style="font-size:14px;color:red;background-color:black"></div>') })).toContain('<div server-rendered="true" style="font-size:14px;color:red;background-color:black"></div>')
}) })
it('text interpolation', () => { it('text interpolation', () => {
@ -59,7 +59,7 @@ describe('SSR: renderToString', () => {
foo: 'server', foo: 'server',
bar: 'rendering' bar: 'rendering'
} }
})).toContain('<div>server side rendering</div>') })).toContain('<div server-rendered="true">server side rendering</div>')
}) })
it('child component (hoc)', () => { it('child component (hoc)', () => {
@ -84,7 +84,7 @@ describe('SSR: renderToString', () => {
} }
} }
} }
})).toContain('<div class="foo bar">hello bar</div>') })).toContain('<div server-rendered="true" class="foo bar">hello bar</div>')
}) })
it('everything together', () => { it('everything together', () => {
@ -94,6 +94,7 @@ describe('SSR: renderToString', () => {
<p class="hi">yoyo</p> <p class="hi">yoyo</p>
<div id="ho" :class="{ red: isRed }"></div> <div id="ho" :class="{ red: isRed }"></div>
<span>{{ test }}</span> <span>{{ test }}</span>
<input :value="test">
<test></test> <test></test>
</div> </div>
`, `,
@ -108,7 +109,15 @@ describe('SSR: renderToString', () => {
} }
} }
} }
})).toContain('<div><p class="hi">yoyo</p><div id="ho" class="red"></div><span>hi</span><div class="a">hahahaha</div></div>') })).toContain(
'<div server-rendered="true">' +
'<p class="hi">yoyo</p>' +
'<div id="ho" class="red"></div>' +
'<span>hi</span>' +
'<input value="hi">' +
'<div class="a">hahahaha</div>' +
'</div>'
)
}) })
}) })

Loading…
Cancel
Save