From 2efc0446b3ac1f18fff8b59215e7b60d51c41965 Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 23 Apr 2017 22:54:22 +0800 Subject: [PATCH] inline css links in renderStyles() --- src/server/template-renderer/index.js | 22 +++++++++++++------ .../template-renderer/template-stream.js | 7 +++--- src/server/util.js | 5 +++++ src/server/webpack-plugin/client.js | 10 ++++----- src/server/webpack-plugin/util.js | 2 +- test/ssr/compile-with-webpack.js | 2 +- test/ssr/fixtures/async-foo.js | 1 + test/ssr/fixtures/test.css | 0 test/ssr/ssr-template.spec.js | 12 ++++++---- 9 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 src/server/util.js create mode 100644 test/ssr/fixtures/test.css diff --git a/src/server/template-renderer/index.js b/src/server/template-renderer/index.js index cc2b249a1e6..9d035851541 100644 --- a/src/server/template-renderer/index.js +++ b/src/server/template-renderer/index.js @@ -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 => ``).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 diff --git a/src/server/template-renderer/template-stream.js b/src/server/template-renderer/template-stream.js index 8e2ba1e725d..ed4db78f65a 100644 --- a/src/server/template-renderer/template-stream.js +++ b/src/server/template-renderer/template-stream.js @@ -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) } } diff --git a/src/server/util.js b/src/server/util.js new file mode 100644 index 00000000000..21e3c0d7296 --- /dev/null +++ b/src/server/util.js @@ -0,0 +1,5 @@ +/* @flow */ + +export const isJS = (file: string): boolean => /\.js($|\?)/.test(file) + +export const isCSS = (file: string): boolean => /\.css($|\?)/.test(file) diff --git a/src/server/webpack-plugin/client.js b/src/server/webpack-plugin/client.js index dbce4f855dd..02d1fb6bf21 100644 --- a/src/server/webpack-plugin/client.js +++ b/src/server/webpack-plugin/client.js @@ -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 */ } } diff --git a/src/server/webpack-plugin/util.js b/src/server/webpack-plugin/util.js index 8efb3bc0446..d22e4b2c497 100644 --- a/src/server/webpack-plugin/util.js +++ b/src/server/webpack-plugin/util.js @@ -21,4 +21,4 @@ export const validate = compiler => { } } -export const isJS = file => /\.js($|\?)/.test(file) +export { isJS, isCSS } from '../util' diff --git a/test/ssr/compile-with-webpack.js b/test/ssr/compile-with-webpack.js index 42639f4fcff..0e8ecc4f78e 100644 --- a/test/ssr/compile-with-webpack.js +++ b/test/ssr/compile-with-webpack.js @@ -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]' diff --git a/test/ssr/fixtures/async-foo.js b/test/ssr/fixtures/async-foo.js index 1d22b23d39a..929d73f43c5 100644 --- a/test/ssr/fixtures/async-foo.js +++ b/test/ssr/fixtures/async-foo.js @@ -1,4 +1,5 @@ // import image and font +import './test.css' import font from './test.woff2' import image from './test.png' diff --git a/test/ssr/fixtures/test.css b/test/ssr/fixtures/test.css new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/ssr/ssr-template.spec.js b/test/ssr/ssr-template.spec.js index 9786fbf4b43..0b47d0d11d7 100644 --- a/test/ssr/ssr-template.spec.js +++ b/test/ssr/ssr-template.spec.js @@ -225,11 +225,14 @@ describe('SSR: template option', () => { `` + `` + `` + + `` + // images and fonts are only preloaded when explicitly asked for (preloadOtherAssets ? `` : ``) + (preloadOtherAssets ? `` : ``) + // unused chunks should have prefetch `` + + // css assets should be loaded + `` + `` + `
async test.woff2 test.png
` + // 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: `` + - `{{{ renderResourceHints() }}}` + + `{{{ renderResourceHints() }}}{{{ renderStyles() }}}` + `{{{ renderScripts() }}}` + ``, inject: false @@ -303,7 +306,8 @@ describe('SSR: template option', () => { const customOutput = `${ - context.renderResourceHints() + context.renderResourceHints() + + context.renderStyles() }${ res + context.renderScripts()