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) {
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}"`

7
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'

19
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 = `</${el.tag}>`
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)
})
}
}

33
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 = `</${el.tag}>`
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 + `</${el.tag}>`
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)
}
}

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

@ -10,6 +10,7 @@ describe('SSR: renderToStream', () => {
<p class="hi">yoyo</p>
<div id="ho" :class="{ red: isRed }"></div>
<span>{{ test }}</span>
<input :value="test">
<test></test>
</div>
`,
@ -31,7 +32,15 @@ describe('SSR: renderToStream', () => {
res += chunk
})
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()
})
})

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

@ -6,13 +6,13 @@ describe('SSR: renderToString', () => {
it('static attributes', () => {
expect(renderVmWithOptions({
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', () => {
expect(renderVmWithOptions({
template: '<input value="123">'
})).toContain('<input value="123">')
})).toContain('<input value="123" server-rendered="true">')
})
it('dynamic attributes', () => {
@ -22,13 +22,13 @@ describe('SSR: renderToString', () => {
foo: 'hi',
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', () => {
expect(renderVmWithOptions({
template: '<div class="foo bar"></div>'
})).toContain('<div class="foo bar"></div>')
})).toContain('<div server-rendered="true" class="foo bar"></div>')
})
it('dynamic class', () => {
@ -39,7 +39,7 @@ describe('SSR: renderToString', () => {
hasQux: true,
hasQuux: false
}
})).toContain('<div class="foo bar baz qux"></div>')
})).toContain('<div server-rendered="true" class="foo bar baz qux"></div>')
})
it('dynamic style', () => {
@ -49,7 +49,7 @@ describe('SSR: renderToString', () => {
fontSize: 14,
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', () => {
@ -59,7 +59,7 @@ describe('SSR: renderToString', () => {
foo: 'server',
bar: 'rendering'
}
})).toContain('<div>server side rendering</div>')
})).toContain('<div server-rendered="true">server side rendering</div>')
})
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', () => {
@ -94,6 +94,7 @@ describe('SSR: renderToString', () => {
<p class="hi">yoyo</p>
<div id="ho" :class="{ red: isRed }"></div>
<span>{{ test }}</span>
<input :value="test">
<test></test>
</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