inline css links in renderStyles()

This commit is contained in:
Evan You 2017-04-23 22:54:22 +08:00
parent 3a621d1860
commit 2efc0446b3
9 changed files with 40 additions and 21 deletions

View File

@ -3,15 +3,13 @@
const path = require('path')
const serialize = require('serialize-javascript')
import { isJS, isCSS } from '../util'
import TemplateStream from './template-stream'
import { parseTemplate } from './parse-template'
import { createMapper } from './create-async-file-mapper'
import type { ParsedTemplate } from './parse-template'
import type { AsyncFileMapper } from './create-async-file-mapper'
const JS_RE = /\.js($|\?)/
export const isJS = (file: string): boolean => JS_RE.test(file)
type TemplateRendererOptions = {
template: ?string;
inject?: boolean;
@ -100,8 +98,18 @@ export default class TemplateRenderer {
}
renderStyles (context: Object): string {
// context.styles is a getter exposed by vue-style-loader
return context.styles || ''
const cssFiles = this.clientManifest
? this.clientManifest.all.filter(isCSS)
: []
return (
// render links for css files
(cssFiles.length
? cssFiles.map(file => `<link rel="stylesheet" href="${this.publicPath}/${file}">`).join('')
: '') +
// context.styles is a getter exposed by vue-style-loader which contains
// the inline component styles collected during SSR
(context.styles || '')
)
}
renderResourceHints (context: Object): string {
@ -117,8 +125,8 @@ export default class TemplateRenderer {
const ext = path.extname(withoutQuery).slice(1)
const type = getPreloadType(ext)
const shouldPreload = this.options.shouldPreload
// by default, we only preload scripts
if (!shouldPreload && type !== 'script') {
// by default, we only preload scripts or css
if (!shouldPreload && type !== 'script' && type !== 'style') {
return ''
}
// user wants to explicitly control what to preload

View File

@ -49,9 +49,10 @@ export default class TemplateStream extends Transform {
this.push(links)
}
// inline server-rendered CSS collected by vue-style-loader
if (this.context.styles) {
this.push(this.context.styles)
// CSS files and inline server-rendered CSS collected by vue-style-loader
const styles = this.renderer.renderStyles(this.context)
if (styles) {
this.push(styles)
}
}

5
src/server/util.js Normal file
View File

@ -0,0 +1,5 @@
/* @flow */
export const isJS = (file: string): boolean => /\.js($|\?)/.test(file)
export const isCSS = (file: string): boolean => /\.css($|\?)/.test(file)

View File

@ -15,20 +15,20 @@ export default class VueSSRClientPlugin {
const allFiles = stats.assets
.map(a => a.name)
const initialScripts = Object.keys(stats.entrypoints)
const initialFiles = Object.keys(stats.entrypoints)
.map(name => stats.entrypoints[name].assets)
.reduce((assets, all) => all.concat(assets), [])
.filter(isJS)
const asyncScripts = allFiles
const asyncFiles = allFiles
.filter(isJS)
.filter(file => initialScripts.indexOf(file) < 0)
.filter(file => initialFiles.indexOf(file) < 0)
const manifest = {
publicPath: stats.publicPath,
all: allFiles,
initial: initialScripts,
async: asyncScripts,
initial: initialFiles,
async: asyncFiles,
modules: { /* [identifier: string]: Array<index: number> */ }
}

View File

@ -21,4 +21,4 @@ export const validate = compiler => {
}
}
export const isJS = file => /\.js($|\?)/.test(file)
export { isJS, isCSS } from '../util'

View File

@ -16,7 +16,7 @@ export function compileWithWebpack (file, extraConfig, cb) {
loader: require.resolve('./async-loader')
},
{
test: /\.(png|woff2)$/,
test: /\.(png|woff2|css)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]'

View File

@ -1,4 +1,5 @@
// import image and font
import './test.css'
import font from './test.woff2'
import image from './test.png'

View File

View File

@ -225,11 +225,14 @@ describe('SSR: template option', () => {
`<link rel="preload" href="/manifest.js" as="script">` +
`<link rel="preload" href="/main.js" as="script">` +
`<link rel="preload" href="/0.js" as="script">` +
`<link rel="preload" href="/test.css" as="style">` +
// images and fonts are only preloaded when explicitly asked for
(preloadOtherAssets ? `<link rel="preload" href="/test.png" as="image">` : ``) +
(preloadOtherAssets ? `<link rel="preload" href="/test.woff2" as="font" type="font/woff2" crossorigin>` : ``) +
// unused chunks should have prefetch
`<link rel="prefetch" href="/1.js" as="script">` +
// css assets should be loaded
`<link rel="stylesheet" href="/test.css">` +
`</head><body>` +
`<div data-server-rendered="true"><div>async test.woff2 test.png</div></div>` +
// manifest chunk should be first
@ -243,7 +246,7 @@ describe('SSR: template option', () => {
createClientManifestAssertions(false)
function createClientManifestAssertions (runInNewContext) {
it('bundleRenderer + renderToString + clientManifest', done => {
it('bundleRenderer + renderToString + clientManifest ()', done => {
createRendererWithManifest('split.js', { runInNewContext }, renderer => {
renderer.renderToString({}, (err, res) => {
expect(err).toBeNull()
@ -257,7 +260,7 @@ describe('SSR: template option', () => {
createRendererWithManifest('split.js', {
runInNewContext,
shouldPreload: (file, type) => {
if (type === 'image' || type === 'script' || type === 'font') {
if (type === 'image' || type === 'script' || type === 'font' || type === 'style') {
return true
}
}
@ -278,7 +281,7 @@ describe('SSR: template option', () => {
createRendererWithManifest('split.js', {
runInNewContext,
template: `<html>` +
`<head>{{{ renderResourceHints() }}}</head>` +
`<head>{{{ renderResourceHints() }}}{{{ renderStyles() }}}</head>` +
`<body><!--vue-ssr-outlet-->{{{ renderScripts() }}}</body>` +
`</html>`,
inject: false
@ -303,7 +306,8 @@ describe('SSR: template option', () => {
const customOutput =
`<html><head>${
context.renderResourceHints()
context.renderResourceHints() +
context.renderStyles()
}</head><body>${
res +
context.renderScripts()